plesty-sdk 0.0.2.dev2__py3-none-any.whl → 0.0.2.dev4__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.
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/METADATA +2 -1
- plesty_sdk-0.0.2.dev4.dist-info/RECORD +7 -0
- plesty_sdk-0.0.2.dev2.dist-info/RECORD +0 -20
- plestysdk/__init__.py +0 -1
- plestysdk/assets/docs-build-gitlab-ci.yml +0 -11
- plestysdk/assets/logos/plesty_icon.png +0 -0
- plestysdk/assets/logos/plesty_logo.png +0 -0
- plestysdk/assets/ruff.toml +0 -11
- plestysdk/cache.py +0 -106
- plestysdk/cli.py +0 -32
- plestysdk/commands/check.py +0 -146
- plestysdk/commands/docs/__init__.py +0 -35
- plestysdk/commands/docs/deploy.py +0 -193
- plestysdk/commands/docs/serve.py +0 -80
- plestysdk/commands/docs/sphinx_builder.py +0 -158
- plestysdk/context.py +0 -116
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/WHEEL +0 -0
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/entry_points.txt +0 -0
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/licenses/COPYING +0 -0
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/licenses/COPYING.LESSER +0 -0
- {plesty_sdk-0.0.2.dev2.dist-info → plesty_sdk-0.0.2.dev4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plesty-sdk
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.dev4
|
|
4
4
|
Summary: Command-line application for developing Python modules compliant with the Plesty standard.
|
|
5
5
|
Author: Plesty Development Team
|
|
6
6
|
License-File: COPYING
|
|
@@ -55,5 +55,6 @@ The suggested levels of plesty standards are:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
## LICENSE
|
|
58
|
+
|
|
58
59
|
Plesty is licensed under the GNU Lesser General Public License v3.0 or later
|
|
59
60
|
(LGPL-3.0-or-later). See the LICENSE file.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
plesty_sdk-0.0.2.dev4.dist-info/METADATA,sha256=8fy0ilYqdc0rnS2KVAi0k5Xtuez9kz-3nWY-fpwsh8E,2002
|
|
2
|
+
plesty_sdk-0.0.2.dev4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
3
|
+
plesty_sdk-0.0.2.dev4.dist-info/entry_points.txt,sha256=fNrx7o8Sf1lQfZJM-_uPYcPWbJMO94RAYh7z-DnGB5o,45
|
|
4
|
+
plesty_sdk-0.0.2.dev4.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
5
|
+
plesty_sdk-0.0.2.dev4.dist-info/licenses/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
6
|
+
plesty_sdk-0.0.2.dev4.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
7
|
+
plesty_sdk-0.0.2.dev4.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
plestysdk/__init__.py,sha256=m6UekKftTahNJ3W5K3mZSz4Y4ZZpHRxF_ZAxuaKYL7o,12
|
|
2
|
-
plestysdk/cache.py,sha256=EvqvGCj00oQEzoKij7LyfIX0Her_U9nrezChkZ-u7EQ,3878
|
|
3
|
-
plestysdk/cli.py,sha256=6IY-PvABpDxdl4f79QcvxdvzgErQByNv8acYlaj87ts,1123
|
|
4
|
-
plestysdk/context.py,sha256=4OR7gXMMTG9mbs_yvEj365du23VtuVqbgDQmA_85ENA,5017
|
|
5
|
-
plestysdk/assets/docs-build-gitlab-ci.yml,sha256=Ee-Iu1FymeGc7H93tVdBA-y5vJYNfCur4GVfEYNYd5s,185
|
|
6
|
-
plestysdk/assets/ruff.toml,sha256=aluT6IyKKC66_Z8X4OCQ9ujE1-Cf_mrnVCWCoHMZFMM,222
|
|
7
|
-
plestysdk/assets/logos/plesty_icon.png,sha256=yFrk2tQDGUzTTzRLdsbqUYwhe2YYa7VlXXfzZCUlIrk,18048
|
|
8
|
-
plestysdk/assets/logos/plesty_logo.png,sha256=QgqgLl3s8s-NmrtjTHG650uCzJwblwHwI62nc5IG1bg,15218
|
|
9
|
-
plestysdk/commands/check.py,sha256=xf_mVnece_KaGpmkJ_6oFXk9cdLPhayguSq7Ijp8zwk,5863
|
|
10
|
-
plestysdk/commands/docs/__init__.py,sha256=R7SMCSGhQrm8UM5GqscqdhdjTOmYoP-Zt3MlP7pctkY,1380
|
|
11
|
-
plestysdk/commands/docs/deploy.py,sha256=MJX97wKJKRJEn8_Ovj74Sr9VIirvZxfkyB5cuMjoB5c,8020
|
|
12
|
-
plestysdk/commands/docs/serve.py,sha256=U_6CUtmZFoK3joWBD4maLyX-pDEn4o9ont3ae7SW8ds,2796
|
|
13
|
-
plestysdk/commands/docs/sphinx_builder.py,sha256=DK1w8LAS36V5iKwz8JhSR15qGoecffF99djnutejofo,5392
|
|
14
|
-
plesty_sdk-0.0.2.dev2.dist-info/METADATA,sha256=ocYElNE8o7bPko2QWYHRpK_Ux7GD_8k5Mm7oCNblWUM,2001
|
|
15
|
-
plesty_sdk-0.0.2.dev2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
16
|
-
plesty_sdk-0.0.2.dev2.dist-info/entry_points.txt,sha256=fNrx7o8Sf1lQfZJM-_uPYcPWbJMO94RAYh7z-DnGB5o,45
|
|
17
|
-
plesty_sdk-0.0.2.dev2.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
18
|
-
plesty_sdk-0.0.2.dev2.dist-info/licenses/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
19
|
-
plesty_sdk-0.0.2.dev2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
20
|
-
plesty_sdk-0.0.2.dev2.dist-info/RECORD,,
|
plestysdk/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Init."""
|
|
Binary file
|
|
Binary file
|
plestysdk/assets/ruff.toml
DELETED
plestysdk/cache.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"""Cache management."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
from __future__ import annotations
|
|
21
|
-
import os
|
|
22
|
-
import shutil
|
|
23
|
-
from collections.abc import Callable
|
|
24
|
-
from importlib import metadata
|
|
25
|
-
from typing import ClassVar
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class CachedFile:
|
|
29
|
-
"""Function decorator for defining cached (text) files with content generated at runtime.
|
|
30
|
-
This class also serves as a registry of files to be cached.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
registry: ClassVar[list[CachedFile]] = []
|
|
34
|
-
"""Registry of instanciated CachedFile objects."""
|
|
35
|
-
filename: str
|
|
36
|
-
"""The path to the file to be cached, relative to the '.plesty' cache directory."""
|
|
37
|
-
content: Callable[[], str]
|
|
38
|
-
"""The decorated function that generates the content of the cached file."""
|
|
39
|
-
|
|
40
|
-
def __init__(self, filename: str) -> None:
|
|
41
|
-
"""Add the File object to the registry.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
filename: The path to the file to be cached, relative to the '.plesty'
|
|
45
|
-
cache directory.
|
|
46
|
-
"""
|
|
47
|
-
self.filename = filename
|
|
48
|
-
CachedFile.registry.append(self)
|
|
49
|
-
|
|
50
|
-
def __call__(self, content: Callable[[], str]) -> None:
|
|
51
|
-
"""Apply the decorator to a function."""
|
|
52
|
-
self.content = content
|
|
53
|
-
|
|
54
|
-
def generate(self) -> None:
|
|
55
|
-
"""Write the content of the cached file to disk."""
|
|
56
|
-
complete_file_path = os.path.join(os.getcwd(), '.plesty', self.filename)
|
|
57
|
-
os.makedirs(os.path.dirname(complete_file_path), exist_ok=True)
|
|
58
|
-
with open(complete_file_path, 'xt') as file:
|
|
59
|
-
file.write(self.content())
|
|
60
|
-
|
|
61
|
-
def is_correct(self) -> bool:
|
|
62
|
-
"""Check whether the cached file is up-to-date by comparing the generated content
|
|
63
|
-
with the content of the existing file on disk.
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
False, if the file does not exist on disk or its content is not up-to-date.
|
|
67
|
-
True otherwise.
|
|
68
|
-
"""
|
|
69
|
-
complete_file_path = os.path.join(os.getcwd(), '.plesty', self.filename)
|
|
70
|
-
if not os.path.exists(complete_file_path):
|
|
71
|
-
return False
|
|
72
|
-
with open(complete_file_path, 'rt') as file:
|
|
73
|
-
existing_content = file.read()
|
|
74
|
-
if not existing_content == self.content():
|
|
75
|
-
return False
|
|
76
|
-
return True
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def ensure_cache() -> None:
|
|
80
|
-
"""Ensure that the cache directory '.plesty' exists and that all files are
|
|
81
|
-
up-to-date. If not, (re)create the cache.
|
|
82
|
-
"""
|
|
83
|
-
for file in CachedFile.registry:
|
|
84
|
-
if not file.is_correct():
|
|
85
|
-
break
|
|
86
|
-
else:
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
cache_dir_path = os.path.join(os.getcwd(), '.plesty')
|
|
90
|
-
if os.path.exists(cache_dir_path):
|
|
91
|
-
shutil.rmtree(cache_dir_path)
|
|
92
|
-
os.makedirs(cache_dir_path)
|
|
93
|
-
for file in CachedFile.registry:
|
|
94
|
-
file.generate()
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@CachedFile('.gitignore')
|
|
98
|
-
def gitignore() -> str:
|
|
99
|
-
"""This file causes the entire '.plesty' cache directory to be ignored by version control."""
|
|
100
|
-
return '*'
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@CachedFile('.sdk-version')
|
|
104
|
-
def sdk_version() -> str:
|
|
105
|
-
"""This file specifies the version number of the Plesty SDK used when creating the cache."""
|
|
106
|
-
return metadata.version('plesty-sdk')
|
plestysdk/cli.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"""CLI tool."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import click
|
|
21
|
-
|
|
22
|
-
from plesty_sdk.commands.check import check_command
|
|
23
|
-
from plesty_sdk.commands.docs import docs_command
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@click.group("plesty")
|
|
27
|
-
def app():
|
|
28
|
-
"""The Plesty CLI tool."""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
app.add_command(check_command)
|
|
32
|
-
app.add_command(docs_command)
|
plestysdk/commands/check.py
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"""Check a module for plesty standard."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import asyncio
|
|
21
|
-
import importlib
|
|
22
|
-
import subprocess
|
|
23
|
-
from importlib import resources
|
|
24
|
-
from pathlib import Path
|
|
25
|
-
from typing import Any, Literal
|
|
26
|
-
|
|
27
|
-
import click
|
|
28
|
-
from bluesky.protocols import Readable
|
|
29
|
-
try:
|
|
30
|
-
from PySide6.QtWidgets import QWidget
|
|
31
|
-
PYSIDE6_IS_INSTALLED = True
|
|
32
|
-
except ImportError:
|
|
33
|
-
PYSIDE6_IS_INSTALLED = False
|
|
34
|
-
|
|
35
|
-
from plesty_sdk.context import ApplicationContext, pass_context
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@click.command(name="check", short_help="Check compliance with the Plesty standard.")
|
|
39
|
-
@click.option(
|
|
40
|
-
"--standard",
|
|
41
|
-
help="...",
|
|
42
|
-
default="pixel",
|
|
43
|
-
type=click.Choice(["pixel", "nebula", "quantum"]),
|
|
44
|
-
show_choices=True,
|
|
45
|
-
show_default=True,
|
|
46
|
-
)
|
|
47
|
-
@click.option("--device", help="...", is_flag=True)
|
|
48
|
-
@pass_context
|
|
49
|
-
def check_command(
|
|
50
|
-
context: ApplicationContext, standard: Literal["pixel", "nebula", "quantum"], device: bool
|
|
51
|
-
):
|
|
52
|
-
"""..."""
|
|
53
|
-
# ruff
|
|
54
|
-
run_linter()
|
|
55
|
-
|
|
56
|
-
check_gui(standard, context.pyproject_toml)
|
|
57
|
-
|
|
58
|
-
if device:
|
|
59
|
-
check_device(standard, context.pyproject_toml)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def check_gui(standard: Literal["pixel", "nebula", "quantum"], pyproject_toml: dict[str, Any]):
|
|
63
|
-
"""Checks whether the module's GUI class, if present, is compatible with the standard."""
|
|
64
|
-
click.echo("Checking GUI requirements...")
|
|
65
|
-
project_name: str = pyproject_toml["project"]["name"]
|
|
66
|
-
module = importlib.import_module(project_name.replace("-", "_"))
|
|
67
|
-
try:
|
|
68
|
-
gui_class = getattr(module, "GUI")
|
|
69
|
-
except AttributeError:
|
|
70
|
-
click.echo("Module does not implement a GUI.")
|
|
71
|
-
return
|
|
72
|
-
match standard:
|
|
73
|
-
case "pixel":
|
|
74
|
-
click.echo("Check passed, no GUI specific requirements in standard pixel.")
|
|
75
|
-
case "nebula" | "quantum":
|
|
76
|
-
if not PYSIDE6_IS_INSTALLED:
|
|
77
|
-
raise click.ClickException(
|
|
78
|
-
"PySide6 is not installed in the environment, but the GUI "
|
|
79
|
-
"class is required to inherit from PySide6.QtWidgets.QtWidget "
|
|
80
|
-
f"to be compatible with plesty standard {standard}."
|
|
81
|
-
)
|
|
82
|
-
if not issubclass(gui_class, QWidget):
|
|
83
|
-
raise click.ClickException(
|
|
84
|
-
"GUI class does not inherit from PySide6.QtWidgets.QtWidget "
|
|
85
|
-
f"required by plesty standard {standard}."
|
|
86
|
-
)
|
|
87
|
-
try:
|
|
88
|
-
if not callable(getattr(gui_class, "connect")):
|
|
89
|
-
raise AttributeError
|
|
90
|
-
except AttributeError:
|
|
91
|
-
raise click.ClickException(
|
|
92
|
-
"GUI class does not implement a connect method "
|
|
93
|
-
f"required by plesty standard {standard}."
|
|
94
|
-
)
|
|
95
|
-
click.echo(f"Check passed for standard {standard}.")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def check_device(standard: Literal["pixel", "nebula", "quantum"], pyproject_toml: dict[str, Any]):
|
|
99
|
-
"""Checks whether the device has a Bluesky compatible interface."""
|
|
100
|
-
click.echo("Checking device specific requirements...")
|
|
101
|
-
match standard:
|
|
102
|
-
case "pixel":
|
|
103
|
-
click.echo("Check passed, no device specific requirements in standard pixel.")
|
|
104
|
-
case "nebula" | "quantum":
|
|
105
|
-
device_class = pyproject_toml["tool"]["plesty"]["device-class"]
|
|
106
|
-
project_name = pyproject_toml["project"]["name"]
|
|
107
|
-
module = importlib.import_module(project_name.replace("-", "_"))
|
|
108
|
-
device_instance = getattr(module, device_class)()
|
|
109
|
-
# check for compatibility with bluesky Readable
|
|
110
|
-
if not isinstance(device_instance, Readable):
|
|
111
|
-
raise click.ClickException(
|
|
112
|
-
"Device class does not implement the 'Readable' protocol from Bluesky required"
|
|
113
|
-
f"by standard {standard}."
|
|
114
|
-
)
|
|
115
|
-
# check for async connect method
|
|
116
|
-
if not asyncio.iscoroutinefunction(getattr(device_instance, "connect")):
|
|
117
|
-
raise click.ClickException(
|
|
118
|
-
"Device class does not implement an asyncronous 'connect' functions requried"
|
|
119
|
-
f"by plesty standard {standard}."
|
|
120
|
-
)
|
|
121
|
-
click.echo(f"Check passed for standard {standard}.")
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def run_linter():
|
|
125
|
-
"""Run ruff linter.
|
|
126
|
-
|
|
127
|
-
Prints errors if checks are not fulfilled and exits.
|
|
128
|
-
Checks pyflakes, pycodestyle errors & warnings and pydocstyle.
|
|
129
|
-
Independent of standard level, as it needs to be fulfilled for every level.
|
|
130
|
-
"""
|
|
131
|
-
pkg_root = resources.files("plesty_sdk")
|
|
132
|
-
config = pkg_root.joinpath("assets/ruff.toml")
|
|
133
|
-
|
|
134
|
-
repo_root = str(Path.cwd())
|
|
135
|
-
with resources.as_file(config) as config_path:
|
|
136
|
-
commands = [
|
|
137
|
-
["ruff", "check", "--config", str(config_path), repo_root],
|
|
138
|
-
["ruff", "format", "--check", "--config", str(config_path), repo_root],
|
|
139
|
-
]
|
|
140
|
-
|
|
141
|
-
for cmd in commands:
|
|
142
|
-
p = subprocess.run(cmd, text=True, capture_output=True)
|
|
143
|
-
print(p.stdout)
|
|
144
|
-
print(p.stderr)
|
|
145
|
-
if p.returncode != 0:
|
|
146
|
-
raise SystemExit(p.returncode)
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"""CLI commands for previewing and deploying Sphinx documentation pages."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import click
|
|
21
|
-
|
|
22
|
-
from plesty_sdk.commands.docs.deploy import deploy_command
|
|
23
|
-
from plesty_sdk.commands.docs.serve import serve_command
|
|
24
|
-
from plesty_sdk.context import ApplicationContext, pass_context
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@click.group('docs')
|
|
28
|
-
@pass_context
|
|
29
|
-
def docs_command(context: ApplicationContext):
|
|
30
|
-
"""Preview and deploy documentation pages."""
|
|
31
|
-
context.ensure_docs_index()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
docs_command.add_command(deploy_command)
|
|
35
|
-
docs_command.add_command(serve_command)
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
"""The plesty docs deploy command."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import contextlib
|
|
21
|
-
import json
|
|
22
|
-
import os
|
|
23
|
-
import shutil
|
|
24
|
-
import subprocess
|
|
25
|
-
from importlib import metadata, resources
|
|
26
|
-
from packaging.version import InvalidVersion, Version
|
|
27
|
-
from tempfile import TemporaryDirectory
|
|
28
|
-
|
|
29
|
-
import click
|
|
30
|
-
|
|
31
|
-
from plesty_sdk.commands.docs.sphinx_builder import build_sphinx_app
|
|
32
|
-
from plesty_sdk.context import ApplicationContext, pass_context
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@click.command(
|
|
36
|
-
name='deploy', short_help="Build and push documentation pages to a remote branch."
|
|
37
|
-
)
|
|
38
|
-
@click.argument('remote_url', required=True)
|
|
39
|
-
@click.argument('target_branch', required=True)
|
|
40
|
-
@click.option(
|
|
41
|
-
"--release/--latest",
|
|
42
|
-
is_flag=True,
|
|
43
|
-
required=True,
|
|
44
|
-
help="Whether to deploy the documentation pages for a "
|
|
45
|
-
"release version or the latest development version.",
|
|
46
|
-
)
|
|
47
|
-
@pass_context
|
|
48
|
-
def deploy_command(
|
|
49
|
-
context: ApplicationContext, remote_url: str, target_branch: str, release: bool
|
|
50
|
-
):
|
|
51
|
-
"""Build and push the documentation pages for the Plesty project in the
|
|
52
|
-
current working directory to the <TARGET_BRANCH> of the repository at
|
|
53
|
-
<REMOTE_URL>. The branch is created if it does not yet exist.
|
|
54
|
-
"""
|
|
55
|
-
if release:
|
|
56
|
-
try:
|
|
57
|
-
version_str = metadata.version(context.pyproject_toml['project']['name'])
|
|
58
|
-
except metadata.PackageNotFoundError:
|
|
59
|
-
try:
|
|
60
|
-
version_str = context.pyproject_toml['project']['version']
|
|
61
|
-
except KeyError:
|
|
62
|
-
raise click.ClickException(
|
|
63
|
-
"The project version could not be retrieved either from the build "
|
|
64
|
-
"metadata - as the project is not installed in the current "
|
|
65
|
-
"environment - or directly from the pyproject.toml file."
|
|
66
|
-
)
|
|
67
|
-
try:
|
|
68
|
-
version = Version(version_str)
|
|
69
|
-
except InvalidVersion:
|
|
70
|
-
raise click.ClickException("The project version is not PEP 440 compliant.")
|
|
71
|
-
version_tag = f'{version.major}.{version.minor}'
|
|
72
|
-
build_dir = os.path.join('html', 'releases', version_tag)
|
|
73
|
-
else:
|
|
74
|
-
version_tag = None
|
|
75
|
-
build_dir = os.path.join('html', 'latest')
|
|
76
|
-
|
|
77
|
-
with TemporaryDirectory() as tmp_dir:
|
|
78
|
-
with contextlib.chdir(tmp_dir):
|
|
79
|
-
# check whether target branch exists on remote
|
|
80
|
-
if run_git(
|
|
81
|
-
['ls-remote', '--exit-code', '--heads', remote_url, target_branch],
|
|
82
|
-
expected_exit_codes={0, 2}
|
|
83
|
-
) == 0:
|
|
84
|
-
click.echo(f"Branch {target_branch} exists on remote. Cloning...")
|
|
85
|
-
run_git(['clone', '--branch', target_branch, '--depth', '1', remote_url, '.'])
|
|
86
|
-
else:
|
|
87
|
-
click.echo(
|
|
88
|
-
f"Branch {target_branch} does not yet exist "
|
|
89
|
-
"on remote. Creating orphan branch..."
|
|
90
|
-
)
|
|
91
|
-
run_git(['init'])
|
|
92
|
-
run_git(['checkout', '--orphan', target_branch])
|
|
93
|
-
with resources.as_file(
|
|
94
|
-
resources.files('plesty_sdk').joinpath('assets', 'docs-build-gitlab-ci.yml')
|
|
95
|
-
) as gitlab_ci_yml:
|
|
96
|
-
shutil.copyfile(gitlab_ci_yml, os.path.join(tmp_dir, '.gitlab-ci.yml'))
|
|
97
|
-
run_git(['add', '.gitlab-ci.yml'])
|
|
98
|
-
run_git(['commit', '--message', 'Initialize documentation branch.'])
|
|
99
|
-
|
|
100
|
-
click.echo("Building documentation...")
|
|
101
|
-
build_sphinx_app(
|
|
102
|
-
context.docs_settings,
|
|
103
|
-
version_tag,
|
|
104
|
-
build_dir=os.path.join(tmp_dir, build_dir),
|
|
105
|
-
delete_artifacts=True,
|
|
106
|
-
)
|
|
107
|
-
if version_tag is not None:
|
|
108
|
-
update_versions_json(os.path.join(tmp_dir, 'html', 'releases'), version_tag)
|
|
109
|
-
|
|
110
|
-
with contextlib.chdir(tmp_dir):
|
|
111
|
-
click.echo("Analyzing changes...")
|
|
112
|
-
run_git(['add', '--all'])
|
|
113
|
-
if run_git(['diff', '--staged', '--quiet'], expected_exit_codes={0, 1}) == 0:
|
|
114
|
-
click.echo("No changes made to the documentation pages.")
|
|
115
|
-
else:
|
|
116
|
-
commit_message = (
|
|
117
|
-
f"Add or update documentation pages for version {version_tag}."
|
|
118
|
-
) if version_tag is not None else "Update latest documentation pages."
|
|
119
|
-
run_git(['commit', '--message', commit_message])
|
|
120
|
-
run_git(['push', remote_url, target_branch])
|
|
121
|
-
click.echo("Changes to the documentation pages successfully pushed.")
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def run_git(args: list[str], expected_exit_codes: set[int] = {0}) -> int:
|
|
125
|
-
"""Run a git command in the current working directory.
|
|
126
|
-
|
|
127
|
-
Args:
|
|
128
|
-
args: The arguments and flags to be passed to the git command.
|
|
129
|
-
expected_exit_codes: Exit codes that should not raise an exception.
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
The exit code of the git command.
|
|
133
|
-
|
|
134
|
-
Raises:
|
|
135
|
-
click.ClickException: If the exit code is not in `expected_exit_codes`.
|
|
136
|
-
"""
|
|
137
|
-
if shutil.which('git') is None:
|
|
138
|
-
raise click.ClickException("Git is not installed or not found in PATH.")
|
|
139
|
-
result = subprocess.run((command := ['git'] + args), capture_output=True)
|
|
140
|
-
if result.returncode in expected_exit_codes:
|
|
141
|
-
return result.returncode
|
|
142
|
-
else:
|
|
143
|
-
error_message = result.stderr.decode('utf-8', errors='replace')
|
|
144
|
-
raise click.ClickException(f"Failed to run {' '.join(command)}.\n{error_message}")
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def version_tuple(version_tag: str) -> tuple[int, int]:
|
|
148
|
-
"""Parse a version tag into a tuple of the form (major, minor).
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
version_tag: The version tag to be parsed in 'major.minor' format.
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
Version tuple of the form (major, minor) as integers.
|
|
155
|
-
"""
|
|
156
|
-
major, minor = version_tag.split('.')
|
|
157
|
-
return (int(major), int(minor))
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def update_versions_json(releases_dir: str, new_version_tag: str) -> None:
|
|
161
|
-
"""Update the versions.json file to include the newly built version in the correct order
|
|
162
|
-
without duplicates. If necessary, update the automatic redirection to the newest version.
|
|
163
|
-
|
|
164
|
-
Args:
|
|
165
|
-
releases_dir: The directory containing the versions.json file.
|
|
166
|
-
new_version_tag: The version tag to be added.
|
|
167
|
-
"""
|
|
168
|
-
versions_json_path = os.path.join(releases_dir, 'versions.json')
|
|
169
|
-
try:
|
|
170
|
-
with open(versions_json_path, 'rt', encoding='utf-8') as file:
|
|
171
|
-
version_list: list[dict[str, str]] = json.load(file)
|
|
172
|
-
except FileNotFoundError:
|
|
173
|
-
version_list = []
|
|
174
|
-
|
|
175
|
-
new_version = version_tuple(new_version_tag)
|
|
176
|
-
new_version_entry = {'version': new_version_tag, 'url': f'/{new_version_tag}/'}
|
|
177
|
-
i = 0
|
|
178
|
-
while True:
|
|
179
|
-
if i == len(version_list):
|
|
180
|
-
version_list.append(new_version_entry)
|
|
181
|
-
break
|
|
182
|
-
old_version = version_tuple(version_list[i]['version'])
|
|
183
|
-
if new_version == old_version:
|
|
184
|
-
break
|
|
185
|
-
if new_version > old_version:
|
|
186
|
-
version_list.insert(i, new_version_entry)
|
|
187
|
-
break
|
|
188
|
-
i += 1
|
|
189
|
-
|
|
190
|
-
with open(versions_json_path, 'wt', encoding='utf-8') as file:
|
|
191
|
-
json.dump(version_list, file, indent=4)
|
|
192
|
-
with open(os.path.join(releases_dir, 'index.html'), 'wt') as file:
|
|
193
|
-
file.write(f'<script>window.location.replace("{version_list[0]['url']}")</script>')
|
plestysdk/commands/docs/serve.py
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"""The plesty docs serve command."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import logging
|
|
21
|
-
import os
|
|
22
|
-
from threading import Thread
|
|
23
|
-
|
|
24
|
-
import click
|
|
25
|
-
import livereload
|
|
26
|
-
import watchfiles
|
|
27
|
-
|
|
28
|
-
from plesty_sdk.cache import ensure_cache
|
|
29
|
-
from plesty_sdk.commands.docs.sphinx_builder import build_sphinx_app
|
|
30
|
-
from plesty_sdk.context import ApplicationContext, pass_context
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
logging.getLogger('livereload').disabled = True
|
|
34
|
-
for name in ['tornado.access', 'tornado.application', 'tornado.general']:
|
|
35
|
-
logging.getLogger(name).setLevel(logging.CRITICAL)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@click.command(
|
|
39
|
-
name='serve', short_help="Preview the documentation pages using a local webserver."
|
|
40
|
-
)
|
|
41
|
-
@click.option(
|
|
42
|
-
'--dev-addr',
|
|
43
|
-
default='localhost:8000',
|
|
44
|
-
help="IP address and port",
|
|
45
|
-
show_default=True,
|
|
46
|
-
metavar="<IP:PORT>",
|
|
47
|
-
)
|
|
48
|
-
@pass_context
|
|
49
|
-
def serve_command(context: ApplicationContext, dev_addr: str):
|
|
50
|
-
"""Start a local development webserver to preview the documentation pages
|
|
51
|
-
for the Plesty project in the current working directory. Changes to the
|
|
52
|
-
.md or .rst files are applied immediately via live reload.
|
|
53
|
-
"""
|
|
54
|
-
ensure_cache()
|
|
55
|
-
sphinx_app = build_sphinx_app(
|
|
56
|
-
context.docs_settings,
|
|
57
|
-
version_tag=None,
|
|
58
|
-
build_dir=os.path.join('.plesty', 'docs-build'),
|
|
59
|
-
delete_artifacts=False,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
def watch_changes():
|
|
63
|
-
for _ in watchfiles.watch(sphinx_app.srcdir):
|
|
64
|
-
sphinx_app.build()
|
|
65
|
-
|
|
66
|
-
watcher_thread = Thread(target=watch_changes, daemon=True)
|
|
67
|
-
watcher_thread.start()
|
|
68
|
-
|
|
69
|
-
server = livereload.Server()
|
|
70
|
-
dev_host, dev_port = dev_addr.split(':')
|
|
71
|
-
click.echo(f"Serving preview at http://{dev_host}:{dev_port}.")
|
|
72
|
-
click.echo(
|
|
73
|
-
f"Changes in ./{os.path.relpath(sphinx_app.srcdir)} "
|
|
74
|
-
"are detected and immediately visible."
|
|
75
|
-
)
|
|
76
|
-
click.echo("Press Ctrl+C to stop the server.")
|
|
77
|
-
server.serve(
|
|
78
|
-
host=dev_host, port=int(dev_port), root=os.path.join('.plesty', 'docs-build')
|
|
79
|
-
)
|
|
80
|
-
click.echo("Server shut down.")
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"""Sphinx app configuration and build process."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import os
|
|
21
|
-
import shutil
|
|
22
|
-
from datetime import date
|
|
23
|
-
from importlib import resources
|
|
24
|
-
from typing import Any
|
|
25
|
-
|
|
26
|
-
from sphinx.application import Sphinx
|
|
27
|
-
|
|
28
|
-
from plesty_sdk.context import DocumentationSettings
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def make_sphinx_config(
|
|
32
|
-
settings: DocumentationSettings, version_tag: str | None, logo_dir: os.PathLike[str]
|
|
33
|
-
) -> dict[str, Any]:
|
|
34
|
-
"""Set up the Sphinx configuration dictionary customized for the project.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
settings: The documentation settings.
|
|
38
|
-
version_tag: The version for which the documentation pages are built.
|
|
39
|
-
logo_dir: The directory containing the logo files.
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
The Sphinx configuration dictionary.
|
|
43
|
-
"""
|
|
44
|
-
config = dict[str, Any](
|
|
45
|
-
project = settings.site_name,
|
|
46
|
-
copyright = f"{date.today().year}, Plesty Development Team",
|
|
47
|
-
extensions = [
|
|
48
|
-
'myst_parser',
|
|
49
|
-
'sphinx.ext.autodoc',
|
|
50
|
-
'sphinx.ext.napoleon',
|
|
51
|
-
'sphinxcontrib.mermaid',
|
|
52
|
-
'sphinx_design',
|
|
53
|
-
'sphinx_copybutton',
|
|
54
|
-
],
|
|
55
|
-
source_suffix = {
|
|
56
|
-
'.rst': 'restructuredtext',
|
|
57
|
-
'.md': 'markdown',
|
|
58
|
-
},
|
|
59
|
-
# HTML theme
|
|
60
|
-
html_theme = 'pydata_sphinx_theme',
|
|
61
|
-
html_theme_options = {
|
|
62
|
-
'navbar_center': ['navbar-nav'],
|
|
63
|
-
'secondary_sidebar_items': ['page-toc', 'sourcelink'],
|
|
64
|
-
'footer_start': ['copyright'],
|
|
65
|
-
'footer_center': [],
|
|
66
|
-
'footer_end': ['theme-version'],
|
|
67
|
-
'logo': {
|
|
68
|
-
'text': settings.site_name,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
html_sidebars = {
|
|
72
|
-
'*': [],
|
|
73
|
-
},
|
|
74
|
-
html_logo = os.path.join(logo_dir, 'plesty_logo.png'),
|
|
75
|
-
html_favicon = os.path.join(logo_dir, 'plesty_icon.png'),
|
|
76
|
-
# MyST configuration
|
|
77
|
-
myst_enable_extensions = ['colon_fence'],
|
|
78
|
-
myst_fence_as_directive = ['mermaid'],
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if version_tag is not None:
|
|
82
|
-
config['html_theme_options']['navbar_center'].insert(0, 'version-switcher')
|
|
83
|
-
config['html_theme_options']['switcher'] = {
|
|
84
|
-
'json_url': '/versions.json',
|
|
85
|
-
'version_match': version_tag,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if settings.api_reference:
|
|
89
|
-
project_root = os.getcwd()
|
|
90
|
-
if not os.path.isdir(src_dir := os.path.join(project_root, 'src')):
|
|
91
|
-
src_dir = project_root
|
|
92
|
-
|
|
93
|
-
import_packages = [
|
|
94
|
-
entry.path for entry in os.scandir(src_dir) if (
|
|
95
|
-
entry.is_dir()
|
|
96
|
-
and entry.name != "tests"
|
|
97
|
-
and os.path.isfile(os.path.join(entry.path, '__init__.py'))
|
|
98
|
-
)
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
if import_packages:
|
|
102
|
-
config['extensions'].append('autoapi.extension')
|
|
103
|
-
config.update(
|
|
104
|
-
autoapi_generate_api_docs = True,
|
|
105
|
-
autoapi_add_toctree_entry = True,
|
|
106
|
-
autoapi_root = 'reference',
|
|
107
|
-
autoapi_dirs = import_packages,
|
|
108
|
-
autoapi_python_class_content = 'both',
|
|
109
|
-
napoleon_google_docstring = True,
|
|
110
|
-
autodoc_typehints = 'both',
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
if settings.toc_file is not None:
|
|
114
|
-
config['extensions'].append('sphinx_external_toc')
|
|
115
|
-
config.update(
|
|
116
|
-
suppress_warnings = ["etoc.toctree"],
|
|
117
|
-
external_toc_path = settings.toc_file,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
return config
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def build_sphinx_app(
|
|
124
|
-
settings: DocumentationSettings,
|
|
125
|
-
version_tag: str | None,
|
|
126
|
-
build_dir: str,
|
|
127
|
-
delete_artifacts: bool,
|
|
128
|
-
) -> Sphinx:
|
|
129
|
-
"""Build the Sphinx application.
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
settings: The documentation settings.
|
|
133
|
-
version_tag: The version for which the documentation pages are built.
|
|
134
|
-
build_dir: The location where the documentation pages are built.
|
|
135
|
-
delete_artifacts: Whether to delete Sphinx build artifacts.
|
|
136
|
-
|
|
137
|
-
Returns:
|
|
138
|
-
The Sphinx application object.
|
|
139
|
-
"""
|
|
140
|
-
if os.path.isdir(build_dir):
|
|
141
|
-
shutil.rmtree(build_dir)
|
|
142
|
-
|
|
143
|
-
with resources.as_file(resources.files('plesty_sdk').joinpath('assets', 'logos')) as logo_dir:
|
|
144
|
-
app = Sphinx(
|
|
145
|
-
srcdir=settings.docs_dir,
|
|
146
|
-
confdir=None,
|
|
147
|
-
outdir=build_dir,
|
|
148
|
-
doctreedir=os.path.join(build_dir, '.doctrees'),
|
|
149
|
-
buildername='html',
|
|
150
|
-
confoverrides=make_sphinx_config(settings, version_tag, logo_dir),
|
|
151
|
-
)
|
|
152
|
-
app.build()
|
|
153
|
-
|
|
154
|
-
if delete_artifacts:
|
|
155
|
-
shutil.rmtree(os.path.join(build_dir, '.doctrees'))
|
|
156
|
-
os.remove(os.path.join(build_dir, '.buildinfo'))
|
|
157
|
-
|
|
158
|
-
return app
|
plestysdk/context.py
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"""Cross-command context management."""
|
|
2
|
-
|
|
3
|
-
# Copyright (C) 2025, 2026 Christopher Borchers, fvrehlinger, Maximilian
|
|
4
|
-
# Heller, Michael Zopf (names in alphabetic order wrt. surnames)
|
|
5
|
-
#
|
|
6
|
-
# This file is part of plesty-sdk which is part of the Plesty library.
|
|
7
|
-
#
|
|
8
|
-
# plesty-sdk is free software: you can redistribute it and/or modify it under
|
|
9
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
|
10
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
11
|
-
# later version.
|
|
12
|
-
#
|
|
13
|
-
# plesty-sdk is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
15
|
-
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
16
|
-
#
|
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
|
18
|
-
# with plesty-sdk. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
|
|
20
|
-
import os
|
|
21
|
-
import tomllib
|
|
22
|
-
from typing import Any
|
|
23
|
-
|
|
24
|
-
import click
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class DocumentationSettings:
|
|
28
|
-
"""Settings for generating the documentation pages,
|
|
29
|
-
which can be configured in the pyproject.toml file.
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
site_name: str
|
|
33
|
-
"""The title of the website present on each page next to the Plesty logo."""
|
|
34
|
-
docs_dir: str
|
|
35
|
-
"""The directory containing the .md or .rst files to generate the documentation from."""
|
|
36
|
-
api_reference: bool
|
|
37
|
-
"""Whether to automatically generate API reference pages."""
|
|
38
|
-
toc_file: str | None
|
|
39
|
-
"""The path to the external TOC file."""
|
|
40
|
-
|
|
41
|
-
def __init__(self, pyproject_toml: dict[str, Any]) -> None:
|
|
42
|
-
"""Read the documentation settings specified by the user in the [tool.plesty.docs]
|
|
43
|
-
section of the pyproject.toml file and set default values for missing settings.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
pyproject_toml: The parsed content of the pyproject.toml file.
|
|
47
|
-
"""
|
|
48
|
-
try:
|
|
49
|
-
plesty_docs_section: dict[str, Any] = pyproject_toml['tool']['plesty']['docs']
|
|
50
|
-
except KeyError:
|
|
51
|
-
plesty_docs_section = {}
|
|
52
|
-
|
|
53
|
-
self.site_name = plesty_docs_section.get('site-name', pyproject_toml['project']['name'])
|
|
54
|
-
self.docs_dir = os.path.join(os.getcwd(), plesty_docs_section.get('docs-dir', 'docs'))
|
|
55
|
-
self.api_reference = plesty_docs_section.get('api-reference', True)
|
|
56
|
-
|
|
57
|
-
if (toc_file := plesty_docs_section.get('toc-file', 'toc.yaml')) == '':
|
|
58
|
-
self.toc_file = None
|
|
59
|
-
else:
|
|
60
|
-
self.toc_file = os.path.join(self.docs_dir, toc_file)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class ApplicationContext:
|
|
64
|
-
"""Container class for storing data shared across multiple commands."""
|
|
65
|
-
|
|
66
|
-
pyproject_toml: dict[str, Any]
|
|
67
|
-
"""The parsed content of the pyproject.toml file in the current working directory."""
|
|
68
|
-
docs_settings: DocumentationSettings
|
|
69
|
-
"""The documentation settings."""
|
|
70
|
-
|
|
71
|
-
def __init__(self) -> None:
|
|
72
|
-
"""Parse the pyproject.toml file and read the documentation settings."""
|
|
73
|
-
pyproject_toml_path = os.path.join(os.getcwd(), 'pyproject.toml')
|
|
74
|
-
try:
|
|
75
|
-
with open(pyproject_toml_path, 'rb') as file:
|
|
76
|
-
self.pyproject_toml = tomllib.load(file)
|
|
77
|
-
except FileNotFoundError:
|
|
78
|
-
raise click.ClickException(
|
|
79
|
-
"No pyproject.toml file found in the current "
|
|
80
|
-
f"working directory at {pyproject_toml_path}."
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
self.docs_settings = DocumentationSettings(self.pyproject_toml)
|
|
84
|
-
|
|
85
|
-
def ensure_docs_index(self) -> None:
|
|
86
|
-
"""Ensure that the docs directory exists and contains an index.md or index.rst
|
|
87
|
-
file, as well as the external TOC file, if specified in the documentation settings.
|
|
88
|
-
|
|
89
|
-
Raises:
|
|
90
|
-
click.ClickException: If a required file is missing.
|
|
91
|
-
"""
|
|
92
|
-
if not os.path.isdir(self.docs_settings.docs_dir):
|
|
93
|
-
raise click.ClickException(
|
|
94
|
-
f"No docs directory found at {self.docs_settings.docs_dir}. Create one or ensure "
|
|
95
|
-
"that the correct docs directory is specified in the pyproject.toml file."
|
|
96
|
-
)
|
|
97
|
-
if not (
|
|
98
|
-
os.path.isfile(os.path.join(self.docs_settings.docs_dir, 'index.md'))
|
|
99
|
-
or os.path.isfile(os.path.join(self.docs_settings.docs_dir, 'index.rst'))
|
|
100
|
-
):
|
|
101
|
-
raise click.ClickException(
|
|
102
|
-
"No index.md or index.rst master file found in the docs "
|
|
103
|
-
"directory (entry point for the documentation generation)."
|
|
104
|
-
)
|
|
105
|
-
if not (
|
|
106
|
-
self.docs_settings.toc_file is None or os.path.isfile(self.docs_settings.toc_file)
|
|
107
|
-
):
|
|
108
|
-
raise click.ClickException(
|
|
109
|
-
f"No external TOC file found at {self.docs_settings.toc_file}. Create one or "
|
|
110
|
-
"ensure that the correct path is specified in the pyproject.toml file. If you "
|
|
111
|
-
"do not want to use an external table of contents, specify an empty string."
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
pass_context = click.make_pass_decorator(ApplicationContext, ensure=True)
|
|
116
|
-
"""Decorator to pass the application context to a Click command as the first argument."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|