uEdition 0.8.0__py3-none-any.whl → 1.0.0__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.
Potentially problematic release.
This version of uEdition might be problematic. Click here for more details.
- uedition/CHANGELOG.md +75 -0
- uedition/__about__.py +1 -1
- uedition/__init__.py +3 -3
- uedition/__main__.py +1 -2
- uedition/cli/__init__.py +8 -12
- uedition/cli/build.py +72 -31
- uedition/cli/check.py +24 -44
- uedition/cli/language.py +4 -10
- uedition/cli/serve.py +7 -8
- uedition/cli/update.py +1 -1
- uedition/ext/__init__.py +1 -1
- uedition/ext/config.py +33 -52
- uedition/ext/language_switcher.js +3 -3
- uedition/ext/language_switcher.py +8 -9
- uedition/ext/tei/__init__.py +32 -0
- uedition/ext/tei/builder.py +297 -0
- uedition/ext/{tei.py → tei/parser.py} +17 -35
- uedition/ext/tei/tei_download.js +34 -0
- uedition/settings.py +28 -30
- {uedition-0.8.0.dist-info → uedition-1.0.0.dist-info}/METADATA +16 -6
- uedition-1.0.0.dist-info/RECORD +26 -0
- {uedition-0.8.0.dist-info → uedition-1.0.0.dist-info}/WHEEL +1 -1
- uedition-1.0.0.dist-info/entry_points.txt +5 -0
- uedition-0.8.0.dist-info/RECORD +0 -22
- uedition-0.8.0.dist-info/entry_points.txt +0 -2
- {uedition-0.8.0.dist-info → uedition-1.0.0.dist-info}/licenses/LICENSE.txt +0 -0
uedition/CHANGELOG.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Dev
|
|
4
|
+
|
|
5
|
+
* **New**: Add a switch to turn off the TEI generation
|
|
6
|
+
* **Update**: Don't enable the language switcher when only one language is configured
|
|
7
|
+
* **Bugfix**: Fixed a bug in the TEI generation
|
|
8
|
+
* **Bugfix**: Fixed the builder output paths
|
|
9
|
+
|
|
10
|
+
## 0.9.0
|
|
11
|
+
|
|
12
|
+
* **New**: Added TEI output
|
|
13
|
+
* **Update**: Don't re-ask answered questions on updates
|
|
14
|
+
|
|
15
|
+
# 0.8.0
|
|
16
|
+
|
|
17
|
+
* **New**: Add support for publishing via Read the Docs
|
|
18
|
+
* **Documentation**: Added basic documentation
|
|
19
|
+
* **Update**: Updated dependencies
|
|
20
|
+
|
|
21
|
+
# 0.7.0
|
|
22
|
+
|
|
23
|
+
* **New**: Add support for static files
|
|
24
|
+
* **Bugfix**: Fix a missing progress update
|
|
25
|
+
|
|
26
|
+
# 0.6.1
|
|
27
|
+
|
|
28
|
+
* **Update**: Fixes for changes in the JupyterBook
|
|
29
|
+
|
|
30
|
+
# 0.6.0
|
|
31
|
+
|
|
32
|
+
* **New**: Switch to having a central configuration and table of contents
|
|
33
|
+
* **Bugfix**: Added a missing content watch
|
|
34
|
+
|
|
35
|
+
# 0.5.0
|
|
36
|
+
|
|
37
|
+
* **New**: Integrate copier into the CLI
|
|
38
|
+
|
|
39
|
+
# 0.4.0
|
|
40
|
+
|
|
41
|
+
* **New**: Added a language-switcher extension
|
|
42
|
+
|
|
43
|
+
# 0.3.1
|
|
44
|
+
|
|
45
|
+
* **Bugfix**: Ensure that the output folder exists
|
|
46
|
+
|
|
47
|
+
# 0.3.0
|
|
48
|
+
|
|
49
|
+
* **New**: Support automatic landing page redirects
|
|
50
|
+
* **Update**: Clean up the build commands
|
|
51
|
+
|
|
52
|
+
# 0.2.1
|
|
53
|
+
|
|
54
|
+
* **Bugfix**: Fixed a bug in the section wrapping
|
|
55
|
+
|
|
56
|
+
# 0.2.0
|
|
57
|
+
|
|
58
|
+
* **New**: Added a serve command
|
|
59
|
+
* **Update**: Updated the TEI layout
|
|
60
|
+
|
|
61
|
+
# 0.1.0
|
|
62
|
+
|
|
63
|
+
* **New**: Added basic TEI parsing
|
|
64
|
+
|
|
65
|
+
# 0.0.3
|
|
66
|
+
|
|
67
|
+
* **Bugfix**: Workflow fixes
|
|
68
|
+
|
|
69
|
+
# 0.0.2
|
|
70
|
+
|
|
71
|
+
* **Bugfix**: Fix output path handling
|
|
72
|
+
|
|
73
|
+
# 0.0.1
|
|
74
|
+
|
|
75
|
+
* Initial release
|
uedition/__about__.py
CHANGED
uedition/__init__.py
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"""The uEdition toolkit."""
|
|
5
5
|
from sphinx.application import Sphinx
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from uedition import ext
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def setup(app: Sphinx):
|
|
11
|
-
"""
|
|
10
|
+
def setup(app: Sphinx) -> None:
|
|
11
|
+
"""Set up the theme and its extensions."""
|
|
12
12
|
ext.setup(app)
|
uedition/__main__.py
CHANGED
uedition/cli/__init__.py
CHANGED
|
@@ -3,20 +3,16 @@
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
"""uEdtion Command-line Tool."""
|
|
5
5
|
import typer
|
|
6
|
-
|
|
7
6
|
from rich import print as print_cli
|
|
8
7
|
|
|
9
|
-
from . import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
from ..__about__ import __version__
|
|
18
|
-
from ..settings import settings
|
|
19
|
-
|
|
8
|
+
from uedition.__about__ import __version__
|
|
9
|
+
from uedition.cli import build as build_module
|
|
10
|
+
from uedition.cli import check as check_module
|
|
11
|
+
from uedition.cli import create as create_module
|
|
12
|
+
from uedition.cli import language as language_module
|
|
13
|
+
from uedition.cli import serve as serve_module
|
|
14
|
+
from uedition.cli import update as update_module
|
|
15
|
+
from uedition.settings import settings
|
|
20
16
|
|
|
21
17
|
app = typer.Typer()
|
|
22
18
|
language_app = typer.Typer()
|
uedition/cli/build.py
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
"""Build functionality."""
|
|
5
5
|
import json
|
|
6
6
|
import subprocess
|
|
7
|
-
|
|
8
7
|
from copy import deepcopy
|
|
9
|
-
from os import
|
|
10
|
-
from shutil import
|
|
11
|
-
|
|
8
|
+
from os import makedirs, path
|
|
9
|
+
from shutil import copytree, ignore_patterns, rmtree
|
|
10
|
+
|
|
11
|
+
from yaml import safe_dump, safe_load
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from uedition.settings import reload_settings, settings
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def landing_build() -> None:
|
|
17
17
|
"""Build the landing page."""
|
|
18
|
-
if not path.exists(settings["output"]):
|
|
19
|
-
makedirs(settings["output"], exist_ok=True)
|
|
20
|
-
with open(path.join(settings["output"], "config.json"), "w") as out_f:
|
|
18
|
+
if not path.exists(settings["output"]["path"]):
|
|
19
|
+
makedirs(settings["output"]["path"], exist_ok=True)
|
|
20
|
+
with open(path.join(settings["output"]["path"], "config.json"), "w") as out_f:
|
|
21
21
|
json.dump(settings, out_f)
|
|
22
|
-
with open(path.join(settings["output"], "index.html"), "w") as out_f:
|
|
22
|
+
with open(path.join(settings["output"]["path"], "index.html"), "w") as out_f:
|
|
23
23
|
out_f.write(
|
|
24
24
|
"""\
|
|
25
25
|
<!DOCTYPE html>
|
|
@@ -74,16 +74,13 @@ def toc_build(lang: dict) -> None:
|
|
|
74
74
|
with open("toc.yml") as in_f:
|
|
75
75
|
toc = safe_load(in_f)
|
|
76
76
|
|
|
77
|
-
def walk(
|
|
77
|
+
def walk(source: dict) -> dict:
|
|
78
78
|
output = {}
|
|
79
|
-
for key, value in
|
|
79
|
+
for key, value in source.items():
|
|
80
80
|
if isinstance(value, dict):
|
|
81
81
|
if lang["code"] in value:
|
|
82
82
|
output[key] = value[lang["code"]]
|
|
83
|
-
elif (
|
|
84
|
-
len(settings["languages"]) > 0
|
|
85
|
-
and settings["languages"][0]["code"] in value
|
|
86
|
-
):
|
|
83
|
+
elif len(settings["languages"]) > 0 and settings["languages"][0]["code"] in value:
|
|
87
84
|
output[key] = value[settings["languages"][0]["code"]]
|
|
88
85
|
else:
|
|
89
86
|
output[key] = ""
|
|
@@ -104,10 +101,7 @@ def config_build(lang: dict) -> None:
|
|
|
104
101
|
# Set the language-specific title
|
|
105
102
|
if lang["code"] in settings["title"]:
|
|
106
103
|
config["title"] = settings["title"][lang["code"]]
|
|
107
|
-
elif (
|
|
108
|
-
len(settings["languages"]) > 0
|
|
109
|
-
and settings["languages"][0]["code"] in settings["title"]
|
|
110
|
-
):
|
|
104
|
+
elif len(settings["languages"]) > 0 and settings["languages"][0]["code"] in settings["title"]:
|
|
111
105
|
config["title"] = settings["title"][settings["languages"][0]["code"]]
|
|
112
106
|
else:
|
|
113
107
|
config["title"] = f"Missing title for {lang['label']}"
|
|
@@ -152,44 +146,91 @@ def full_build(lang: dict) -> None:
|
|
|
152
146
|
config_build(lang)
|
|
153
147
|
static_build(lang)
|
|
154
148
|
subprocess.run(
|
|
155
|
-
[
|
|
149
|
+
[ # noqa: S603, S607
|
|
156
150
|
"jupyter-book",
|
|
157
151
|
"build",
|
|
158
152
|
"--all",
|
|
159
153
|
"--path-output",
|
|
160
|
-
path.join("_build", lang["
|
|
154
|
+
path.join("_build", lang["path"]),
|
|
161
155
|
lang["path"],
|
|
162
|
-
]
|
|
156
|
+
],
|
|
157
|
+
check=False,
|
|
163
158
|
)
|
|
159
|
+
if settings["output"]["tei"]:
|
|
160
|
+
subprocess.run(
|
|
161
|
+
[ # noqa: S603, S607
|
|
162
|
+
"jupyter-book",
|
|
163
|
+
"build",
|
|
164
|
+
"--all",
|
|
165
|
+
"--path-output",
|
|
166
|
+
path.join("_build", lang["path"]),
|
|
167
|
+
"--builder",
|
|
168
|
+
"custom",
|
|
169
|
+
"--custom-builder",
|
|
170
|
+
"tei",
|
|
171
|
+
lang["path"],
|
|
172
|
+
],
|
|
173
|
+
check=False,
|
|
174
|
+
)
|
|
164
175
|
copytree(
|
|
165
|
-
path.join("_build", lang["
|
|
166
|
-
path.join(settings["output"], lang["
|
|
176
|
+
path.join("_build", lang["path"], "_build", "html"),
|
|
177
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
167
178
|
dirs_exist_ok=True,
|
|
168
179
|
)
|
|
180
|
+
if settings["output"]["tei"]:
|
|
181
|
+
copytree(
|
|
182
|
+
path.join("_build", lang["path"], "_build", "tei"),
|
|
183
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
184
|
+
ignore=ignore_patterns("_sphinx_design_static"),
|
|
185
|
+
dirs_exist_ok=True,
|
|
186
|
+
)
|
|
169
187
|
|
|
170
188
|
|
|
171
189
|
def partial_build(lang: dict) -> None:
|
|
172
190
|
"""Run the as-needed build process for a single language."""
|
|
173
191
|
landing_build()
|
|
174
192
|
subprocess.run(
|
|
175
|
-
[
|
|
193
|
+
[ # noqa: S603, S607
|
|
176
194
|
"jupyter-book",
|
|
177
195
|
"build",
|
|
178
196
|
"--path-output",
|
|
179
|
-
path.join("_build", lang["
|
|
197
|
+
path.join("_build", lang["path"]),
|
|
180
198
|
lang["path"],
|
|
181
|
-
]
|
|
199
|
+
],
|
|
200
|
+
check=False,
|
|
182
201
|
)
|
|
202
|
+
if settings["output"]["tei"]:
|
|
203
|
+
subprocess.run(
|
|
204
|
+
[ # noqa: S603, S607
|
|
205
|
+
"jupyter-book",
|
|
206
|
+
"build",
|
|
207
|
+
"--path-output",
|
|
208
|
+
path.join("_build", lang["path"]),
|
|
209
|
+
"--builder",
|
|
210
|
+
"custom",
|
|
211
|
+
"--custom-builder",
|
|
212
|
+
"tei",
|
|
213
|
+
lang["path"],
|
|
214
|
+
],
|
|
215
|
+
check=False,
|
|
216
|
+
)
|
|
183
217
|
copytree(
|
|
184
|
-
path.join("_build", lang["
|
|
185
|
-
path.join(settings["output"], lang["
|
|
218
|
+
path.join("_build", lang["path"], "_build", "html"),
|
|
219
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
186
220
|
dirs_exist_ok=True,
|
|
187
221
|
)
|
|
222
|
+
if settings["output"]["tei"]:
|
|
223
|
+
copytree(
|
|
224
|
+
path.join("_build", lang["path"], "_build", "tei"),
|
|
225
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
226
|
+
ignore=ignore_patterns("_sphinx_design_static"),
|
|
227
|
+
dirs_exist_ok=True,
|
|
228
|
+
)
|
|
188
229
|
|
|
189
230
|
|
|
190
231
|
def run() -> None:
|
|
191
232
|
"""Build the full uEdition."""
|
|
192
|
-
if path.exists(settings["output"]):
|
|
193
|
-
rmtree(settings["output"])
|
|
233
|
+
if path.exists(settings["output"]["path"]):
|
|
234
|
+
rmtree(settings["output"]["path"])
|
|
194
235
|
for lang in settings["languages"]:
|
|
195
236
|
full_build(lang)
|
uedition/cli/check.py
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
"""The uEdition check functionality for validating a uEdition and its files."""
|
|
5
5
|
import os
|
|
6
|
-
import
|
|
6
|
+
from threading import Thread
|
|
7
7
|
|
|
8
|
+
import typer
|
|
8
9
|
from rich import print as print_cli
|
|
9
10
|
from rich.progress import Progress
|
|
10
|
-
from threading import Thread
|
|
11
11
|
from yaml import safe_load
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from uedition.settings import settings
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def collect_files(toc: dict) -> list[str]:
|
|
@@ -29,9 +29,7 @@ def collect_files(toc: dict) -> list[str]:
|
|
|
29
29
|
return files
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def compare_tocs(
|
|
33
|
-
prefix: str, toc_a: dict, toc_b: dict
|
|
34
|
-
) -> list[tuple[str | None, str | None]]:
|
|
32
|
+
def compare_tocs(prefix: str, toc_a: dict, toc_b: dict) -> list[tuple[str | None, str | None]]:
|
|
35
33
|
"""Recursively compare the structure of two TOCs."""
|
|
36
34
|
mismatches = []
|
|
37
35
|
if "parts" in toc_a and "parts" in toc_b:
|
|
@@ -41,20 +39,18 @@ def compare_tocs(
|
|
|
41
39
|
elif "sections" in toc_a and "sections" in toc_b:
|
|
42
40
|
pass
|
|
43
41
|
elif "parts" in toc_a:
|
|
44
|
-
mismatches.append(
|
|
42
|
+
mismatches.append(f"{prefix}has parts, which are missing from")
|
|
45
43
|
elif "chapters" in toc_a:
|
|
46
|
-
mismatches.append(
|
|
44
|
+
mismatches.append(f"{prefix}has chapters, which are missing from")
|
|
47
45
|
elif "sections" in toc_b:
|
|
48
|
-
mismatches.append(
|
|
46
|
+
mismatches.append(f"{prefix}has sections, which are missing from")
|
|
49
47
|
return mismatches
|
|
50
48
|
|
|
51
49
|
|
|
52
50
|
class ConfigurationFileChecks(Thread):
|
|
53
51
|
"""Basic configuration file checks."""
|
|
54
52
|
|
|
55
|
-
def __init__(
|
|
56
|
-
self: "ConfigurationFileChecks", progress: Progress, task: int
|
|
57
|
-
) -> None:
|
|
53
|
+
def __init__(self: "ConfigurationFileChecks", progress: Progress, task: int) -> None:
|
|
58
54
|
"""Initialise the thread."""
|
|
59
55
|
super().__init__(group=None)
|
|
60
56
|
self._progress = progress
|
|
@@ -97,17 +93,11 @@ class TocFileChecks(Thread):
|
|
|
97
93
|
if "root" in toc:
|
|
98
94
|
root_path = os.path.join(lang["path"], f'{toc["root"]}.md')
|
|
99
95
|
if not os.path.exists(root_path):
|
|
100
|
-
self.errors.append(
|
|
101
|
-
f'Root in {yaml_path} points to missing file {toc["root"]}.md'
|
|
102
|
-
)
|
|
96
|
+
self.errors.append(f'Root in {yaml_path} points to missing file {toc["root"]}.md')
|
|
103
97
|
root_base = os.path.dirname(root_path)
|
|
104
98
|
for filename in collect_files(toc):
|
|
105
|
-
if not os.path.exists(
|
|
106
|
-
|
|
107
|
-
):
|
|
108
|
-
self.errors.append(
|
|
109
|
-
f"File {filename}.md missing in {yaml_path}"
|
|
110
|
-
)
|
|
99
|
+
if not os.path.exists(os.path.join(root_base, f"{filename}.md")):
|
|
100
|
+
self.errors.append(f"File {filename}.md missing in {yaml_path}")
|
|
111
101
|
else:
|
|
112
102
|
self.errors.append(f"No root in {yaml_path}")
|
|
113
103
|
except Exception as e:
|
|
@@ -120,9 +110,7 @@ class TocFileChecks(Thread):
|
|
|
120
110
|
class LanguageConsistencyChecks(Thread):
|
|
121
111
|
"""Multi-language consistency checks."""
|
|
122
112
|
|
|
123
|
-
def __init__(
|
|
124
|
-
self: "LanguageConsistencyChecks", progress: Progress, task: int
|
|
125
|
-
) -> None:
|
|
113
|
+
def __init__(self: "LanguageConsistencyChecks", progress: Progress, task: int) -> None:
|
|
126
114
|
"""Initialise the thread."""
|
|
127
115
|
super().__init__(group=None)
|
|
128
116
|
self._progress = progress
|
|
@@ -148,20 +136,16 @@ class LanguageConsistencyChecks(Thread):
|
|
|
148
136
|
try:
|
|
149
137
|
with open(lang_toc_path) as in_f:
|
|
150
138
|
lang_toc = safe_load(in_f)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
self.errors.append(
|
|
162
|
-
f"{lang_toc_path} {mismatch[0]} {base_toc_path}"
|
|
163
|
-
)
|
|
164
|
-
self._progress.update(self._task, advance=1)
|
|
139
|
+
missmatches = compare_tocs("", base_toc, lang_toc)
|
|
140
|
+
if len(missmatches) > 0:
|
|
141
|
+
for mismatch in missmatches:
|
|
142
|
+
if mismatch[0] is None:
|
|
143
|
+
self.errors.append(f"{base_toc_path} {mismatch[1]} {lang_toc_path}")
|
|
144
|
+
elif mismatch[0] is None:
|
|
145
|
+
self.errors.append(f"{lang_toc_path} {mismatch[0]} {base_toc_path}")
|
|
146
|
+
self._progress.update(self._task, advance=1)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
self.errors.append(f"Fail to check langauge {lang}: {e!s}")
|
|
165
149
|
|
|
166
150
|
|
|
167
151
|
def run() -> None:
|
|
@@ -171,15 +155,11 @@ def run() -> None:
|
|
|
171
155
|
threads = [
|
|
172
156
|
ConfigurationFileChecks(
|
|
173
157
|
progress,
|
|
174
|
-
progress.add_task(
|
|
175
|
-
"[green]Configuration file checks", total=len(settings["languages"])
|
|
176
|
-
),
|
|
158
|
+
progress.add_task("[green]Configuration file checks", total=len(settings["languages"])),
|
|
177
159
|
),
|
|
178
160
|
TocFileChecks(
|
|
179
161
|
progress,
|
|
180
|
-
progress.add_task(
|
|
181
|
-
"[green]TOC file checks", total=len(settings["languages"])
|
|
182
|
-
),
|
|
162
|
+
progress.add_task("[green]TOC file checks", total=len(settings["languages"])),
|
|
183
163
|
),
|
|
184
164
|
LanguageConsistencyChecks(
|
|
185
165
|
progress,
|
uedition/cli/language.py
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import os
|
|
6
6
|
|
|
7
7
|
from copier import run_copy, run_update
|
|
8
|
-
from yaml import
|
|
8
|
+
from yaml import dump, safe_load
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def add(path: str) -> None:
|
|
@@ -25,18 +25,14 @@ def add(path: str) -> None:
|
|
|
25
25
|
else:
|
|
26
26
|
config["languages"] = []
|
|
27
27
|
if not found:
|
|
28
|
-
config["languages"].append(
|
|
29
|
-
{"code": answers["code"], "label": answers["label"], "path": path}
|
|
30
|
-
)
|
|
28
|
+
config["languages"].append({"code": answers["code"], "label": answers["label"], "path": path})
|
|
31
29
|
with open("uEdition.yml", "w") as out_f:
|
|
32
30
|
dump(config, out_f)
|
|
33
31
|
|
|
34
32
|
|
|
35
33
|
def update(path: str) -> None:
|
|
36
34
|
"""Update a language to the latest template."""
|
|
37
|
-
run_update(
|
|
38
|
-
path, answers_file=".uEdition.answers", overwrite=True, data={"path": path}
|
|
39
|
-
)
|
|
35
|
+
run_update(path, answers_file=".uEdition.answers", overwrite=True, data={"path": path})
|
|
40
36
|
with open(os.path.join(path, ".uEdition.answers")) as in_f:
|
|
41
37
|
answers = safe_load(in_f)
|
|
42
38
|
with open("uEdition.yml") as in_f:
|
|
@@ -51,8 +47,6 @@ def update(path: str) -> None:
|
|
|
51
47
|
else:
|
|
52
48
|
config["languages"] = []
|
|
53
49
|
if not found:
|
|
54
|
-
config["languages"].append(
|
|
55
|
-
{"code": answers["code"], "label": answers["label"], "path": path}
|
|
56
|
-
)
|
|
50
|
+
config["languages"].append({"code": answers["code"], "label": answers["label"], "path": path})
|
|
57
51
|
with open("uEdition.yml", "w") as out_f:
|
|
58
52
|
dump(config, out_f)
|
uedition/cli/serve.py
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
"""Local server that automatically rebuilds on changes."""
|
|
5
|
-
from livereload import Server
|
|
6
5
|
from os import path
|
|
7
6
|
from typing import Callable
|
|
8
7
|
|
|
9
|
-
from
|
|
10
|
-
|
|
8
|
+
from livereload import Server
|
|
9
|
+
|
|
10
|
+
from uedition.cli.build import full_build, partial_build
|
|
11
|
+
from uedition.settings import settings
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def build_cmd(lang: dict, full: bool = True) -> Callable[[], None]:
|
|
14
|
+
def build_cmd(lang: dict, full: bool = True) -> Callable[[], None]: # noqa: FBT001, FBT002
|
|
14
15
|
"""Create a function that (re-)builds one of the sub-sites."""
|
|
15
16
|
if full:
|
|
16
17
|
|
|
@@ -33,11 +34,9 @@ def run() -> None:
|
|
|
33
34
|
for cmd in full_rebuilds:
|
|
34
35
|
cmd()
|
|
35
36
|
server = Server()
|
|
36
|
-
for lang, full_cmd, partial_cmd in zip(
|
|
37
|
-
settings["languages"], full_rebuilds, partial_rebuilds
|
|
38
|
-
):
|
|
37
|
+
for lang, full_cmd, partial_cmd in zip(settings["languages"], full_rebuilds, partial_rebuilds):
|
|
39
38
|
server.watch("*.yml", full_cmd)
|
|
40
39
|
server.watch(path.join("static", "**", "*.*"), full_cmd)
|
|
41
40
|
server.watch(path.join(lang["path"], "**", "*.*"), partial_cmd)
|
|
42
41
|
server.watch("uEdition.*", lambda: [cmd() for cmd in full_rebuilds])
|
|
43
|
-
server.serve(root="
|
|
42
|
+
server.serve(root=settings["output"]["path"], port=8000)
|
uedition/cli/update.py
CHANGED
uedition/ext/__init__.py
CHANGED
uedition/ext/config.py
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
This module handles reading the uEdition-specific configuration settings, validating them and
|
|
7
7
|
adding any required default values.
|
|
8
8
|
"""
|
|
9
|
-
from
|
|
9
|
+
from typing import Annotated, Any, Literal, Optional, Union
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ValidationError
|
|
12
|
+
from pydantic.functional_validators import BeforeValidator
|
|
10
13
|
from sphinx.application import Sphinx
|
|
11
14
|
from sphinx.util import logging
|
|
12
|
-
from typing import Union, Literal, Optional
|
|
13
|
-
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -22,27 +23,23 @@ class RuleSelectorAttribute(BaseModel):
|
|
|
22
23
|
value: str
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
"""
|
|
26
|
+
def expand_tei_namespace(value: str) -> str:
|
|
27
|
+
"""Expand any ```tei:``` namespace prefixes."""
|
|
28
|
+
return value.replace("tei:", "{http://www.tei-c.org/ns/1.0}")
|
|
27
29
|
|
|
28
|
-
tag: str
|
|
29
|
-
attributes: list[RuleSelectorAttribute] = []
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return v.replace("tei:", "{http://www.tei-c.org/ns/1.0}")
|
|
31
|
+
def force_list(value: Any) -> list: # noqa: ANN401
|
|
32
|
+
"""Force the value into a list form."""
|
|
33
|
+
if isinstance(value, list):
|
|
34
|
+
return value
|
|
35
|
+
return [value]
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return [v]
|
|
45
|
-
return v
|
|
37
|
+
|
|
38
|
+
class RuleSelector(BaseModel):
|
|
39
|
+
"""Validation rule for the selector for matching a TEI tag."""
|
|
40
|
+
|
|
41
|
+
tag: Annotated[str, BeforeValidator(expand_tei_namespace)]
|
|
42
|
+
attributes: Annotated[list[RuleSelectorAttribute], BeforeValidator(force_list)] = []
|
|
46
43
|
|
|
47
44
|
|
|
48
45
|
class RuleText(BaseModel):
|
|
@@ -75,24 +72,20 @@ class RuleAttributeDelete(BaseModel):
|
|
|
75
72
|
attr: str
|
|
76
73
|
|
|
77
74
|
|
|
75
|
+
def convert_string_to_selector_dict(value: str | dict) -> dict:
|
|
76
|
+
"""Convert a simple string selector into the dictionary representation."""
|
|
77
|
+
if isinstance(value, str):
|
|
78
|
+
return {"tag": value}
|
|
79
|
+
return value
|
|
80
|
+
|
|
81
|
+
|
|
78
82
|
class Rule(BaseModel):
|
|
79
83
|
"""Validation model for a rule transforming a TEI tag into a HTML tag."""
|
|
80
84
|
|
|
81
|
-
selector: RuleSelector
|
|
85
|
+
selector: Annotated[RuleSelector, BeforeValidator(convert_string_to_selector_dict)]
|
|
82
86
|
tag: Union[str, None] = "div"
|
|
83
87
|
text: Union[RuleText, None] = None
|
|
84
|
-
attributes: list[
|
|
85
|
-
Union[RuleAttributeCopy, RuleAttributeSet, RuleAttributeDelete]
|
|
86
|
-
] = []
|
|
87
|
-
|
|
88
|
-
@validator("selector", pre=True)
|
|
89
|
-
def convert_str_selector_to_dict(
|
|
90
|
-
cls: "Rule", v: str | dict, values: dict, **kwargs: dict
|
|
91
|
-
) -> dict:
|
|
92
|
-
"""Convert a simple string selector into the dictionary representation."""
|
|
93
|
-
if isinstance(v, str):
|
|
94
|
-
return {"tag": v}
|
|
95
|
-
return v
|
|
88
|
+
attributes: list[Union[RuleAttributeCopy, RuleAttributeSet, RuleAttributeDelete]] = []
|
|
96
89
|
|
|
97
90
|
|
|
98
91
|
class TextSection(BaseModel):
|
|
@@ -166,29 +159,17 @@ BASE_RULES = [
|
|
|
166
159
|
"""Base mapping rules for mapping TEI tags to default HTML elements."""
|
|
167
160
|
|
|
168
161
|
|
|
169
|
-
def validate_config(app: Sphinx, config: Config) -> None:
|
|
162
|
+
def validate_config(app: Sphinx, config: Config) -> None: # noqa: ARG001
|
|
170
163
|
"""Validate the configuration and add any default values."""
|
|
171
164
|
if config.uEdition:
|
|
172
165
|
if "tei" in config.uEdition:
|
|
173
|
-
if "sections" in config.uEdition["tei"] and isinstance(
|
|
174
|
-
config.uEdition["tei"]["
|
|
175
|
-
):
|
|
176
|
-
if "mappings" in config.uEdition["tei"] and isinstance(
|
|
177
|
-
config.uEdition["tei"]["mappings"], list
|
|
178
|
-
):
|
|
166
|
+
if "sections" in config.uEdition["tei"] and isinstance(config.uEdition["tei"]["sections"], list):
|
|
167
|
+
if "mappings" in config.uEdition["tei"] and isinstance(config.uEdition["tei"]["mappings"], list):
|
|
179
168
|
for section in config.uEdition["tei"]["sections"]:
|
|
180
|
-
if "mappings" in section and isinstance(
|
|
181
|
-
section["mappings"]
|
|
182
|
-
):
|
|
183
|
-
section["mappings"] = (
|
|
184
|
-
section["mappings"]
|
|
185
|
-
+ config.uEdition["tei"]["mappings"]
|
|
186
|
-
+ BASE_RULES
|
|
187
|
-
)
|
|
169
|
+
if "mappings" in section and isinstance(section["mappings"], list):
|
|
170
|
+
section["mappings"] = section["mappings"] + config.uEdition["tei"]["mappings"] + BASE_RULES
|
|
188
171
|
else:
|
|
189
|
-
section["mappings"] =
|
|
190
|
-
config.uEdition["tei"]["mappings"] + BASE_RULES
|
|
191
|
-
)
|
|
172
|
+
section["mappings"] = config.uEdition["tei"]["mappings"] + BASE_RULES
|
|
192
173
|
try:
|
|
193
174
|
config.uEdition = Config(**config.uEdition).dict()
|
|
194
175
|
except ValidationError as e:
|