uEdition 2.0.0a1__py3-none-any.whl → 2.0.0b1__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/__about__.py +1 -1
- uedition/cli/__init__.py +6 -49
- uedition/cli/base.py +8 -0
- uedition/cli/build.py +22 -18
- uedition/cli/init.py +22 -0
- uedition/cli/language.py +50 -45
- uedition/cli/migrate.py +137 -0
- uedition/cli/serve.py +5 -2
- uedition/cli/update.py +75 -7
- uedition/ext/tei/builder.py +19 -0
- uedition/ext/tei/parser.py +18 -9
- {uedition-2.0.0a1.dist-info → uedition-2.0.0b1.dist-info}/METADATA +5 -2
- uedition-2.0.0b1.dist-info/RECORD +27 -0
- uedition/cli/create.py +0 -10
- uedition-2.0.0a1.dist-info/RECORD +0 -25
- {uedition-2.0.0a1.dist-info → uedition-2.0.0b1.dist-info}/WHEEL +0 -0
- {uedition-2.0.0a1.dist-info → uedition-2.0.0b1.dist-info}/entry_points.txt +0 -0
- {uedition-2.0.0a1.dist-info → uedition-2.0.0b1.dist-info}/licenses/LICENSE.txt +0 -0
uedition/__about__.py
CHANGED
uedition/cli/__init__.py
CHANGED
|
@@ -2,60 +2,17 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
"""uEdtion Command-line Tool."""
|
|
5
|
-
|
|
6
|
-
from rich import print as
|
|
5
|
+
|
|
6
|
+
from rich import print as output
|
|
7
7
|
|
|
8
8
|
from uedition.__about__ import __version__
|
|
9
|
-
from uedition.cli import build
|
|
10
|
-
from uedition.cli import
|
|
11
|
-
from uedition.cli import language as language_module
|
|
12
|
-
from uedition.cli import serve as serve_module
|
|
13
|
-
from uedition.cli import update as update_module
|
|
9
|
+
from uedition.cli import build, init, language, migrate, serve, update # noqa:F401
|
|
10
|
+
from uedition.cli.base import app
|
|
14
11
|
from uedition.settings import settings
|
|
15
12
|
|
|
16
|
-
app = typer.Typer()
|
|
17
|
-
language_app = typer.Typer()
|
|
18
|
-
app.add_typer(language_app, name="language")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@app.command()
|
|
22
|
-
def create(path: str) -> None:
|
|
23
|
-
"""Create a new μEdition."""
|
|
24
|
-
create_module.run(path)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@app.command()
|
|
28
|
-
def build() -> None:
|
|
29
|
-
"""Build the μEdition."""
|
|
30
|
-
build_module.run()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@app.command()
|
|
34
|
-
def serve() -> None:
|
|
35
|
-
"""Serve the μEdition for writing."""
|
|
36
|
-
serve_module.run()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@app.command()
|
|
40
|
-
def update() -> None:
|
|
41
|
-
"""Update the μEdition."""
|
|
42
|
-
update_module.run()
|
|
43
|
-
|
|
44
13
|
|
|
45
14
|
@app.command()
|
|
46
15
|
def version() -> None:
|
|
47
16
|
"""Output the current μEdition version."""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@language_app.command("add")
|
|
53
|
-
def language_add(path: str) -> None:
|
|
54
|
-
"""Add a language to the μEdition."""
|
|
55
|
-
language_module.add(path)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@language_app.command("update")
|
|
59
|
-
def language_update(path: str) -> None:
|
|
60
|
-
"""Update a language."""
|
|
61
|
-
language_module.update(path)
|
|
17
|
+
output(f"μEdition: {__version__}")
|
|
18
|
+
output(f"Configuration: {settings['version']}")
|
uedition/cli/base.py
ADDED
uedition/cli/build.py
CHANGED
|
@@ -10,6 +10,7 @@ from shutil import copytree, ignore_patterns, rmtree
|
|
|
10
10
|
|
|
11
11
|
from yaml import safe_dump, safe_load
|
|
12
12
|
|
|
13
|
+
from uedition.cli.base import app
|
|
13
14
|
from uedition.settings import NoConfigError, reload_settings, settings
|
|
14
15
|
|
|
15
16
|
LANDING_PAGE_TEMPLATE = """\
|
|
@@ -187,18 +188,19 @@ def full_build(lang: dict) -> None:
|
|
|
187
188
|
check=False,
|
|
188
189
|
shell=False,
|
|
189
190
|
)
|
|
190
|
-
|
|
191
|
-
path.join("_build", lang["path"], "html"),
|
|
192
|
-
path.join(settings["output"]["path"], lang["path"]),
|
|
193
|
-
dirs_exist_ok=True,
|
|
194
|
-
)
|
|
195
|
-
if settings["output"]["tei"]:
|
|
191
|
+
if path.isdir(path.join("_build", lang["path"], "html")):
|
|
196
192
|
copytree(
|
|
197
|
-
path.join("_build", lang["path"], "
|
|
193
|
+
path.join("_build", lang["path"], "html"),
|
|
198
194
|
path.join(settings["output"]["path"], lang["path"]),
|
|
199
|
-
ignore=ignore_patterns("_sphinx_design_static"),
|
|
200
195
|
dirs_exist_ok=True,
|
|
201
196
|
)
|
|
197
|
+
if settings["output"]["tei"] and path.isdir(path.join("_build", lang["path"], "tei")):
|
|
198
|
+
copytree(
|
|
199
|
+
path.join("_build", lang["path"], "tei"),
|
|
200
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
201
|
+
ignore=ignore_patterns("_sphinx_design_static"),
|
|
202
|
+
dirs_exist_ok=True,
|
|
203
|
+
)
|
|
202
204
|
|
|
203
205
|
|
|
204
206
|
def partial_build(lang: dict) -> None:
|
|
@@ -227,22 +229,24 @@ def partial_build(lang: dict) -> None:
|
|
|
227
229
|
check=False,
|
|
228
230
|
shell=False,
|
|
229
231
|
)
|
|
230
|
-
|
|
231
|
-
path.join("_build", lang["path"], "html"),
|
|
232
|
-
path.join(settings["output"]["path"], lang["path"]),
|
|
233
|
-
dirs_exist_ok=True,
|
|
234
|
-
)
|
|
235
|
-
if settings["output"]["tei"]:
|
|
232
|
+
if path.isdir(path.join("_build", lang["path"], "html")):
|
|
236
233
|
copytree(
|
|
237
|
-
path.join("_build", lang["path"], "
|
|
234
|
+
path.join("_build", lang["path"], "html"),
|
|
238
235
|
path.join(settings["output"]["path"], lang["path"]),
|
|
239
|
-
ignore=ignore_patterns("_sphinx_design_static"),
|
|
240
236
|
dirs_exist_ok=True,
|
|
241
237
|
)
|
|
238
|
+
if settings["output"]["tei"] and path.isdir(path.join("_build", lang["path"], "tei")):
|
|
239
|
+
copytree(
|
|
240
|
+
path.join("_build", lang["path"], "tei"),
|
|
241
|
+
path.join(settings["output"]["path"], lang["path"]),
|
|
242
|
+
ignore=ignore_patterns("_sphinx_design_static"),
|
|
243
|
+
dirs_exist_ok=True,
|
|
244
|
+
)
|
|
242
245
|
|
|
243
246
|
|
|
244
|
-
|
|
245
|
-
|
|
247
|
+
@app.command()
|
|
248
|
+
def build() -> None:
|
|
249
|
+
"""Build the full μEdition."""
|
|
246
250
|
if not path.exists("uEdition.yml") and not path.exists("uEdition.yaml"):
|
|
247
251
|
raise NoConfigError()
|
|
248
252
|
if path.exists(settings["output"]["path"]):
|
uedition/cli/init.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-present Mark Hall <mark.hall@work.room3b.eu>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""Local server that automatically rebuilds on changes."""
|
|
5
|
+
|
|
6
|
+
from os import path
|
|
7
|
+
|
|
8
|
+
from yaml import safe_dump
|
|
9
|
+
|
|
10
|
+
from uedition.cli.base import app
|
|
11
|
+
from uedition.settings import NoConfigError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command()
|
|
15
|
+
def init(force: bool = False) -> None: # noqa: ARG001 FBT001 FBT002
|
|
16
|
+
"""Initialise a new μEdition."""
|
|
17
|
+
if path.exists("uEdition.yml") and path.exists("uEdition.yaml"):
|
|
18
|
+
raise NoConfigError()
|
|
19
|
+
with open("uEdition.yml", "w") as out_f:
|
|
20
|
+
safe_dump({"version": "2", "output": "site"}, out_f)
|
|
21
|
+
with open("toc.yml", "w") as out_f:
|
|
22
|
+
safe_dump({"format": "jb-book", "root": "index"}, out_f)
|
uedition/cli/language.py
CHANGED
|
@@ -1,58 +1,63 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2023-present Mark Hall <mark.hall@work.room3b.eu>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
"""The μEdition language functionality
|
|
4
|
+
"""The μEdition language functionality."""
|
|
5
|
+
|
|
5
6
|
import os
|
|
7
|
+
from typing import Annotated
|
|
6
8
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
+
from rich import print as output
|
|
10
|
+
from typer import Option, Typer
|
|
11
|
+
from yaml import safe_dump, safe_load
|
|
9
12
|
|
|
13
|
+
from uedition.cli.base import app
|
|
10
14
|
from uedition.settings import NoConfigError
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"""Add a language to the μEdition using Copier."""
|
|
15
|
-
if not os.path.exists("uEdition.yml") and not os.path.exists("uEdition.yaml"):
|
|
16
|
-
raise NoConfigError()
|
|
17
|
-
run_copy("gh:uEdition/uEdition-language-template", path, data={"path": path})
|
|
18
|
-
with open(os.path.join(path, ".uEdition.answers")) as in_f:
|
|
19
|
-
answers = safe_load(in_f)
|
|
20
|
-
with open("uEdition.yml") as in_f:
|
|
21
|
-
config = safe_load(in_f)
|
|
22
|
-
found = False
|
|
23
|
-
if "languages" in config:
|
|
24
|
-
for lang in config["languages"]:
|
|
25
|
-
if lang["code"] == answers["code"]:
|
|
26
|
-
lang["label"] = answers["label"]
|
|
27
|
-
lang["path"] = path
|
|
28
|
-
found = True
|
|
29
|
-
else:
|
|
30
|
-
config["languages"] = []
|
|
31
|
-
if not found:
|
|
32
|
-
config["languages"].append({"code": answers["code"], "label": answers["label"], "path": path})
|
|
33
|
-
with open("uEdition.yml", "w") as out_f:
|
|
34
|
-
dump(config, out_f)
|
|
16
|
+
lang_app = Typer(help="Language configuration functionality")
|
|
17
|
+
app.add_typer(lang_app, name="language")
|
|
35
18
|
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
20
|
+
@lang_app.command()
|
|
21
|
+
def add(
|
|
22
|
+
code: Annotated[str, Option(prompt="Language code")],
|
|
23
|
+
label: Annotated[
|
|
24
|
+
str,
|
|
25
|
+
Option(prompt="Language name"),
|
|
26
|
+
],
|
|
27
|
+
title: Annotated[str, Option(prompt="Title")],
|
|
28
|
+
path: str | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Add a language."""
|
|
31
|
+
if path is None:
|
|
32
|
+
path = code
|
|
33
|
+
if os.path.exists("uEdition.yml"):
|
|
34
|
+
with open("uEdition.yml") as in_f:
|
|
35
|
+
config = safe_load(in_f)
|
|
36
|
+
elif os.path.exists("uEdition.yaml"):
|
|
37
|
+
with open("uEdition.yaml") as in_f:
|
|
38
|
+
config = safe_load(in_f)
|
|
53
39
|
else:
|
|
40
|
+
raise NoConfigError()
|
|
41
|
+
if "languages" not in config:
|
|
54
42
|
config["languages"] = []
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
for lang in config["languages"]:
|
|
44
|
+
if lang["code"] == code:
|
|
45
|
+
output(
|
|
46
|
+
"[red bold]The language code [/red bold]"
|
|
47
|
+
f"[cyan bold]{code}[/cyan bold]"
|
|
48
|
+
"[red bold] is already configured[/red bold]"
|
|
49
|
+
)
|
|
50
|
+
return
|
|
51
|
+
if os.path.exists(path):
|
|
52
|
+
output(
|
|
53
|
+
f"[red bold]The target path [/red bold][cyan bold]{code}[/cyan bold][red bold] already exists.[/red bold]"
|
|
54
|
+
)
|
|
55
|
+
os.mkdir(path)
|
|
56
|
+
config["languages"].append({"code": code, "label": label, "path": path})
|
|
57
|
+
if "title" not in config:
|
|
58
|
+
config["title"] = {}
|
|
59
|
+
config["title"][code] = title
|
|
57
60
|
with open("uEdition.yml", "w") as out_f:
|
|
58
|
-
|
|
61
|
+
safe_dump(config, out_f)
|
|
62
|
+
with open(os.path.join(path, "index.md"), "w") as out_f:
|
|
63
|
+
out_f.write(f"# {title}\n")
|
uedition/cli/migrate.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-present Mark Hall <mark.hall@work.room3b.eu>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""The μEdition migration functionality."""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from os import path
|
|
8
|
+
|
|
9
|
+
import tomlkit
|
|
10
|
+
from rich import print as output
|
|
11
|
+
|
|
12
|
+
from uedition.cli.base import app
|
|
13
|
+
from uedition.settings import NoConfigError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cleanup_old_files() -> None:
|
|
17
|
+
"""Cleanup any old files."""
|
|
18
|
+
# Scan for old files to remove
|
|
19
|
+
old_copier_files = []
|
|
20
|
+
old_config_files = []
|
|
21
|
+
for basepath, _, filenames in os.walk("."):
|
|
22
|
+
for filename in filenames:
|
|
23
|
+
if filename == ".uEdition.answers":
|
|
24
|
+
old_copier_files.append(path.join(basepath, filename))
|
|
25
|
+
elif filename in ("_config.yaml", "_config.yml"):
|
|
26
|
+
old_config_files.append(path.join(basepath, filename))
|
|
27
|
+
# Remove old copier files (v2.0.0)
|
|
28
|
+
if len(old_copier_files) > 0:
|
|
29
|
+
output(":broom: Removing old configuration answers")
|
|
30
|
+
for filename in old_copier_files:
|
|
31
|
+
os.unlink(filename)
|
|
32
|
+
# Remove old config files (v2.0.0)
|
|
33
|
+
if len(old_config_files) > 0:
|
|
34
|
+
output(":broom: Removing old configuration files")
|
|
35
|
+
for filename in old_config_files:
|
|
36
|
+
os.unlink(filename)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cleanup_gitignore() -> None:
|
|
40
|
+
"""Cleanup the gitignore."""
|
|
41
|
+
# Update .gitignore (v2.0.0)
|
|
42
|
+
if path.isfile(".gitignore"):
|
|
43
|
+
with open(".gitignore") as in_f:
|
|
44
|
+
lines = in_f.readlines()
|
|
45
|
+
original_length = len(lines)
|
|
46
|
+
lines = [line for line in lines if line.strip() not in ("_config.yml", "_config.yaml")]
|
|
47
|
+
found = False
|
|
48
|
+
for line in lines:
|
|
49
|
+
if line.strip() == "conf.py":
|
|
50
|
+
found = True
|
|
51
|
+
if not found or len(lines) != original_length:
|
|
52
|
+
output(":broom: Updating the .gitignore")
|
|
53
|
+
if not found:
|
|
54
|
+
lines.append("conf.py\n")
|
|
55
|
+
with open(".gitignore", "w") as out_f:
|
|
56
|
+
for line in lines:
|
|
57
|
+
out_f.write(line)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def cleanup_pyproject() -> None:
|
|
61
|
+
"""Cleanup the pyproject file."""
|
|
62
|
+
if not path.exists("pyproject.toml"):
|
|
63
|
+
raise NoConfigError()
|
|
64
|
+
with open("pyproject.toml") as in_f:
|
|
65
|
+
pyproject = tomlkit.parse(in_f.read())
|
|
66
|
+
updated = False
|
|
67
|
+
if "tool" not in pyproject:
|
|
68
|
+
pyproject["tool"] = tomlkit.table()
|
|
69
|
+
if "hatch" not in pyproject["tool"]:
|
|
70
|
+
pyproject["tool"]["hatch"] = tomlkit.table()
|
|
71
|
+
if "envs" not in pyproject["tool"]["hatch"]:
|
|
72
|
+
pyproject["tool"]["hatch"]["envs"] = tomlkit.table()
|
|
73
|
+
if "default" not in pyproject["tool"]["hatch"]["envs"]:
|
|
74
|
+
pyproject["tool"]["hatch"]["envs"]["default"] = tomlkit.table()
|
|
75
|
+
if "scripts" not in pyproject["tool"]["hatch"]["envs"]["default"]:
|
|
76
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["scripts"] = tomlkit.table()
|
|
77
|
+
updated = True
|
|
78
|
+
if "add-language" in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
79
|
+
del pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["add-language"]
|
|
80
|
+
updated = True
|
|
81
|
+
if "update-language" in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
82
|
+
del pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["update-language"]
|
|
83
|
+
updated = True
|
|
84
|
+
if "migrate" not in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
85
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["migrate"] = "uEdition migrate {args}"
|
|
86
|
+
updated = True
|
|
87
|
+
if "language" not in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
88
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["language"] = "uEdition language {args}"
|
|
89
|
+
updated = True
|
|
90
|
+
if "init" not in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
91
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["init"] = "uEdition init {args}"
|
|
92
|
+
updated = True
|
|
93
|
+
if "edit" not in pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]:
|
|
94
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["scripts"]["edit"] = (
|
|
95
|
+
"uvicorn --port 8000 uedition_editor:app {args} {args}"
|
|
96
|
+
)
|
|
97
|
+
updated = True
|
|
98
|
+
if updated:
|
|
99
|
+
output(":broom: Updating the pyproject.toml")
|
|
100
|
+
with open("pyproject.toml", "w") as out_f:
|
|
101
|
+
tomlkit.dump(pyproject, out_f)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def migrate_ueditor() -> None:
|
|
105
|
+
"""Migrate the μEditor to the latest supported version."""
|
|
106
|
+
ueditor_version = "uedition_editor>=2.0.0b5,<2.1"
|
|
107
|
+
output(":hammer: Updating the μEditor")
|
|
108
|
+
with open("pyproject.toml") as in_f:
|
|
109
|
+
pyproject = tomlkit.parse(in_f.read())
|
|
110
|
+
found_ueditor = False
|
|
111
|
+
for idx, dep in enumerate(pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"]):
|
|
112
|
+
dep = dep.lower() # noqa:PLW2901
|
|
113
|
+
if (
|
|
114
|
+
dep == "uedition_editor"
|
|
115
|
+
or dep.startswith("uedition_editor=")
|
|
116
|
+
or dep.startswith("uedition_editor<")
|
|
117
|
+
or dep.startswith("uedition_editor>")
|
|
118
|
+
):
|
|
119
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"][idx] = ueditor_version
|
|
120
|
+
found_ueditor = True
|
|
121
|
+
if not found_ueditor:
|
|
122
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"].append(ueditor_version)
|
|
123
|
+
with open("pyproject.toml", "w") as out_f:
|
|
124
|
+
tomlkit.dump(pyproject, out_f)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@app.command()
|
|
128
|
+
def migrate() -> None:
|
|
129
|
+
"""Migrate the μEdition to the latest version."""
|
|
130
|
+
if not path.exists("uEdition.yml") and not path.exists("uEdition.yaml") and not path.exists("pyproject.toml"):
|
|
131
|
+
raise NoConfigError()
|
|
132
|
+
output(":hammer: Migrating the μEdition")
|
|
133
|
+
cleanup_old_files()
|
|
134
|
+
cleanup_gitignore()
|
|
135
|
+
cleanup_pyproject()
|
|
136
|
+
migrate_ueditor()
|
|
137
|
+
output(":checkered_flag: Migration complete")
|
uedition/cli/serve.py
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
"""Local server that automatically rebuilds on changes."""
|
|
5
|
+
|
|
5
6
|
from os import path
|
|
6
7
|
from typing import Callable
|
|
7
8
|
|
|
8
9
|
from livereload import Server
|
|
9
10
|
|
|
11
|
+
from uedition.cli.base import app
|
|
10
12
|
from uedition.cli.build import full_build, partial_build
|
|
11
13
|
from uedition.settings import NoConfigError, settings
|
|
12
14
|
|
|
@@ -27,8 +29,9 @@ def build_cmd(lang: dict, full: bool = True) -> Callable[[], None]: # noqa: FBT
|
|
|
27
29
|
return cmd
|
|
28
30
|
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
@app.command()
|
|
33
|
+
def serve() -> None:
|
|
34
|
+
"""Run the local μEdition writing server."""
|
|
32
35
|
if not path.exists("uEdition.yml") and not path.exists("uEdition.yaml"):
|
|
33
36
|
raise NoConfigError()
|
|
34
37
|
full_rebuilds = [build_cmd(lang, full=True) for lang in settings["languages"]]
|
uedition/cli/update.py
CHANGED
|
@@ -1,16 +1,84 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2023-present Mark Hall <mark.hall@work.room3b.eu>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
"""The μEdition
|
|
4
|
+
"""The μEdition update functionality."""
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
5
7
|
from os import path
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
import httpx
|
|
10
|
+
import tomlkit
|
|
11
|
+
from packaging.specifiers import SpecifierSet
|
|
12
|
+
from packaging.version import Version
|
|
13
|
+
from rich import print as output
|
|
8
14
|
|
|
15
|
+
from uedition.cli.base import app
|
|
9
16
|
from uedition.settings import NoConfigError
|
|
10
17
|
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
class UpdateVersionOptions(Enum):
|
|
20
|
+
"""Option settings for upgrade version selection."""
|
|
21
|
+
|
|
22
|
+
RELEASES = "releases"
|
|
23
|
+
PRE_RELEASES = "pre-releases"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command()
|
|
27
|
+
def update(allow_versions: UpdateVersionOptions = UpdateVersionOptions.RELEASES.value) -> None:
|
|
28
|
+
"""Update the μEdition to the latest version."""
|
|
29
|
+
try:
|
|
30
|
+
if not path.exists("uEdition.yml") and not path.exists("uEdition.yaml") and not path.exists("pyproject.toml"):
|
|
31
|
+
raise NoConfigError()
|
|
32
|
+
with open("pyproject.toml") as in_f:
|
|
33
|
+
pyproject = tomlkit.parse(in_f.read())
|
|
34
|
+
output(":hourglass_flowing_sand: Determining latest version")
|
|
35
|
+
response = httpx.get(
|
|
36
|
+
"https://pypi.org/simple/uedition/", headers={"Accept": "application/vnd.pypi.simple.v1+json"}
|
|
37
|
+
)
|
|
38
|
+
if response.status_code != 200: # noqa: PLR2004
|
|
39
|
+
output(":cross_mark: Failed to determine the latest version")
|
|
40
|
+
raise Exception("PyPi access error") # noqa: EM101
|
|
41
|
+
versions = response.json()["versions"]
|
|
42
|
+
versions.reverse()
|
|
43
|
+
target_version = None
|
|
44
|
+
for version in versions:
|
|
45
|
+
if Version(version).is_prerelease and allow_versions == UpdateVersionOptions.PRE_RELEASES:
|
|
46
|
+
target_version = Version(version)
|
|
47
|
+
break
|
|
48
|
+
elif not Version(version).is_prerelease:
|
|
49
|
+
target_version = Version(version)
|
|
50
|
+
break
|
|
51
|
+
if target_version is None:
|
|
52
|
+
raise Exception("No version found to upgrade to") # noqa: EM101
|
|
53
|
+
target_specifier = SpecifierSet(f">={target_version},<{target_version.major}.{target_version.minor + 1}")
|
|
54
|
+
output(f":hammer: Upgrading to {target_version}")
|
|
55
|
+
if "tool" not in pyproject:
|
|
56
|
+
pyproject["tool"] = tomlkit.table()
|
|
57
|
+
if "hatch" not in pyproject["tool"]:
|
|
58
|
+
pyproject["tool"]["hatch"] = tomlkit.table()
|
|
59
|
+
if "envs" not in pyproject["tool"]["hatch"]:
|
|
60
|
+
pyproject["tool"]["hatch"]["envs"] = tomlkit.table()
|
|
61
|
+
if "default" not in pyproject["tool"]["hatch"]["envs"]:
|
|
62
|
+
pyproject["tool"]["hatch"]["envs"]["default"] = tomlkit.table()
|
|
63
|
+
if "dependencies" not in pyproject["tool"]["hatch"]["envs"]["default"]:
|
|
64
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"] = []
|
|
65
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["skip-install"] = True
|
|
66
|
+
# Upgrade the uEdition version
|
|
67
|
+
found_uedition = False
|
|
68
|
+
for idx, dep in enumerate(pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"]):
|
|
69
|
+
dep = dep.lower() # noqa:PLW2901
|
|
70
|
+
if (
|
|
71
|
+
dep == "uedition"
|
|
72
|
+
or dep.startswith("uedition=")
|
|
73
|
+
or dep.startswith("uedition<")
|
|
74
|
+
or dep.startswith("uedition>")
|
|
75
|
+
):
|
|
76
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"][idx] = f"uedition{target_specifier}"
|
|
77
|
+
found_uedition = True
|
|
78
|
+
if not found_uedition:
|
|
79
|
+
pyproject["tool"]["hatch"]["envs"]["default"]["dependencies"].append(f"uedition{target_specifier}")
|
|
80
|
+
with open("pyproject.toml", "w") as out_f:
|
|
81
|
+
tomlkit.dump(pyproject, out_f)
|
|
82
|
+
output(":checkered_flag: Upgrade complete")
|
|
83
|
+
except Exception as e:
|
|
84
|
+
output(f":cross_mark: Update failed: {e}")
|
uedition/ext/tei/builder.py
CHANGED
|
@@ -14,6 +14,8 @@ from sphinx.locale import __
|
|
|
14
14
|
from sphinx.util.osutil import ensuredir, os_path
|
|
15
15
|
from sphinx.writers.xml import XMLWriter
|
|
16
16
|
|
|
17
|
+
from uedition.ext.tei.parser import TeiElement
|
|
18
|
+
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
19
21
|
MAPPINGS = [
|
|
@@ -37,6 +39,13 @@ MAPPINGS = [
|
|
|
37
39
|
},
|
|
38
40
|
{"cls": nodes.compound, "tagname": "div", "type": "block"},
|
|
39
41
|
{"cls": nodes.admonition, "tagname": "div", "type": "block"},
|
|
42
|
+
{
|
|
43
|
+
"cls": nodes.definition_list,
|
|
44
|
+
"tagname": "list",
|
|
45
|
+
"type": "block",
|
|
46
|
+
"attrs": [{"target": "type", "value": "definition"}],
|
|
47
|
+
},
|
|
48
|
+
{"cls": nodes.definition_list_item, "tagname": "item", "type": "block"},
|
|
40
49
|
{"cls": sphinx.addnodes.toctree},
|
|
41
50
|
{
|
|
42
51
|
"cls": nodes.footnote,
|
|
@@ -105,6 +114,16 @@ class TEITranslator(nodes.GenericNodeVisitor):
|
|
|
105
114
|
for rule in MAPPINGS:
|
|
106
115
|
if isinstance(node, rule["cls"]):
|
|
107
116
|
return rule
|
|
117
|
+
if isinstance(node, TeiElement):
|
|
118
|
+
tag = node.get("tei_tag")
|
|
119
|
+
tag = tag[tag.find("}") + 1 :]
|
|
120
|
+
attrs = []
|
|
121
|
+
for key, value in node.get("tei_attributes").items():
|
|
122
|
+
if key.startswith("data-tei-block-") or key.startswith("data-tei-mark-"):
|
|
123
|
+
continue
|
|
124
|
+
elif key.startswith("data-tei-attribute-"):
|
|
125
|
+
attrs.append({"target": key[19:], "value": value})
|
|
126
|
+
return {"tagname": tag, "type": "block", "attrs": attrs}
|
|
108
127
|
self.warn(f"Unknown node {node.__class__.__module__}.{node.__class__.__qualname__} ({node.attlist()})")
|
|
109
128
|
return {"tagname": "div", "type": "block"}
|
|
110
129
|
|
uedition/ext/tei/parser.py
CHANGED
|
@@ -104,10 +104,15 @@ class TEIParser(SphinxParser):
|
|
|
104
104
|
source.sort(key=self._sort_key(conf_section["sort"]))
|
|
105
105
|
doc_section.append(section)
|
|
106
106
|
for source in sources:
|
|
107
|
-
tmp =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
tmp = None
|
|
108
|
+
if "id" in source.attrib:
|
|
109
|
+
tmp = nodes.section(ids=[source.attrib["id"]])
|
|
110
|
+
elif "{http://www.w3.org/XML/1998/namespace}id" in source.attrib:
|
|
111
|
+
tmp = nodes.section(ids=[source.attrib["{http://www.w3.org/XML/1998/namespace}id"]])
|
|
112
|
+
if tmp is not None and len(source) > 0:
|
|
113
|
+
for child in source:
|
|
114
|
+
self._walk_tree(child, tmp)
|
|
115
|
+
section.append(tmp)
|
|
111
116
|
# self._wrap_sections(section, tmp)
|
|
112
117
|
elif conf_section["type"] == "metadata":
|
|
113
118
|
# Process a field or metadata section
|
|
@@ -169,8 +174,12 @@ class TEIParser(SphinxParser):
|
|
|
169
174
|
element = TeiElement(
|
|
170
175
|
html_tag=conf["tag"] if conf["tag"] else "span", tei_tag=node.tag, tei_attributes=attrs
|
|
171
176
|
)
|
|
172
|
-
if len(node) == 0
|
|
173
|
-
|
|
177
|
+
if len(node) == 0:
|
|
178
|
+
if conf["text"]:
|
|
179
|
+
for match in node.xpath(conf["text"], namespaces=namespaces):
|
|
180
|
+
element.append(nodes.Text(match))
|
|
181
|
+
elif node.text:
|
|
182
|
+
element.append(nodes.Text(node.text))
|
|
174
183
|
else:
|
|
175
184
|
for child in node:
|
|
176
185
|
self._walk_tree(child, element)
|
|
@@ -179,7 +188,7 @@ class TEIParser(SphinxParser):
|
|
|
179
188
|
if len(node) == 0:
|
|
180
189
|
parent.append(nodes.Text(node.text))
|
|
181
190
|
else:
|
|
182
|
-
logger.warning(f"No block or mark configured for {node.tag}")
|
|
191
|
+
logger.warning(f"No block or mark configured for {node.tag} ({node.attrib})")
|
|
183
192
|
|
|
184
193
|
def _parse_attributes(self, node: etree.Element, attribute_configs: list) -> dict:
|
|
185
194
|
attrs = {}
|
|
@@ -256,7 +265,7 @@ class TEIParser(SphinxParser):
|
|
|
256
265
|
|
|
257
266
|
def _parse_list_field(self: "TEIParser", parent: etree.Element, field: dict, root: etree.Element) -> None:
|
|
258
267
|
"""Parse a list of metadata fields."""
|
|
259
|
-
content = root.xpath(field["
|
|
268
|
+
content = root.xpath(field["selector"], namespaces=namespaces)
|
|
260
269
|
if len(content) > 0:
|
|
261
270
|
li = nodes.definition_list_item()
|
|
262
271
|
dt = nodes.term()
|
|
@@ -274,7 +283,7 @@ class TEIParser(SphinxParser):
|
|
|
274
283
|
|
|
275
284
|
def _parse_download_field(self: "TEIParser", parent: etree.Element, field: dict, root: etree.Element) -> None:
|
|
276
285
|
"""Parse a download metadata field."""
|
|
277
|
-
content = root.xpath(field["
|
|
286
|
+
content = root.xpath(field["selector"], namespaces=namespaces)
|
|
278
287
|
target = root.xpath(field["target"], namespaces=namespaces)
|
|
279
288
|
if len(content) > 0 and len(target) > 0:
|
|
280
289
|
if isinstance(content, list):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uEdition
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b1
|
|
4
4
|
Project-URL: Documentation, https://github.com/uEdition/uEdition#readme
|
|
5
5
|
Project-URL: Issues, https://github.com/uEdition/uEdition/issues
|
|
6
6
|
Project-URL: Source, https://github.com/uEdition/uEdition
|
|
@@ -16,16 +16,19 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
17
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: httpx<0.29,>=0.28.1
|
|
20
20
|
Requires-Dist: livereload
|
|
21
21
|
Requires-Dist: lxml<6.0.0,>=4.9.2
|
|
22
22
|
Requires-Dist: myst-parser<5,>=4.0.1
|
|
23
|
+
Requires-Dist: packaging==24.2
|
|
23
24
|
Requires-Dist: pydantic-settings<3.0.0,>=2.0.0
|
|
24
25
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
25
26
|
Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
26
27
|
Requires-Dist: sphinx-book-theme<2,>=1.1.4
|
|
27
28
|
Requires-Dist: sphinx-external-toc<2,>=1.0.1
|
|
28
29
|
Requires-Dist: sphinx<9,>=8.2.3
|
|
30
|
+
Requires-Dist: strictyaml<2,>=1.7.3
|
|
31
|
+
Requires-Dist: tomlkit<0.14,>=0.13.2
|
|
29
32
|
Requires-Dist: typer<1.0.0
|
|
30
33
|
Description-Content-Type: text/markdown
|
|
31
34
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
uedition/__about__.py,sha256=igeteuzPnSden9GXP0P5R-IDvMoaA-z1geZwMH_24Mw,160
|
|
2
|
+
uedition/__init__.py,sha256=xDDK2i5l1NLhxq6_LJH5g7UJDrFEjIJNmT2tcg7xNWI,301
|
|
3
|
+
uedition/__main__.py,sha256=Pg_yGV-ndR2iiImDC3f017llSX6pSYMslIwZlw8vEpQ,189
|
|
4
|
+
uedition/settings.py,sha256=G73r6muJmRzuENZ-2n51bBf88qS3tRv0kdxr2SRt1j4,5048
|
|
5
|
+
uedition/cli/__init__.py,sha256=3UpRbkx28iIvg0A3yLL-hHQS_TBkRzf8eoHW6ICSR8k,562
|
|
6
|
+
uedition/cli/base.py,sha256=wi8hQbNlC5u0xdagd4x9GEV8W8WhVbEk5WoC8cECniw,174
|
|
7
|
+
uedition/cli/build.py,sha256=aVKFLsZ0XZ2cexx4Bc2z75b4vNn207d8_m5W3IsMUfU,8555
|
|
8
|
+
uedition/cli/init.py,sha256=yD4l1ZhSlq_ftdrfv2wa0WPNwDnuP0lQ2WGR757jb7g,730
|
|
9
|
+
uedition/cli/language.py,sha256=EgjphwNH_2all60Tnc2FFMR8ZRV_Ox_hiD_h2r18Sd0,1988
|
|
10
|
+
uedition/cli/migrate.py,sha256=U4Whtat02Baa_fhlUaDcgBl6f8Jvp1GStyONxF5fXsI,5672
|
|
11
|
+
uedition/cli/serve.py,sha256=640qCCalJBXcLtdgep6e0xITaF5oa3_hbUrh--6mMY0,1577
|
|
12
|
+
uedition/cli/update.py,sha256=XRhv9Fdvcf_iKxZMW4Oz1_5h0OSbf32y18gKijFjOQk,3752
|
|
13
|
+
uedition/ext/__init__.py,sha256=hAK3MB5il4tAkfWnZNVxIJhfJ5vN0Fdmmtz0ZAYsvo4,406
|
|
14
|
+
uedition/ext/config.py,sha256=zu0XSH3Ca09n--umhUJ7k6611lVCecOTVZCWAFn4TRU,994
|
|
15
|
+
uedition/ext/language_switcher.css,sha256=y4LzkCgCm6E_nHt15I4Ku5QzBNpjwda9bt9FsqD1ybM,132
|
|
16
|
+
uedition/ext/language_switcher.js,sha256=HIgQiLg0WGS_G_VjpfEpTDLqb1HwHxcL3r6mdoSUix4,2697
|
|
17
|
+
uedition/ext/language_switcher.py,sha256=tHpf4HsvMtatVn5dQ3EFlrk5urFaMzsZZY755cvgCu8,1425
|
|
18
|
+
uedition/ext/settings.py,sha256=CCbvwlWjhikhoeRZ5E_SuA4zUIqDBMkRes_hjOUxjWk,3735
|
|
19
|
+
uedition/ext/tei/__init__.py,sha256=8KgVi31bz8nI65m6u4EdT_f1qNCP45HrU0V7MSGlZxA,1074
|
|
20
|
+
uedition/ext/tei/builder.py,sha256=XcMv_QTGM1zs86lbdQ2Maotl_7RPEvZ004XM9G7orBM,12539
|
|
21
|
+
uedition/ext/tei/parser.py,sha256=b-1jYRd_9994BzVglY_zadFmQkTJHcE2qbp_wa2hnNA,13813
|
|
22
|
+
uedition/ext/tei/tei_download.js,sha256=5_IPCuamZGPXWriPPPz5wA-zo0Y0Oy1858S6ltxSdQ8,2151
|
|
23
|
+
uedition-2.0.0b1.dist-info/METADATA,sha256=PVpjm2v-vfhhy6OtLYd218p7lQXF8yIG8Kgkio7mdAM,2674
|
|
24
|
+
uedition-2.0.0b1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
25
|
+
uedition-2.0.0b1.dist-info/entry_points.txt,sha256=cDOOVBb1SZ072ZkY1hW4Y7I_WKKGCtCJtDY1XST1Hr4,96
|
|
26
|
+
uedition-2.0.0b1.dist-info/licenses/LICENSE.txt,sha256=MhLJl8GE8mnuO5i_pvKaobpIGnhiAEdkY-a6LKiuwCE,1101
|
|
27
|
+
uedition-2.0.0b1.dist-info/RECORD,,
|
uedition/cli/create.py
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023-present Mark Hall <mark.hall@work.room3b.eu>
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: MIT
|
|
4
|
-
"""The μEdition create functionality for creating a new μEdition from the project template."""
|
|
5
|
-
from copier import run_copy
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def run(path: str) -> None:
|
|
9
|
-
"""Create a new μEdition using Copier."""
|
|
10
|
-
run_copy("gh:uEdition/uEdition-project-template", path)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
uedition/__about__.py,sha256=kq7WGmRyG7G8lDeopl9H4oZ4X9xtex9Qs3BryTuumU0,160
|
|
2
|
-
uedition/__init__.py,sha256=xDDK2i5l1NLhxq6_LJH5g7UJDrFEjIJNmT2tcg7xNWI,301
|
|
3
|
-
uedition/__main__.py,sha256=Pg_yGV-ndR2iiImDC3f017llSX6pSYMslIwZlw8vEpQ,189
|
|
4
|
-
uedition/settings.py,sha256=G73r6muJmRzuENZ-2n51bBf88qS3tRv0kdxr2SRt1j4,5048
|
|
5
|
-
uedition/cli/__init__.py,sha256=gnpl_N8uaw-4uF5ByWV3jXveJBvjLb_uay5YkYCUQWw,1478
|
|
6
|
-
uedition/cli/build.py,sha256=L_-OsIEFBXQh-aTHLwLhOXJzXqRH61X2SgVIPxmUgQ0,8169
|
|
7
|
-
uedition/cli/create.py,sha256=Q-SvDq9VtmUP4DQhuuvt1eZ_72sX8_tcFOj2Bt_T6J8,371
|
|
8
|
-
uedition/cli/language.py,sha256=JAyUwNa4gwqMvrJDPPKGkMLm5Cx9sHJkU5r6xTAMb7M,2214
|
|
9
|
-
uedition/cli/serve.py,sha256=FcKp0IbjcyCgn1XjU8VdhI59pGRMNCSXa5jbAS23nxs,1513
|
|
10
|
-
uedition/cli/update.py,sha256=SmAcczHxj3j3X0QYplvXPoIHS8XEfXYVqgXBney0v9c,550
|
|
11
|
-
uedition/ext/__init__.py,sha256=hAK3MB5il4tAkfWnZNVxIJhfJ5vN0Fdmmtz0ZAYsvo4,406
|
|
12
|
-
uedition/ext/config.py,sha256=zu0XSH3Ca09n--umhUJ7k6611lVCecOTVZCWAFn4TRU,994
|
|
13
|
-
uedition/ext/language_switcher.css,sha256=y4LzkCgCm6E_nHt15I4Ku5QzBNpjwda9bt9FsqD1ybM,132
|
|
14
|
-
uedition/ext/language_switcher.js,sha256=HIgQiLg0WGS_G_VjpfEpTDLqb1HwHxcL3r6mdoSUix4,2697
|
|
15
|
-
uedition/ext/language_switcher.py,sha256=tHpf4HsvMtatVn5dQ3EFlrk5urFaMzsZZY755cvgCu8,1425
|
|
16
|
-
uedition/ext/settings.py,sha256=CCbvwlWjhikhoeRZ5E_SuA4zUIqDBMkRes_hjOUxjWk,3735
|
|
17
|
-
uedition/ext/tei/__init__.py,sha256=8KgVi31bz8nI65m6u4EdT_f1qNCP45HrU0V7MSGlZxA,1074
|
|
18
|
-
uedition/ext/tei/builder.py,sha256=LkLR3cu1M6lHHdcjhH5esZ9Qn6eFBXCZWkxVxdXv69E,11719
|
|
19
|
-
uedition/ext/tei/parser.py,sha256=ebeqEiji2RZ-Gfszf0Yaa-Lqww-EPqLmxXiMBq-gj90,13216
|
|
20
|
-
uedition/ext/tei/tei_download.js,sha256=5_IPCuamZGPXWriPPPz5wA-zo0Y0Oy1858S6ltxSdQ8,2151
|
|
21
|
-
uedition-2.0.0a1.dist-info/METADATA,sha256=YquOplbaOq4Z17fRpMEQMK2S1imGpd0HS3TWeCaWXEk,2572
|
|
22
|
-
uedition-2.0.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
-
uedition-2.0.0a1.dist-info/entry_points.txt,sha256=cDOOVBb1SZ072ZkY1hW4Y7I_WKKGCtCJtDY1XST1Hr4,96
|
|
24
|
-
uedition-2.0.0a1.dist-info/licenses/LICENSE.txt,sha256=MhLJl8GE8mnuO5i_pvKaobpIGnhiAEdkY-a6LKiuwCE,1101
|
|
25
|
-
uedition-2.0.0a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|