oodocs 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
oodocs/__init__.py ADDED
@@ -0,0 +1,294 @@
1
+ """Top-level package for oodocs."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version as package_version
4
+
5
+ from oodocs.core import OODocsError
6
+ from oodocs.compatibility import OUTPUT_FORMATS, OutputFormat
7
+ from oodocs.components.blocks import (
8
+ Assumption,
9
+ Axiom,
10
+ Box,
11
+ BulletList,
12
+ Chapter,
13
+ Claim,
14
+ CodeBlock,
15
+ ColumnSpan,
16
+ Conjecture,
17
+ CountableBlock,
18
+ Corollary,
19
+ Definition,
20
+ Divider,
21
+ Equation,
22
+ Example,
23
+ MAX_SECTION_LEVEL,
24
+ MIN_SECTION_LEVEL,
25
+ Lemma,
26
+ MultiColumn,
27
+ NumberedList,
28
+ PageBreak,
29
+ Paragraph,
30
+ Part,
31
+ Proof,
32
+ Proposition,
33
+ Remark,
34
+ Section,
35
+ Subsection,
36
+ Subsubsection,
37
+ Theorem,
38
+ VerticalSpace,
39
+ countable_kind,
40
+ section_for_level,
41
+ shift_heading_level,
42
+ shift_heading_levels,
43
+ )
44
+ from oodocs.components.generated import (
45
+ CommentsPage,
46
+ FigureList,
47
+ ReferencesPage,
48
+ TableList,
49
+ TableOfContents,
50
+ TocLevelStyle,
51
+ )
52
+ from oodocs.components.media import (
53
+ Figure,
54
+ ImageData,
55
+ SubFigure,
56
+ SubFigureGroup,
57
+ Table,
58
+ TableCell,
59
+ TableCellStyle,
60
+ )
61
+ from oodocs.components.markup import markup
62
+ from oodocs.components.people import Affiliation, Author, AuthorLayout
63
+ from oodocs.components.references import CitationLibrary, CitationSource
64
+ from oodocs.components.positioning import ImageBox, Shape, TextBox
65
+ from oodocs.document import Document
66
+ from oodocs.importers.markdown import (
67
+ from_markdown,
68
+ from_markdown_file,
69
+ parse_markdown,
70
+ parse_markdown_file,
71
+ )
72
+ from oodocs.importers.notebook import (
73
+ from_ipynb,
74
+ from_notebook,
75
+ parse_ipynb,
76
+ parse_notebook,
77
+ )
78
+ from oodocs.components.inline import (
79
+ Comment,
80
+ Footnote,
81
+ InlineChip,
82
+ InlineChipStyle,
83
+ LineBreak,
84
+ Math,
85
+ Text,
86
+ badge,
87
+ bold,
88
+ code,
89
+ color,
90
+ cite,
91
+ comment,
92
+ footnote,
93
+ highlight,
94
+ italic,
95
+ keyboard,
96
+ link,
97
+ line_break,
98
+ math,
99
+ prescript,
100
+ reference,
101
+ status,
102
+ strike,
103
+ strikethrough,
104
+ styled,
105
+ subscript,
106
+ superscript,
107
+ tag,
108
+ )
109
+ from oodocs.settings import (
110
+ BlockOptions,
111
+ BoxStyle,
112
+ CaptionOptions,
113
+ CitationOptions,
114
+ DocumentSettings,
115
+ GeneratedPageOptions,
116
+ HeadingNumbering,
117
+ ListStyle,
118
+ PageNumberOptions,
119
+ PageMargins,
120
+ PageSize,
121
+ ParagraphStyle,
122
+ TableStyle,
123
+ TextStyle,
124
+ TitleMatterOptions,
125
+ TypographyOptions,
126
+ Theme,
127
+ )
128
+ from oodocs.validation import DocumentValidationError, ValidationIssue, ValidationResult
129
+ from oodocs.workflows import (
130
+ RenderedOutputs,
131
+ build_python_document,
132
+ convert_source,
133
+ load_document,
134
+ load_python_document,
135
+ render_document,
136
+ validate_source,
137
+ )
138
+
139
+
140
+ _FALLBACK_VERSION = "1.0.1"
141
+
142
+
143
+ def _resolve_version() -> str:
144
+ try:
145
+ return package_version("oodocs")
146
+ except PackageNotFoundError:
147
+ try:
148
+ from setuptools_scm import get_version
149
+ except ImportError:
150
+ return _FALLBACK_VERSION
151
+ return get_version(
152
+ root="../..",
153
+ relative_to=__file__,
154
+ fallback_version=_FALLBACK_VERSION,
155
+ tag_regex=r"^v(?P<version>\d+\.\d+\.\d+)$",
156
+ )
157
+
158
+
159
+ __version__ = _resolve_version()
160
+
161
+ __all__ = [
162
+ "Affiliation",
163
+ "Assumption",
164
+ "Author",
165
+ "AuthorLayout",
166
+ "Axiom",
167
+ "BlockOptions",
168
+ "Box",
169
+ "BoxStyle",
170
+ "BulletList",
171
+ "CaptionOptions",
172
+ "CitationOptions",
173
+ "CitationLibrary",
174
+ "CitationSource",
175
+ "Chapter",
176
+ "Claim",
177
+ "Comment",
178
+ "CommentsPage",
179
+ "CodeBlock",
180
+ "ColumnSpan",
181
+ "Conjecture",
182
+ "CountableBlock",
183
+ "Corollary",
184
+ "Definition",
185
+ "Document",
186
+ "DocumentSettings",
187
+ "OODocsError",
188
+ "Divider",
189
+ "Equation",
190
+ "Example",
191
+ "Figure",
192
+ "FigureList",
193
+ "Footnote",
194
+ "GeneratedPageOptions",
195
+ "HeadingNumbering",
196
+ "ImageBox",
197
+ "ImageData",
198
+ "InlineChip",
199
+ "InlineChipStyle",
200
+ "LineBreak",
201
+ "ListStyle",
202
+ "Lemma",
203
+ "Math",
204
+ "MAX_SECTION_LEVEL",
205
+ "MIN_SECTION_LEVEL",
206
+ "MultiColumn",
207
+ "NumberedList",
208
+ "OUTPUT_FORMATS",
209
+ "OutputFormat",
210
+ "PageNumberOptions",
211
+ "PageMargins",
212
+ "PageSize",
213
+ "PageBreak",
214
+ "Paragraph",
215
+ "Part",
216
+ "Proof",
217
+ "Proposition",
218
+ "ParagraphStyle",
219
+ "ReferencesPage",
220
+ "RenderedOutputs",
221
+ "Remark",
222
+ "Section",
223
+ "Shape",
224
+ "SubFigure",
225
+ "SubFigureGroup",
226
+ "Subsection",
227
+ "Subsubsection",
228
+ "Table",
229
+ "TableCell",
230
+ "TableCellStyle",
231
+ "TableStyle",
232
+ "TableOfContents",
233
+ "TableList",
234
+ "Text",
235
+ "TextBox",
236
+ "TextStyle",
237
+ "Theorem",
238
+ "TitleMatterOptions",
239
+ "Theme",
240
+ "TocLevelStyle",
241
+ "TypographyOptions",
242
+ "DocumentValidationError",
243
+ "ValidationIssue",
244
+ "ValidationResult",
245
+ "VerticalSpace",
246
+ "__version__",
247
+ "badge",
248
+ "bold",
249
+ "build_python_document",
250
+ "code",
251
+ "color",
252
+ "cite",
253
+ "comment",
254
+ "countable_kind",
255
+ "convert_source",
256
+ "footnote",
257
+ "from_ipynb",
258
+ "from_markdown",
259
+ "from_markdown_file",
260
+ "from_notebook",
261
+ "highlight",
262
+ "italic",
263
+ "keyboard",
264
+ "link",
265
+ "line_break",
266
+ "load_document",
267
+ "load_python_document",
268
+ "math",
269
+ "prescript",
270
+ "reference",
271
+ "render_document",
272
+ "status",
273
+ "strike",
274
+ "strikethrough",
275
+ "markup",
276
+ "styled",
277
+ "parse_ipynb",
278
+ "parse_markdown",
279
+ "parse_markdown_file",
280
+ "parse_notebook",
281
+ "section_for_level",
282
+ "shift_heading_level",
283
+ "shift_heading_levels",
284
+ "subscript",
285
+ "superscript",
286
+ "tag",
287
+ "validate_source",
288
+ ]
289
+
290
+ for _module_name in ("components", "core", "document", "layout", "settings"):
291
+ globals().pop(_module_name, None)
292
+
293
+ del _resolve_version
294
+ del _module_name
oodocs/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ """Run the OODocs command line interface with ``python -m oodocs``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from oodocs.cli import main
6
+
7
+
8
+ if __name__ == "__main__":
9
+ raise SystemExit(main())
oodocs/cli.py ADDED
@@ -0,0 +1,191 @@
1
+ """Command line interface for OODocs workflows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+ import sys
8
+ from typing import Sequence
9
+
10
+ from oodocs.compatibility import normalize_output_formats
11
+ from oodocs.validation import DocumentValidationError
12
+ from oodocs.workflows import build_python_document, convert_source, validate_source
13
+
14
+
15
+ def main(argv: Sequence[str] | None = None) -> int:
16
+ """Run the OODocs CLI."""
17
+
18
+ parser = _build_parser()
19
+ args = parser.parse_args(argv)
20
+ try:
21
+ return args.func(args)
22
+ except DocumentValidationError as exc:
23
+ print(exc, file=sys.stderr)
24
+ return 1
25
+ except (AttributeError, FileNotFoundError, ImportError, TypeError, ValueError) as exc:
26
+ print(f"oodocs: {exc}", file=sys.stderr)
27
+ return 2
28
+
29
+
30
+ def _build_parser() -> argparse.ArgumentParser:
31
+ parser = argparse.ArgumentParser(
32
+ prog="oodocs",
33
+ description="Build, convert, and validate OODocs documents.",
34
+ )
35
+ subparsers = parser.add_subparsers(dest="command", required=True)
36
+
37
+ build = subparsers.add_parser(
38
+ "build",
39
+ help="Build a Python-authored OODocs document.",
40
+ )
41
+ build.add_argument("source", help="Python file exposing a Document or build function.")
42
+ _add_render_options(build, default_out=".")
43
+ build.add_argument(
44
+ "--factory",
45
+ help="Document variable or zero-argument function to use from the Python source.",
46
+ )
47
+ build.add_argument(
48
+ "--no-chdir",
49
+ action="store_true",
50
+ help="Do not run the Python source with its directory as the working directory.",
51
+ )
52
+ build.set_defaults(func=_run_build)
53
+
54
+ convert = subparsers.add_parser(
55
+ "convert",
56
+ help="Convert Markdown or notebook source to rendered outputs.",
57
+ )
58
+ convert.add_argument("source", help="Markdown (.md) or notebook (.ipynb) file.")
59
+ _add_render_options(convert, default_out=None)
60
+ convert.add_argument("--title", help="Override the imported document title.")
61
+ convert.set_defaults(func=_run_convert)
62
+
63
+ validate = subparsers.add_parser(
64
+ "validate",
65
+ help="Validate a Python, Markdown, or notebook source document.",
66
+ )
67
+ validate.add_argument("source", help="Source file to validate.")
68
+ validate.add_argument(
69
+ "--to",
70
+ default="docx,pdf,html",
71
+ help="Comma-separated output formats to validate for. Defaults to docx,pdf,html.",
72
+ )
73
+ validate.add_argument(
74
+ "--type",
75
+ choices=("python", "py", "markdown", "md", "notebook", "ipynb"),
76
+ help="Override source type inference.",
77
+ )
78
+ validate.add_argument("--title", help="Override imported Markdown/notebook title.")
79
+ validate.add_argument(
80
+ "--factory",
81
+ help="Document variable or zero-argument function for Python sources.",
82
+ )
83
+ validate.add_argument(
84
+ "--strict",
85
+ action="store_true",
86
+ help="Return a non-zero exit code when warnings are present.",
87
+ )
88
+ validate.add_argument(
89
+ "--no-chdir",
90
+ action="store_true",
91
+ help="Do not run Python sources with their directory as the working directory.",
92
+ )
93
+ validate.set_defaults(func=_run_validate)
94
+
95
+ return parser
96
+
97
+
98
+ def _add_render_options(
99
+ parser: argparse.ArgumentParser,
100
+ *,
101
+ default_out: str | None,
102
+ ) -> None:
103
+ parser.add_argument(
104
+ "--out",
105
+ default=default_out,
106
+ help=(
107
+ "Output directory. Defaults to the current directory for build and "
108
+ "the source directory for convert."
109
+ ),
110
+ )
111
+ parser.add_argument(
112
+ "--to",
113
+ default="docx,pdf,html",
114
+ help="Comma-separated output formats. Defaults to docx,pdf,html.",
115
+ )
116
+ parser.add_argument("--stem", help="Output filename stem.")
117
+ parser.add_argument(
118
+ "--no-validate",
119
+ action="store_true",
120
+ help="Skip validation before rendering.",
121
+ )
122
+ parser.add_argument(
123
+ "--verbose",
124
+ action="store_true",
125
+ help="Print slow major build steps.",
126
+ )
127
+
128
+
129
+ def _run_build(args: argparse.Namespace) -> int:
130
+ formats = _parse_formats(args.to)
131
+ outputs = build_python_document(
132
+ args.source,
133
+ args.out,
134
+ formats=formats,
135
+ stem=args.stem,
136
+ factory=args.factory,
137
+ validate=not args.no_validate,
138
+ chdir=not args.no_chdir,
139
+ verbose=args.verbose,
140
+ )
141
+ _print_outputs(outputs.outputs)
142
+ return 0
143
+
144
+
145
+ def _run_convert(args: argparse.Namespace) -> int:
146
+ formats = _parse_formats(args.to)
147
+ outputs = convert_source(
148
+ args.source,
149
+ args.out,
150
+ formats=formats,
151
+ stem=args.stem,
152
+ title=args.title,
153
+ validate=not args.no_validate,
154
+ verbose=args.verbose,
155
+ )
156
+ _print_outputs(outputs.outputs)
157
+ return 0
158
+
159
+
160
+ def _run_validate(args: argparse.Namespace) -> int:
161
+ formats = _parse_formats(args.to)
162
+ result = validate_source(
163
+ args.source,
164
+ source_type=args.type,
165
+ title=args.title,
166
+ factory=args.factory,
167
+ formats=formats,
168
+ chdir=not args.no_chdir,
169
+ )
170
+ print(result.format_table(formats=formats))
171
+ if result.errors_for(formats):
172
+ return 1
173
+ if args.strict and result.warnings_for(formats):
174
+ return 1
175
+ return 0
176
+
177
+
178
+ def _parse_formats(value: str) -> tuple[str, ...]:
179
+ pieces = tuple(piece.strip() for piece in value.split(",") if piece.strip())
180
+ if not pieces:
181
+ raise ValueError("--to must include at least one output format")
182
+ return normalize_output_formats(pieces)
183
+
184
+
185
+ def _print_outputs(outputs: dict[object, Path]) -> None:
186
+ for output_format, path in outputs.items():
187
+ print(f"Wrote {output_format}: {path}")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ raise SystemExit(main())
@@ -0,0 +1,102 @@
1
+ """Shared output-format compatibility helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Iterable, Literal
7
+
8
+
9
+ OutputFormat = Literal["docx", "pdf", "html"]
10
+ OUTPUT_FORMATS: tuple[OutputFormat, ...] = ("docx", "pdf", "html")
11
+ OUTPUT_FORMAT_LABELS: dict[OutputFormat, str] = {
12
+ "docx": "Word",
13
+ "pdf": "PDF",
14
+ "html": "HTML",
15
+ }
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class CompatibilityNote:
20
+ """Renderer compatibility note surfaced by document validation."""
21
+
22
+ code: str
23
+ message: str
24
+ formats: tuple[OutputFormat, ...]
25
+
26
+
27
+ COMPATIBILITY_NOTES: dict[str, CompatibilityNote] = {
28
+ "html-toc-page-numbers": CompatibilityNote(
29
+ code="html-toc-page-numbers",
30
+ message=(
31
+ "HTML output does not have stable rendered page numbers, "
32
+ "so the TOC is link-only there."
33
+ ),
34
+ formats=("html",),
35
+ ),
36
+ }
37
+
38
+
39
+ def compatibility_note(code: str) -> CompatibilityNote:
40
+ """Return a named compatibility note."""
41
+
42
+ try:
43
+ return COMPATIBILITY_NOTES[code]
44
+ except KeyError as exc:
45
+ raise KeyError(f"Unknown compatibility note: {code}") from exc
46
+
47
+
48
+ def normalize_output_format(value: str) -> OutputFormat:
49
+ """Normalize a renderer/output format name."""
50
+
51
+ normalized = value.lower().strip().removeprefix(".")
52
+ if normalized == "htm":
53
+ normalized = "html"
54
+ if normalized not in OUTPUT_FORMATS:
55
+ raise ValueError(
56
+ "Unsupported document output format. Use one of: .docx, .pdf, .html "
57
+ "(or docx, pdf, html in save_all)."
58
+ )
59
+ return normalized # type: ignore[return-value]
60
+
61
+
62
+ def normalize_output_formats(
63
+ values: Iterable[str] | None = None,
64
+ ) -> tuple[OutputFormat, ...]:
65
+ """Normalize a sequence of output formats while preserving order."""
66
+
67
+ if values is None:
68
+ return OUTPUT_FORMATS
69
+
70
+ normalized: list[OutputFormat] = []
71
+ seen: set[OutputFormat] = set()
72
+ for value in values:
73
+ output_format = normalize_output_format(value)
74
+ if output_format in seen:
75
+ continue
76
+ normalized.append(output_format)
77
+ seen.add(output_format)
78
+ return tuple(normalized)
79
+
80
+
81
+ def format_output_formats(formats: Iterable[OutputFormat]) -> str:
82
+ """Return a compact display label for output formats."""
83
+
84
+ normalized = normalize_output_formats(formats)
85
+ if not normalized:
86
+ return "None"
87
+ if set(normalized) == set(OUTPUT_FORMATS):
88
+ return "All"
89
+ return "/".join(OUTPUT_FORMAT_LABELS[output_format] for output_format in normalized)
90
+
91
+
92
+ __all__ = [
93
+ "COMPATIBILITY_NOTES",
94
+ "OUTPUT_FORMATS",
95
+ "OUTPUT_FORMAT_LABELS",
96
+ "CompatibilityNote",
97
+ "OutputFormat",
98
+ "compatibility_note",
99
+ "format_output_formats",
100
+ "normalize_output_format",
101
+ "normalize_output_formats",
102
+ ]
@@ -0,0 +1,106 @@
1
+ """Grouped component namespaces for more granular imports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+
7
+
8
+ _EXPORTS = {
9
+ "Affiliation": "oodocs.components.people",
10
+ "Assumption": "oodocs.components.blocks",
11
+ "Author": "oodocs.components.people",
12
+ "AuthorLayout": "oodocs.components.people",
13
+ "Axiom": "oodocs.components.blocks",
14
+ "Bold": "oodocs.components.inline",
15
+ "Box": "oodocs.components.blocks",
16
+ "BulletList": "oodocs.components.blocks",
17
+ "Chapter": "oodocs.components.blocks",
18
+ "Citation": "oodocs.components.inline",
19
+ "CitationLibrary": "oodocs.components.references",
20
+ "CitationSource": "oodocs.components.references",
21
+ "Claim": "oodocs.components.blocks",
22
+ "CodeBlock": "oodocs.components.blocks",
23
+ "ColumnSpan": "oodocs.components.blocks",
24
+ "Comment": "oodocs.components.inline",
25
+ "Conjecture": "oodocs.components.blocks",
26
+ "CommentsPage": "oodocs.components.generated",
27
+ "Corollary": "oodocs.components.blocks",
28
+ "CountableBlock": "oodocs.components.blocks",
29
+ "Definition": "oodocs.components.blocks",
30
+ "Divider": "oodocs.components.blocks",
31
+ "Equation": "oodocs.components.blocks",
32
+ "Example": "oodocs.components.blocks",
33
+ "Figure": "oodocs.components.media",
34
+ "FigureList": "oodocs.components.generated",
35
+ "Footnote": "oodocs.components.inline",
36
+ "FootnotesPage": "oodocs.components.generated",
37
+ "Highlight": "oodocs.components.inline",
38
+ "Hyperlink": "oodocs.components.inline",
39
+ "ImageBox": "oodocs.components.positioning",
40
+ "ImageData": "oodocs.components.media",
41
+ "InlineChip": "oodocs.components.inline",
42
+ "InlineChipStyle": "oodocs.components.inline",
43
+ "Italic": "oodocs.components.inline",
44
+ "Lemma": "oodocs.components.blocks",
45
+ "LineBreak": "oodocs.components.inline",
46
+ "Math": "oodocs.components.inline",
47
+ "Monospace": "oodocs.components.inline",
48
+ "MultiColumn": "oodocs.components.blocks",
49
+ "NumberedList": "oodocs.components.blocks",
50
+ "Paragraph": "oodocs.components.blocks",
51
+ "Part": "oodocs.components.blocks",
52
+ "Proposition": "oodocs.components.blocks",
53
+ "Proof": "oodocs.components.blocks",
54
+ "ReferencesPage": "oodocs.components.generated",
55
+ "Remark": "oodocs.components.blocks",
56
+ "Section": "oodocs.components.blocks",
57
+ "Shape": "oodocs.components.positioning",
58
+ "SubFigure": "oodocs.components.media",
59
+ "SubFigureGroup": "oodocs.components.media",
60
+ "Subsection": "oodocs.components.blocks",
61
+ "Subsubsection": "oodocs.components.blocks",
62
+ "Table": "oodocs.components.media",
63
+ "TableCell": "oodocs.components.media",
64
+ "TableCellStyle": "oodocs.components.media",
65
+ "TableList": "oodocs.components.generated",
66
+ "TableOfContents": "oodocs.components.generated",
67
+ "Text": "oodocs.components.inline",
68
+ "TextBox": "oodocs.components.positioning",
69
+ "Theorem": "oodocs.components.blocks",
70
+ "VerticalSpace": "oodocs.components.blocks",
71
+ "badge": "oodocs.components.inline",
72
+ "bold": "oodocs.components.inline",
73
+ "code": "oodocs.components.inline",
74
+ "color": "oodocs.components.inline",
75
+ "cite": "oodocs.components.inline",
76
+ "comment": "oodocs.components.inline",
77
+ "countable_kind": "oodocs.components.blocks",
78
+ "footnote": "oodocs.components.inline",
79
+ "highlight": "oodocs.components.inline",
80
+ "italic": "oodocs.components.inline",
81
+ "keyboard": "oodocs.components.inline",
82
+ "link": "oodocs.components.inline",
83
+ "line_break": "oodocs.components.inline",
84
+ "math": "oodocs.components.inline",
85
+ "markup": "oodocs.components.markup",
86
+ "prescript": "oodocs.components.inline",
87
+ "reference": "oodocs.components.inline",
88
+ "status": "oodocs.components.inline",
89
+ "strike": "oodocs.components.inline",
90
+ "strikethrough": "oodocs.components.inline",
91
+ "styled": "oodocs.components.inline",
92
+ "subscript": "oodocs.components.inline",
93
+ "superscript": "oodocs.components.inline",
94
+ "tag": "oodocs.components.inline",
95
+ }
96
+
97
+ __all__ = list(_EXPORTS)
98
+
99
+
100
+ def __getattr__(name: str) -> object:
101
+ module_name = _EXPORTS.get(name)
102
+ if module_name is None:
103
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
104
+ value = getattr(import_module(module_name), name)
105
+ globals()[name] = value
106
+ return value