venv-doc 0.1.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.
- venv_doc/__init__.py +28 -0
- venv_doc/__main__.py +32 -0
- venv_doc/_internal/__init__.py +17 -0
- venv_doc/_internal/cli.py +485 -0
- venv_doc/_internal/debug.py +125 -0
- venv_doc/_internal/sphinx_roles.py +105 -0
- venv_doc/py.typed +0 -0
- venv_doc-0.1.0.dist-info/METADATA +62 -0
- venv_doc-0.1.0.dist-info/RECORD +12 -0
- venv_doc-0.1.0.dist-info/WHEEL +4 -0
- venv_doc-0.1.0.dist-info/entry_points.txt +5 -0
- venv_doc-0.1.0.dist-info/licenses/LICENSE +15 -0
venv_doc/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
"""venv-doc package.
|
|
20
|
+
|
|
21
|
+
Like `cargo doc` for Python venvs.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from venv_doc._internal.cli import get_parser, main
|
|
27
|
+
|
|
28
|
+
__all__: list[str] = ["get_parser", "main"]
|
venv_doc/__main__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
"""Entry-point module, in case you use `python -m venv_doc`.
|
|
20
|
+
|
|
21
|
+
Why does this file exist, and why `__main__`? For more info, read:
|
|
22
|
+
|
|
23
|
+
- https://www.python.org/dev/peps/pep-0338/
|
|
24
|
+
- https://docs.python.org/3/using/cmdline.html#cmdoption-m
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
from venv_doc._internal.cli import main
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
# Why does this file exist, and why not put this in `__main__`?
|
|
20
|
+
#
|
|
21
|
+
# You might be tempted to import things from `__main__` later,
|
|
22
|
+
# but that will cause problems: the code will get executed twice:
|
|
23
|
+
#
|
|
24
|
+
# - When you run `python -m venv_doc` python will execute
|
|
25
|
+
# `__main__.py` as a script. That means there won't be any
|
|
26
|
+
# `venv_doc.__main__` in `sys.modules`.
|
|
27
|
+
# - When you import `__main__` it will get executed again (as a module) because
|
|
28
|
+
# there's no `venv_doc.__main__` in `sys.modules`.
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import json
|
|
34
|
+
import os
|
|
35
|
+
import sys
|
|
36
|
+
from dataclasses import asdict
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from subprocess import run
|
|
39
|
+
from tempfile import TemporaryDirectory
|
|
40
|
+
from textwrap import dedent
|
|
41
|
+
from time import perf_counter
|
|
42
|
+
from typing import Any, cast
|
|
43
|
+
|
|
44
|
+
from griffe import (
|
|
45
|
+
Alias,
|
|
46
|
+
AliasResolutionError,
|
|
47
|
+
CyclicAliasError,
|
|
48
|
+
GriffeLoader,
|
|
49
|
+
Module,
|
|
50
|
+
Object,
|
|
51
|
+
Parser,
|
|
52
|
+
load_extensions,
|
|
53
|
+
)
|
|
54
|
+
from mkdocstrings import CollectionError
|
|
55
|
+
from zensical import build, serve
|
|
56
|
+
from zensical.compat import mkdocstrings as zensical_mkdocstrings
|
|
57
|
+
from zensical.config import parse_config
|
|
58
|
+
|
|
59
|
+
from venv_doc._internal import debug
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class _DebugInfo(argparse.Action):
|
|
63
|
+
def __init__(self, nargs: int | str | None = 0, **kwargs: Any) -> None:
|
|
64
|
+
super().__init__(nargs=nargs, **kwargs)
|
|
65
|
+
|
|
66
|
+
def __call__(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002
|
|
67
|
+
debug._print_debug_info()
|
|
68
|
+
sys.exit(0)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_parser() -> argparse.ArgumentParser:
|
|
72
|
+
"""Return the CLI argument parser.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
An argparse parser.
|
|
76
|
+
"""
|
|
77
|
+
parser = argparse.ArgumentParser(prog="venv-doc")
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"-V",
|
|
80
|
+
"--version",
|
|
81
|
+
action="version",
|
|
82
|
+
version=f"%(prog)s {debug._get_version()}",
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--debug-info",
|
|
86
|
+
action=_DebugInfo,
|
|
87
|
+
help="Print debug information.",
|
|
88
|
+
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"--self",
|
|
91
|
+
action="store_true",
|
|
92
|
+
help="Document packages from the environment running venv-doc instead of .venv.",
|
|
93
|
+
)
|
|
94
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
95
|
+
|
|
96
|
+
build_parser = subparsers.add_parser("build", help="Build the documentation.")
|
|
97
|
+
build_parser.add_argument(
|
|
98
|
+
"-c",
|
|
99
|
+
"--clean",
|
|
100
|
+
action="store_true",
|
|
101
|
+
help="Clean cache.",
|
|
102
|
+
)
|
|
103
|
+
build_parser.add_argument(
|
|
104
|
+
"-s",
|
|
105
|
+
"--strict",
|
|
106
|
+
action="store_true",
|
|
107
|
+
help="Enable strict mode - abort the build on warnings.",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
serve_parser = subparsers.add_parser(
|
|
111
|
+
"serve",
|
|
112
|
+
help="Build and serve the documentation.",
|
|
113
|
+
)
|
|
114
|
+
serve_parser.add_argument(
|
|
115
|
+
"-a",
|
|
116
|
+
"--dev-addr",
|
|
117
|
+
metavar="<IP:PORT>",
|
|
118
|
+
help="IP address and port (default: localhost:8000).",
|
|
119
|
+
)
|
|
120
|
+
serve_parser.add_argument(
|
|
121
|
+
"-o",
|
|
122
|
+
"--open",
|
|
123
|
+
action="store_true",
|
|
124
|
+
help="Open preview in default browser.",
|
|
125
|
+
)
|
|
126
|
+
serve_parser.add_argument(
|
|
127
|
+
"-s",
|
|
128
|
+
"--strict",
|
|
129
|
+
action="store_true",
|
|
130
|
+
help="Strict mode (currently unsupported).",
|
|
131
|
+
)
|
|
132
|
+
return parser
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
_VENV_INFO_SCRIPT = """\
|
|
136
|
+
import json
|
|
137
|
+
from importlib.metadata import distributions
|
|
138
|
+
from importlib.util import find_spec
|
|
139
|
+
from inspect import getmodulename
|
|
140
|
+
from pathlib import Path
|
|
141
|
+
|
|
142
|
+
package_candidates = set()
|
|
143
|
+
for distribution in distributions():
|
|
144
|
+
top_level = distribution.read_text("top_level.txt")
|
|
145
|
+
if top_level:
|
|
146
|
+
package_candidates.update(top_level.split())
|
|
147
|
+
else:
|
|
148
|
+
for path in distribution.files or []:
|
|
149
|
+
name = path.parts[0]
|
|
150
|
+
package_candidates.add(getmodulename(name) or name)
|
|
151
|
+
|
|
152
|
+
package_names = []
|
|
153
|
+
package_paths = set()
|
|
154
|
+
for package in package_candidates:
|
|
155
|
+
if package.startswith("_") or not package.isidentifier():
|
|
156
|
+
continue
|
|
157
|
+
spec = find_spec(package)
|
|
158
|
+
if spec is None:
|
|
159
|
+
continue
|
|
160
|
+
package_names.append(package)
|
|
161
|
+
if spec.submodule_search_locations:
|
|
162
|
+
package_paths.update(str(Path(path).parent) for path in spec.submodule_search_locations)
|
|
163
|
+
elif spec.origin:
|
|
164
|
+
package_paths.add(str(Path(spec.origin).parent))
|
|
165
|
+
|
|
166
|
+
print(json.dumps({"packages": sorted(package_names), "paths": sorted(package_paths)}))
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _venv_python(venv_path: Path) -> Path:
|
|
171
|
+
"""Return the Python interpreter in a virtual environment."""
|
|
172
|
+
candidates = (
|
|
173
|
+
(venv_path / "Scripts" / "python.exe", venv_path / "Scripts" / "python")
|
|
174
|
+
if os.name == "nt"
|
|
175
|
+
else (venv_path / "bin" / "python",)
|
|
176
|
+
)
|
|
177
|
+
for candidate in candidates:
|
|
178
|
+
if candidate.is_file():
|
|
179
|
+
return candidate
|
|
180
|
+
msg = f"Could not find a Python interpreter in {venv_path}"
|
|
181
|
+
raise FileNotFoundError(msg)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _venv_packages(python: Path) -> tuple[list[str], list[str]]:
|
|
185
|
+
"""Return public packages and their import roots from a Python interpreter."""
|
|
186
|
+
result = run( # noqa: S603
|
|
187
|
+
[str(python), "-c", _VENV_INFO_SCRIPT],
|
|
188
|
+
capture_output=True,
|
|
189
|
+
check=True,
|
|
190
|
+
encoding="utf8",
|
|
191
|
+
)
|
|
192
|
+
info = json.loads(result.stdout)
|
|
193
|
+
return info["packages"], info["paths"]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _public_modules(module: Module) -> list[Module]:
|
|
197
|
+
"""Return the public modules below a module, in declaration order."""
|
|
198
|
+
modules = []
|
|
199
|
+
for member in module.members.values():
|
|
200
|
+
if member.is_alias:
|
|
201
|
+
continue
|
|
202
|
+
try:
|
|
203
|
+
if not member.is_module or not member.is_public:
|
|
204
|
+
continue
|
|
205
|
+
except AliasResolutionError:
|
|
206
|
+
continue
|
|
207
|
+
public_module = cast("Module", member)
|
|
208
|
+
modules.append(public_module)
|
|
209
|
+
modules.extend(_public_modules(public_module))
|
|
210
|
+
return modules
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _is_rendered_member(member: Object | Alias) -> bool:
|
|
214
|
+
"""Whether a member is rendered with the generated configuration."""
|
|
215
|
+
try:
|
|
216
|
+
return member.is_public
|
|
217
|
+
except (AliasResolutionError, CyclicAliasError):
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _preparse_docstrings(modules: list[Module]) -> int:
|
|
222
|
+
"""Pre-parse docstrings that will be rendered on module pages."""
|
|
223
|
+
objects: list[Object | Alias] = list(modules)
|
|
224
|
+
seen_objects: set[int] = set()
|
|
225
|
+
seen_docstrings: set[int] = set()
|
|
226
|
+
parsed = 0
|
|
227
|
+
|
|
228
|
+
while objects:
|
|
229
|
+
obj = objects.pop()
|
|
230
|
+
try:
|
|
231
|
+
target = obj.final_target if isinstance(obj, Alias) else obj
|
|
232
|
+
except (AliasResolutionError, CyclicAliasError):
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
object_id = id(target)
|
|
236
|
+
if object_id in seen_objects:
|
|
237
|
+
continue
|
|
238
|
+
seen_objects.add(object_id)
|
|
239
|
+
|
|
240
|
+
if target.docstring is not None:
|
|
241
|
+
docstring_id = id(target.docstring)
|
|
242
|
+
if docstring_id not in seen_docstrings:
|
|
243
|
+
_ = target.docstring.parsed
|
|
244
|
+
seen_docstrings.add(docstring_id)
|
|
245
|
+
parsed += 1
|
|
246
|
+
|
|
247
|
+
if target.is_module or target.is_class:
|
|
248
|
+
objects.extend(member for member in obj.all_members.values() if _is_rendered_member(member))
|
|
249
|
+
|
|
250
|
+
return parsed
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _noop_mkdocstrings_reset() -> None:
|
|
254
|
+
"""Keep the pre-loaded mkdocstrings handlers alive."""
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _write_page(path: Path, identifier: str) -> None:
|
|
258
|
+
"""Write an API documentation page for an identifier."""
|
|
259
|
+
path.write_text(
|
|
260
|
+
f"---\ntitle: {identifier}\n---\n\n::: {identifier}\n options:\n show_submodules: false\n",
|
|
261
|
+
encoding="utf8",
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _get_python_handler(config_path: Path) -> Any:
|
|
266
|
+
"""Return Zensical's shared Python handler for a documentation build."""
|
|
267
|
+
config = parse_config(str(config_path))
|
|
268
|
+
zensical_mkdocstrings.reset()
|
|
269
|
+
zensical_mkdocstrings.get_mkdocstrings_extension(
|
|
270
|
+
**config["plugins"]["mkdocstrings"]["config"],
|
|
271
|
+
config=config,
|
|
272
|
+
)
|
|
273
|
+
handlers = zensical_mkdocstrings.HANDLERS
|
|
274
|
+
return handlers.get_handler("python") # ty:ignore[unresolved-attribute]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _load_packages(handler: Any, packages: list[str]) -> dict[str, Module]:
|
|
278
|
+
"""Load packages into a Python handler's shared Griffe collections."""
|
|
279
|
+
options = handler.get_options({})
|
|
280
|
+
parser = Parser(options.docstring_style) if options.docstring_style else None
|
|
281
|
+
parser_options = asdict(options.docstring_options) if options.docstring_options is not None else None
|
|
282
|
+
|
|
283
|
+
extensions = handler.normalize_extension_paths(options.extensions)
|
|
284
|
+
loader = GriffeLoader(
|
|
285
|
+
extensions=load_extensions(*extensions),
|
|
286
|
+
search_paths=handler._paths,
|
|
287
|
+
docstring_parser=parser,
|
|
288
|
+
docstring_options=parser_options, # ty:ignore[invalid-argument-type]
|
|
289
|
+
modules_collection=handler._modules_collection,
|
|
290
|
+
lines_collection=handler._lines_collection,
|
|
291
|
+
allow_inspection=options.allow_inspection,
|
|
292
|
+
force_inspection=options.force_inspection,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
root_modules = {}
|
|
296
|
+
try:
|
|
297
|
+
for module in options.preload_modules:
|
|
298
|
+
if module not in handler._modules_collection:
|
|
299
|
+
loader.load(
|
|
300
|
+
module,
|
|
301
|
+
try_relative_path=False,
|
|
302
|
+
find_stubs_package=options.find_stubs_package,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
for package in packages:
|
|
306
|
+
if package not in handler._modules_collection:
|
|
307
|
+
loader.load(
|
|
308
|
+
package,
|
|
309
|
+
try_relative_path=False,
|
|
310
|
+
find_stubs_package=options.find_stubs_package,
|
|
311
|
+
)
|
|
312
|
+
root_modules[package] = cast("Module", handler._modules_collection[package])
|
|
313
|
+
except ImportError as error:
|
|
314
|
+
raise CollectionError(str(error)) from error
|
|
315
|
+
|
|
316
|
+
loader.resolve_aliases(
|
|
317
|
+
implicit=False,
|
|
318
|
+
external=handler.config.load_external_modules,
|
|
319
|
+
)
|
|
320
|
+
return root_modules
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def main(args: list[str] | None = None) -> int:
|
|
324
|
+
"""Run the main program.
|
|
325
|
+
|
|
326
|
+
This function is executed when you type `venv-doc` or `python -m venv_doc`.
|
|
327
|
+
|
|
328
|
+
Parameters:
|
|
329
|
+
args: Arguments passed from the command line.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
An exit code.
|
|
333
|
+
"""
|
|
334
|
+
parsed_args = get_parser().parse_args(args)
|
|
335
|
+
python = Path(sys.executable) if parsed_args.self else _venv_python(Path(".venv"))
|
|
336
|
+
packages, package_paths = _venv_packages(python)
|
|
337
|
+
|
|
338
|
+
with TemporaryDirectory() as tmpdir:
|
|
339
|
+
tmppath = Path(tmpdir)
|
|
340
|
+
tmppath.joinpath("docs").mkdir()
|
|
341
|
+
config = dedent(
|
|
342
|
+
f"""\
|
|
343
|
+
[project]
|
|
344
|
+
site_name = "API docs"
|
|
345
|
+
nav = [
|
|
346
|
+
{{ "API docs" = [
|
|
347
|
+
"index.md",
|
|
348
|
+
# {{packages}}
|
|
349
|
+
] }},
|
|
350
|
+
]
|
|
351
|
+
validation = false
|
|
352
|
+
|
|
353
|
+
[project.theme]
|
|
354
|
+
features = [
|
|
355
|
+
"announce.dismiss",
|
|
356
|
+
"content.action.edit",
|
|
357
|
+
"content.action.view",
|
|
358
|
+
"content.code.annotate",
|
|
359
|
+
"content.code.copy",
|
|
360
|
+
"content.tooltips",
|
|
361
|
+
"navigation.footer",
|
|
362
|
+
"navigation.indexes",
|
|
363
|
+
"navigation.instant.preview",
|
|
364
|
+
"navigation.path",
|
|
365
|
+
"navigation.top",
|
|
366
|
+
"search.highlight",
|
|
367
|
+
"search.suggest",
|
|
368
|
+
"toc.follow",
|
|
369
|
+
]
|
|
370
|
+
|
|
371
|
+
[[project.theme.palette]]
|
|
372
|
+
media = "(prefers-color-scheme)"
|
|
373
|
+
toggle.icon = "material/brightness-auto"
|
|
374
|
+
toggle.name = "Switch to light mode"
|
|
375
|
+
|
|
376
|
+
[[project.theme.palette]]
|
|
377
|
+
media = "(prefers-color-scheme: light)"
|
|
378
|
+
scheme = "default"
|
|
379
|
+
primary = "teal"
|
|
380
|
+
accent = "purple"
|
|
381
|
+
toggle.icon = "material/weather-sunny"
|
|
382
|
+
toggle.name = "Switch to dark mode"
|
|
383
|
+
|
|
384
|
+
[[project.theme.palette]]
|
|
385
|
+
media = "(prefers-color-scheme: dark)"
|
|
386
|
+
scheme = "slate"
|
|
387
|
+
primary = "black"
|
|
388
|
+
accent = "lime"
|
|
389
|
+
toggle.icon = "material/weather-night"
|
|
390
|
+
toggle.name = "Switch to system preference"
|
|
391
|
+
|
|
392
|
+
[project.markdown_extensions.toc]
|
|
393
|
+
permalink = true
|
|
394
|
+
|
|
395
|
+
[project.markdown_extensions]
|
|
396
|
+
pycon = {{}}
|
|
397
|
+
"venv_doc._internal.sphinx_roles:_SphinxRolesExtension" = {{}}
|
|
398
|
+
|
|
399
|
+
[project.plugins.mkdocstrings.handlers.python]
|
|
400
|
+
inventories = ["https://docs.python.org/3/objects.inv"]
|
|
401
|
+
paths = {json.dumps(package_paths)}
|
|
402
|
+
|
|
403
|
+
[project.plugins.mkdocstrings.handlers.python.options]
|
|
404
|
+
docstring_options = {{
|
|
405
|
+
per_style_options = {{
|
|
406
|
+
google = {{ warnings = false, ignore_init_summary = true }},
|
|
407
|
+
numpy = {{ warnings = false, ignore_init_summary = true }},
|
|
408
|
+
sphinx = {{ warnings = false }},
|
|
409
|
+
}}
|
|
410
|
+
}}
|
|
411
|
+
docstring_section_style = "list"
|
|
412
|
+
docstring_style = "auto"
|
|
413
|
+
filters = "public"
|
|
414
|
+
heading_level = 1
|
|
415
|
+
inherited_members = true
|
|
416
|
+
merge_init_into_class = true
|
|
417
|
+
scoped_crossrefs = false
|
|
418
|
+
separate_signature = true
|
|
419
|
+
show_if_no_docstring = true
|
|
420
|
+
show_root_heading = true
|
|
421
|
+
show_root_full_path = false
|
|
422
|
+
show_signature_annotations = true
|
|
423
|
+
show_source = false
|
|
424
|
+
show_submodules = false
|
|
425
|
+
show_symbol_type_heading = true
|
|
426
|
+
show_symbol_type_toc = true
|
|
427
|
+
signature_crossrefs = true
|
|
428
|
+
summary = true
|
|
429
|
+
""",
|
|
430
|
+
)
|
|
431
|
+
config_path = tmppath.joinpath("zensical.toml")
|
|
432
|
+
config_path.write_text(config, encoding="utf8")
|
|
433
|
+
tmppath.joinpath("docs", "index.md").write_text(
|
|
434
|
+
"# API docs\n\nSelect a package from the navigation to view its API reference.\n",
|
|
435
|
+
encoding="utf8",
|
|
436
|
+
)
|
|
437
|
+
handler = _get_python_handler(config_path)
|
|
438
|
+
root_modules = _load_packages(handler, packages)
|
|
439
|
+
package_modules = {}
|
|
440
|
+
documented_modules = []
|
|
441
|
+
for package, root_module in root_modules.items():
|
|
442
|
+
package_modules[package] = _public_modules(root_module)
|
|
443
|
+
documented_modules.append(root_module)
|
|
444
|
+
documented_modules.extend(package_modules[package])
|
|
445
|
+
_write_page(tmppath.joinpath("docs", f"{package}.md"), package)
|
|
446
|
+
for module in package_modules[package]:
|
|
447
|
+
_write_page(tmppath.joinpath("docs", f"{module.path}.md"), module.path)
|
|
448
|
+
|
|
449
|
+
nav = ",\n ".join(
|
|
450
|
+
"{ "
|
|
451
|
+
+ json.dumps(package)
|
|
452
|
+
+ " = [\n "
|
|
453
|
+
+ json.dumps(f"{package}.md")
|
|
454
|
+
+ ",\n "
|
|
455
|
+
+ ",\n ".join(
|
|
456
|
+
f"{{ {json.dumps(module.path)} = {json.dumps(f'{module.path}.md')} }}" for module in modules
|
|
457
|
+
)
|
|
458
|
+
+ "\n ] }"
|
|
459
|
+
for package, modules in package_modules.items()
|
|
460
|
+
)
|
|
461
|
+
config_path.write_text(config.replace("# {packages}", nav), encoding="utf8")
|
|
462
|
+
started = perf_counter()
|
|
463
|
+
parsed_docstrings = _preparse_docstrings(documented_modules)
|
|
464
|
+
print(f"Pre-parsed {parsed_docstrings:,} docstrings in {perf_counter() - started:.2f}s", flush=True)
|
|
465
|
+
reset_mkdocstrings = zensical_mkdocstrings.reset
|
|
466
|
+
zensical_mkdocstrings.reset = _noop_mkdocstrings_reset # ty:ignore[invalid-assignment]
|
|
467
|
+
try:
|
|
468
|
+
if parsed_args.command == "build":
|
|
469
|
+
build(
|
|
470
|
+
str(config_path),
|
|
471
|
+
{"clean": parsed_args.clean, "strict": parsed_args.strict},
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
474
|
+
serve(
|
|
475
|
+
str(config_path),
|
|
476
|
+
{
|
|
477
|
+
"dev_addr": parsed_args.dev_addr,
|
|
478
|
+
"open": parsed_args.open,
|
|
479
|
+
"strict": parsed_args.strict,
|
|
480
|
+
},
|
|
481
|
+
)
|
|
482
|
+
finally:
|
|
483
|
+
zensical_mkdocstrings.reset = reset_mkdocstrings
|
|
484
|
+
|
|
485
|
+
return 0
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import platform
|
|
23
|
+
import sys
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from importlib import metadata
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class _Variable:
|
|
30
|
+
"""Dataclass describing an environment variable."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
"""Variable name."""
|
|
34
|
+
value: str
|
|
35
|
+
"""Variable value."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class _Package:
|
|
40
|
+
"""Dataclass describing a Python package."""
|
|
41
|
+
|
|
42
|
+
name: str
|
|
43
|
+
"""Package name."""
|
|
44
|
+
version: str
|
|
45
|
+
"""Package version."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class _Environment:
|
|
50
|
+
"""Dataclass to store environment information."""
|
|
51
|
+
|
|
52
|
+
interpreter_name: str
|
|
53
|
+
"""Python interpreter name."""
|
|
54
|
+
interpreter_version: str
|
|
55
|
+
"""Python interpreter version."""
|
|
56
|
+
interpreter_path: str
|
|
57
|
+
"""Path to Python executable."""
|
|
58
|
+
platform: str
|
|
59
|
+
"""Operating System."""
|
|
60
|
+
packages: list[_Package]
|
|
61
|
+
"""Installed packages."""
|
|
62
|
+
variables: list[_Variable]
|
|
63
|
+
"""Environment variables."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _interpreter_name_version() -> tuple[str, str]:
|
|
67
|
+
if hasattr(sys, "implementation"):
|
|
68
|
+
impl = sys.implementation.version
|
|
69
|
+
version = f"{impl.major}.{impl.minor}.{impl.micro}"
|
|
70
|
+
kind = impl.releaselevel
|
|
71
|
+
if kind != "final":
|
|
72
|
+
version += kind[0] + str(impl.serial)
|
|
73
|
+
return sys.implementation.name, version
|
|
74
|
+
return "", "0.0.0"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _get_version(dist: str = "venv-doc") -> str:
|
|
78
|
+
"""Get version of the given distribution.
|
|
79
|
+
|
|
80
|
+
Parameters:
|
|
81
|
+
dist: A distribution name.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A version number.
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
return metadata.version(dist)
|
|
88
|
+
except metadata.PackageNotFoundError:
|
|
89
|
+
return "0.0.0"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_debug_info() -> _Environment:
|
|
93
|
+
"""Get debug/environment information.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Environment information.
|
|
97
|
+
"""
|
|
98
|
+
py_name, py_version = _interpreter_name_version()
|
|
99
|
+
packages = ["venv-doc"]
|
|
100
|
+
variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("VENV_DOC")]]
|
|
101
|
+
return _Environment(
|
|
102
|
+
interpreter_name=py_name,
|
|
103
|
+
interpreter_version=py_version,
|
|
104
|
+
interpreter_path=sys.executable,
|
|
105
|
+
platform=platform.platform(),
|
|
106
|
+
variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))],
|
|
107
|
+
packages=[_Package(pkg, _get_version(pkg)) for pkg in packages],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _print_debug_info() -> None:
|
|
112
|
+
"""Print debug/environment information."""
|
|
113
|
+
info = _get_debug_info()
|
|
114
|
+
print(f"- __System__: {info.platform}")
|
|
115
|
+
print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})")
|
|
116
|
+
print("- __Environment variables__:")
|
|
117
|
+
for var in info.variables:
|
|
118
|
+
print(f" - `{var.name}`: `{var.value}`")
|
|
119
|
+
print("- __Installed packages__:")
|
|
120
|
+
for pkg in info.packages:
|
|
121
|
+
print(f" - `{pkg.name}` v{pkg.version}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
_print_debug_info()
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE
|
|
17
|
+
# USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from re import fullmatch
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
from xml.etree.ElementTree import Element
|
|
24
|
+
|
|
25
|
+
from markdown.extensions import Extension
|
|
26
|
+
from markdown.inlinepatterns import InlineProcessor
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from re import Match
|
|
30
|
+
|
|
31
|
+
from markdown import Markdown
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_PYTHON_ROLES = "attr|class|const|data|deco|exc|func|meth|mod|obj|type"
|
|
35
|
+
"""Python-domain cross-reference roles supported by Sphinx."""
|
|
36
|
+
|
|
37
|
+
_SPHINX_ROLE_RE = rf"(?<![\w`]):(?:py:)?(?P<role>{_PYTHON_ROLES}):`(?P<content>[^`\n]+)`(?!`)"
|
|
38
|
+
"""Match a supported short or qualified Python-domain Sphinx role."""
|
|
39
|
+
|
|
40
|
+
_PYTHON_IDENTIFIER_RE = r"[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*"
|
|
41
|
+
"""Match the dotted Python object identifiers that mkdocs-autorefs can resolve."""
|
|
42
|
+
|
|
43
|
+
_EXPLICIT_TITLE_RE = r"(?P<title>.+?)\s*<(?P<target>[^<>]+)>"
|
|
44
|
+
"""Match Sphinx's ``title <target>`` cross-reference syntax."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _SphinxRoleInlineProcessor(InlineProcessor):
|
|
48
|
+
"""Turn supported Python-domain Sphinx roles into mkdocs-autorefs markers."""
|
|
49
|
+
|
|
50
|
+
def handleMatch(self, m: Match[str], data: str) -> tuple[Element, int, int]: # noqa: ARG002,N802
|
|
51
|
+
"""Create an ``autoref`` element for a Sphinx role where possible."""
|
|
52
|
+
role = m.group("role")
|
|
53
|
+
content = m.group("content")
|
|
54
|
+
|
|
55
|
+
# ``!`` explicitly disables linking in Sphinx. It must not become an
|
|
56
|
+
# autoref marker because a resolvable marker would create a link again.
|
|
57
|
+
if content.startswith("!"):
|
|
58
|
+
element = Element("code")
|
|
59
|
+
element.text = content[1:]
|
|
60
|
+
return element, m.start(0), m.end(0)
|
|
61
|
+
|
|
62
|
+
explicit_title = fullmatch(_EXPLICIT_TITLE_RE, content)
|
|
63
|
+
if explicit_title:
|
|
64
|
+
title = explicit_title.group("title")
|
|
65
|
+
identifier = explicit_title.group("target")
|
|
66
|
+
else:
|
|
67
|
+
title = content
|
|
68
|
+
identifier = content
|
|
69
|
+
|
|
70
|
+
# Sphinx uses a leading ``~`` only to shorten an implicit title, and a
|
|
71
|
+
# leading ``.`` only to affect lookup precedence. Autorefs has no
|
|
72
|
+
# equivalent contextual lookup, so resolve the unprefixed identifier.
|
|
73
|
+
if not explicit_title:
|
|
74
|
+
title = title.lstrip(".")
|
|
75
|
+
if title.startswith("~"):
|
|
76
|
+
title = title[1:].rsplit(".", maxsplit=1)[-1]
|
|
77
|
+
identifier = identifier.strip().lstrip("~.").removesuffix("()")
|
|
78
|
+
|
|
79
|
+
if not fullmatch(_PYTHON_IDENTIFIER_RE, identifier):
|
|
80
|
+
# Leave syntax that cannot name an mkdocs-autorefs object to
|
|
81
|
+
# Markdown's normal handling rather than making an invalid marker.
|
|
82
|
+
element = Element("code")
|
|
83
|
+
element.text = content
|
|
84
|
+
return element, m.start(0), m.end(0)
|
|
85
|
+
|
|
86
|
+
if role == "deco":
|
|
87
|
+
title = f"@{title}"
|
|
88
|
+
|
|
89
|
+
element = Element("autoref", {"identifier": identifier})
|
|
90
|
+
element.text = title
|
|
91
|
+
return element, m.start(0), m.end(0)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class _SphinxRolesExtension(Extension):
|
|
95
|
+
"""Convert Python-domain Sphinx roles to auto-references."""
|
|
96
|
+
|
|
97
|
+
name = "venv-doc-sphinx-roles"
|
|
98
|
+
|
|
99
|
+
def extendMarkdown(self, md: Markdown) -> None: # noqa: N802
|
|
100
|
+
"""Register the role processor before Markdown's code-span processor."""
|
|
101
|
+
md.inlinePatterns.register(
|
|
102
|
+
_SphinxRoleInlineProcessor(_SPHINX_ROLE_RE, md),
|
|
103
|
+
self.name,
|
|
104
|
+
priority=191,
|
|
105
|
+
)
|
venv_doc/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: venv-doc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Like `cargo doc` for Python venvs.
|
|
5
|
+
Author-Email: Timothée Mazzucotelli <dev@pawamoy.fr>
|
|
6
|
+
License-Expression: ISC
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Classifier: Topic :: Documentation
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Project-URL: Homepage, https://pawamoy.github.io/venv-doc
|
|
23
|
+
Project-URL: Documentation, https://pawamoy.github.io/venv-doc
|
|
24
|
+
Project-URL: Changelog, https://pawamoy.github.io/venv-doc/changelog
|
|
25
|
+
Project-URL: Repository, https://github.com/pawamoy/venv-doc
|
|
26
|
+
Project-URL: Issues, https://github.com/pawamoy/venv-doc/issues
|
|
27
|
+
Project-URL: Discussions, https://github.com/pawamoy/venv-doc/discussions
|
|
28
|
+
Project-URL: Gitter, https://gitter.im/venv-doc/community
|
|
29
|
+
Project-URL: Funding, https://github.com/sponsors/pawamoy
|
|
30
|
+
Requires-Python: >=3.11
|
|
31
|
+
Requires-Dist: griffe>=2.2
|
|
32
|
+
Requires-Dist: prune-source>=0.1
|
|
33
|
+
Requires-Dist: markdown-pycon>=1.0
|
|
34
|
+
Requires-Dist: mkdocstrings-python>=2.0
|
|
35
|
+
Requires-Dist: zensical>=0.0.57
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# venv-doc
|
|
39
|
+
|
|
40
|
+
[](https://github.com/pawamoy/venv-doc/actions?query=workflow%3Aci)
|
|
41
|
+
[](https://pawamoy.github.io/venv-doc/)
|
|
42
|
+
[](https://pypi.org/project/venv-doc/)
|
|
43
|
+
[](https://app.gitter.im/#/room/#venv-doc:gitter.im)
|
|
44
|
+
|
|
45
|
+
Like `cargo doc` for Python venvs.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install venv-doc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
With [`uv`](https://docs.astral.sh/uv/):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv tool install venv-doc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Sponsors
|
|
60
|
+
|
|
61
|
+
<!-- sponsors-start -->
|
|
62
|
+
<!-- sponsors-end -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
venv_doc-0.1.0.dist-info/METADATA,sha256=5c8ssgSSRs_ondZki45DmRDfBm_O5e8W56CSaCbPcCQ,2276
|
|
2
|
+
venv_doc-0.1.0.dist-info/WHEEL,sha256=IuIk3wbaEnFH1cx75iM2R1CmliRI3dFLXTbRakgvgFQ,90
|
|
3
|
+
venv_doc-0.1.0.dist-info/entry_points.txt,sha256=c6waGinuuxmqaM4LVxN7e5Wnks0PCE5ckU4YgmtJV00,59
|
|
4
|
+
venv_doc-0.1.0.dist-info/licenses/LICENSE,sha256=FnCVYk610bj4MkIFuYhAXmhIBYKebHhVp2YWO-8rEVY,771
|
|
5
|
+
venv_doc/__init__.py,sha256=aPwPGdcepJuu-3XUkyHNRjq2bZX0cyNgvpU1EdnyOR4,1027
|
|
6
|
+
venv_doc/__main__.py,sha256=Tw8JJrqHjAxblqjNHvOhnFqcMyVovqWmiLs0E6Qw58E,1183
|
|
7
|
+
venv_doc/_internal/__init__.py,sha256=FNcSqCgny9Z6h2ijJwSvekOi5ka7WWQFmAGC7rNkmJE,831
|
|
8
|
+
venv_doc/_internal/cli.py,sha256=0ZJpGfpWsCHsLmA3x7GGzLxGObHFBFHx46NmAfnKHwM,16622
|
|
9
|
+
venv_doc/_internal/debug.py,sha256=PQzUIM1YIe5_DvnPdNyLSLqgDpO55ByL1_MAot8ip1w,3644
|
|
10
|
+
venv_doc/_internal/sphinx_roles.py,sha256=VpD9ZqBT9ByHXaP1U8za8aJIveVYNiccUUy6kbWQWpQ,4124
|
|
11
|
+
venv_doc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
venv_doc-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Timothée Mazzucotelli and contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|