pdoc 14.6.0__py3-none-any.whl → 14.7.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.
pdoc/__init__.py CHANGED
@@ -481,7 +481,7 @@ You can find an example in [`examples/library-usage`](https://github.com/mitmpro
481
481
  from __future__ import annotations
482
482
 
483
483
  __docformat__ = "markdown" # explicitly disable rST processing in the examples above.
484
- __version__ = "14.6.0" # this is read from setup.py
484
+ __version__ = "14.7.0" # this is read from setup.py
485
485
 
486
486
  from pathlib import Path
487
487
  from typing import overload
pdoc/_compat.py CHANGED
@@ -123,6 +123,16 @@ else: # pragma: no cover
123
123
  return ' | '.join(self.option_strings)
124
124
 
125
125
 
126
+ if sys.version_info >= (3, 10):
127
+ from typing import is_typeddict
128
+ else: # pragma: no cover
129
+ def is_typeddict(tp):
130
+ try:
131
+ return tp.__orig_bases__[-1].__name__ == "TypedDict"
132
+ except Exception:
133
+ return False
134
+
135
+
126
136
  __all__ = [
127
137
  "cache",
128
138
  "ast_unparse",
@@ -134,4 +144,5 @@ __all__ = [
134
144
  "removesuffix",
135
145
  "formatannotation",
136
146
  "BooleanOptionalAction",
147
+ "is_typeddict",
137
148
  ]
pdoc/doc.py CHANGED
@@ -37,6 +37,7 @@ import types
37
37
  from typing import Any
38
38
  from typing import ClassVar
39
39
  from typing import Generic
40
+ from typing import TypedDict
40
41
  from typing import TypeVar
41
42
  from typing import Union
42
43
  from typing import get_origin
@@ -49,6 +50,7 @@ from pdoc._compat import TypeAlias
49
50
  from pdoc._compat import TypeAliasType
50
51
  from pdoc._compat import cache
51
52
  from pdoc._compat import formatannotation
53
+ from pdoc._compat import is_typeddict
52
54
  from pdoc.doc_types import GenericAlias
53
55
  from pdoc.doc_types import NonUserDefinedCallables
54
56
  from pdoc.doc_types import empty
@@ -655,14 +657,21 @@ class Class(Namespace[type]):
655
657
  @cached_property
656
658
  def _bases(self) -> tuple[type, ...]:
657
659
  orig_bases = _safe_getattr(self.obj, "__orig_bases__", ())
658
- old_python_typeddict_workaround = (
659
- sys.version_info < (3, 12)
660
- and orig_bases
661
- and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict"
662
- )
663
- if old_python_typeddict_workaround: # pragma: no cover
664
- # TypedDicts on Python <3.12 have a botched __mro__. We need to fix it.
665
- return (self.obj, *orig_bases[:-1])
660
+
661
+ if is_typeddict(self.obj):
662
+ if sys.version_info < (3, 12): # pragma: no cover
663
+ # TypedDicts on Python <3.12 have a botched __mro__. We need to fix it.
664
+ return (self.obj, *orig_bases[:-1])
665
+ else:
666
+ # TypedDict on Python >=3.12 removes intermediate classes from __mro__,
667
+ # so we use orig_bases to recover the full mro.
668
+ while orig_bases and orig_bases[-1] is not TypedDict:
669
+ parent_bases = _safe_getattr(orig_bases[-1], "__orig_bases__", ())
670
+ if (
671
+ len(parent_bases) != 1 or parent_bases in orig_bases
672
+ ): # sanity check that things look right
673
+ break # pragma: no cover
674
+ orig_bases = (*orig_bases, parent_bases[0])
666
675
 
667
676
  # __mro__ and __orig_bases__ differ between Python versions and special cases like TypedDict/NamedTuple.
668
677
  # This here is a pragmatic approximation of what we want.
@@ -1123,9 +1132,11 @@ class Variable(Doc[None]):
1123
1132
  if self.default_value is empty:
1124
1133
  return ""
1125
1134
  if isinstance(self.default_value, TypeAliasType):
1126
- return formatannotation(self.default_value.__value__)
1135
+ formatted = formatannotation(self.default_value.__value__)
1136
+ return _remove_collections_abc(formatted)
1127
1137
  elif self.annotation == TypeAlias:
1128
- return formatannotation(self.default_value)
1138
+ formatted = formatannotation(self.default_value)
1139
+ return _remove_collections_abc(formatted)
1129
1140
 
1130
1141
  # This is not perfect, but a solid attempt at preventing accidental leakage of secrets.
1131
1142
  # If you have input on how to improve the heuristic, please send a pull request!
@@ -1157,7 +1168,8 @@ class Variable(Doc[None]):
1157
1168
  def annotation_str(self) -> str:
1158
1169
  """The variable's type annotation as a pretty-printed str."""
1159
1170
  if self.annotation is not empty:
1160
- return f": {formatannotation(self.annotation)}"
1171
+ formatted = formatannotation(self.annotation)
1172
+ return f": {_remove_collections_abc(formatted)}"
1161
1173
  else:
1162
1174
  return ""
1163
1175
 
@@ -1193,6 +1205,7 @@ class _PrettySignature(inspect.Signature):
1193
1205
  for param in self.parameters.values():
1194
1206
  formatted = str(param)
1195
1207
  formatted = _remove_memory_addresses(formatted)
1208
+ formatted = _remove_collections_abc(formatted)
1196
1209
 
1197
1210
  kind = param.kind
1198
1211
 
@@ -1229,7 +1242,8 @@ class _PrettySignature(inspect.Signature):
1229
1242
 
1230
1243
  def _return_annotation_str(self) -> str:
1231
1244
  if self.return_annotation is not empty:
1232
- return formatannotation(self.return_annotation)
1245
+ formatted = formatannotation(self.return_annotation)
1246
+ return _remove_collections_abc(formatted)
1233
1247
  else:
1234
1248
  return ""
1235
1249
 
@@ -1324,3 +1338,8 @@ _Enum_default_docstrings = tuple(
1324
1338
  def _remove_memory_addresses(x: str) -> str:
1325
1339
  """Remove memory addresses from repr() output"""
1326
1340
  return re.sub(r" at 0x[0-9a-fA-F]+(?=>)", "", x)
1341
+
1342
+
1343
+ def _remove_collections_abc(x: str) -> str:
1344
+ """Remove 'collections.abc' from type signatures."""
1345
+ return re.sub(r"(?!\.)\bcollections\.abc\.", "", x)
pdoc/doc_ast.py CHANGED
@@ -240,12 +240,11 @@ def _parse_class(source: str) -> ast.ClassDef:
240
240
  Returns an empty ast.ClassDef if source is empty.
241
241
  """
242
242
  tree = _parse(source)
243
- assert len(tree.body) <= 1
244
- if tree.body:
243
+ if tree.body and len(tree.body) == 1:
245
244
  t = tree.body[0]
246
- assert isinstance(t, ast.ClassDef)
247
- return t
248
- return ast.ClassDef(body=[], decorator_list=[])
245
+ if isinstance(t, ast.ClassDef):
246
+ return t
247
+ return ast.ClassDef(name="PdocStub", body=[], decorator_list=[]) # type: ignore
249
248
 
250
249
 
251
250
  @cache
@@ -256,8 +255,7 @@ def _parse_function(source: str) -> ast.FunctionDef | ast.AsyncFunctionDef:
256
255
  Returns an empty ast.FunctionDef if source is empty.
257
256
  """
258
257
  tree = _parse(source)
259
- assert len(tree.body) <= 1
260
- if tree.body:
258
+ if tree.body and len(tree.body) == 1:
261
259
  t = tree.body[0]
262
260
  if isinstance(t, (ast.FunctionDef, ast.AsyncFunctionDef)):
263
261
  return t
@@ -265,7 +263,9 @@ def _parse_function(source: str) -> ast.FunctionDef | ast.AsyncFunctionDef:
265
263
  # we have a lambda function,
266
264
  # to simplify the API return the ast.FunctionDef stub.
267
265
  pass
268
- return ast.FunctionDef(body=[], decorator_list=[])
266
+ return ast.FunctionDef(
267
+ name="pdoc_stub", args=ast.arguments(), body=[], decorator_list=[]
268
+ ) # type: ignore
269
269
 
270
270
 
271
271
  def _parse(
pdoc/doc_pyi.py CHANGED
@@ -6,6 +6,7 @@ This makes it possible to add type hints for native modules such as modules writ
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import importlib.util
9
10
  from pathlib import Path
10
11
  import sys
11
12
  import traceback
@@ -45,13 +46,28 @@ def find_stub_file(module_name: str) -> Path | None:
45
46
 
46
47
 
47
48
  def _import_stub_file(module_name: str, stub_file: Path) -> types.ModuleType:
48
- """Import the type stub outside of the normal import machinery."""
49
- code = compile(stub_file.read_text(), str(stub_file), "exec")
50
- m = types.ModuleType(module_name)
51
- m.__file__ = str(stub_file)
52
- eval(code, m.__dict__, m.__dict__)
49
+ """
50
+ Import the type stub outside of the normal import machinery.
53
51
 
54
- return m
52
+ Note that currently, for objects imported by the stub file, the _original_ module
53
+ is used and not the corresponding stub file.
54
+ """
55
+ sys.path_hooks.append(
56
+ importlib.machinery.FileFinder.path_hook(
57
+ (importlib.machinery.SourceFileLoader, [".pyi"])
58
+ )
59
+ )
60
+ try:
61
+ loader = importlib.machinery.SourceFileLoader(module_name, str(stub_file))
62
+ spec = importlib.util.spec_from_file_location(
63
+ module_name, stub_file, loader=loader
64
+ )
65
+ assert spec is not None
66
+ m = importlib.util.module_from_spec(spec)
67
+ loader.exec_module(m)
68
+ return m
69
+ finally:
70
+ sys.path_hooks.pop()
55
71
 
56
72
 
57
73
  def _prepare_module(ns: doc.Namespace) -> None:
pdoc/render_helpers.py CHANGED
@@ -307,11 +307,17 @@ def module_candidates(identifier: str, current_module: str) -> Iterable[str]:
307
307
 
308
308
 
309
309
  @pass_context
310
- def linkify(context: Context, code: str, namespace: str = "") -> str:
310
+ def linkify(
311
+ context: Context, code: str, namespace: str = "", shorten: bool = True
312
+ ) -> str:
311
313
  """
312
314
  Link all identifiers in a block of text. Identifiers referencing unknown modules or modules that
313
315
  are not rendered at the moment will be ignored.
314
316
  A piece of text is considered to be an identifier if it either contains a `.` or is surrounded by `<code>` tags.
317
+
318
+ If `shorten` is True, replace identifiers with short forms where possible.
319
+ For example, replace "current_module.Foo" with "Foo". This is useful for annotations
320
+ (which are verbose), but undesired for docstrings (where we want to preserve intent).
315
321
  """
316
322
 
317
323
  def linkify_repl(m: re.Match):
@@ -381,12 +387,15 @@ def linkify(context: Context, code: str, namespace: str = "") -> str:
381
387
  and (target_object is doc.obj or target_object is None)
382
388
  and context["is_public"](doc).strip()
383
389
  ):
384
- if module == mod:
385
- url_text = qualname
390
+ if shorten:
391
+ if module == mod:
392
+ url_text = qualname
393
+ else:
394
+ url_text = doc.fullname
395
+ if plain_text.endswith("()"):
396
+ url_text += "()"
386
397
  else:
387
- url_text = doc.fullname
388
- if plain_text.endswith("()"):
389
- url_text += "()"
398
+ url_text = plain_text
390
399
  return f'<a href="{relative_link(context["module"].modulename, doc.modulename)}#{qualname}">{url_text}</a>'
391
400
 
392
401
  # No matches found.
@@ -209,7 +209,7 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an
209
209
  {% enddefaultmacro %}
210
210
  {% defaultmacro docstring(var) %}
211
211
  {% if var.docstring %}
212
- <div class="docstring">{{ var.docstring | replace("@public", "") | to_markdown | to_html | linkify(namespace=var.qualname) }}</div>
212
+ <div class="docstring">{{ var.docstring | replace("@public", "") | to_markdown | to_html | linkify(namespace=var.qualname, shorten=False) }}</div>
213
213
  {% endif %}
214
214
  {% enddefaultmacro %}
215
215
  {% defaultmacro nav_members(members) %}
@@ -244,7 +244,7 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an
244
244
  {% elif "@public" in doc.docstring %}
245
245
  {# show members explicitly marked as @public #}
246
246
  true
247
- {% elif not include_undocumented and not doc.docstring %}
247
+ {% elif not include_undocumented and not doc.docstring and doc.kind != "module" %}
248
248
  {# hide members that are undocumented if include_undocumented has been toggled off. #}
249
249
  {% elif doc.name == "__init__" and (doc.docstring or (doc.kind == "function" and doc.signature_without_self.parameters)) %}
250
250
  {# show constructors that have a docstring or at least one extra argument #}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pdoc
3
- Version: 14.6.0
3
+ Version: 14.7.0
4
4
  Summary: API Documentation for Python Projects
5
5
  Author-email: Maximilian Hils <pdoc@maximilianhils.com>
6
6
  License: MIT-0
@@ -21,25 +21,26 @@ Classifier: Programming Language :: Python :: 3.9
21
21
  Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
24
25
  Classifier: Typing :: Typed
25
26
  Requires-Python: >=3.8
26
27
  Description-Content-Type: text/markdown
27
28
  License-File: LICENSE
28
- Requires-Dist: Jinja2 >=2.11.0
29
- Requires-Dist: pygments >=2.12.0
29
+ Requires-Dist: Jinja2>=2.11.0
30
+ Requires-Dist: pygments>=2.12.0
30
31
  Requires-Dist: MarkupSafe
31
- Requires-Dist: astunparse ; python_version < "3.9"
32
+ Requires-Dist: astunparse; python_version < "3.9"
32
33
  Provides-Extra: dev
33
- Requires-Dist: tox ; extra == 'dev'
34
- Requires-Dist: ruff ; extra == 'dev'
35
- Requires-Dist: mypy ; extra == 'dev'
36
- Requires-Dist: types-pygments ; extra == 'dev'
37
- Requires-Dist: pytest ; extra == 'dev'
38
- Requires-Dist: pytest-cov ; extra == 'dev'
39
- Requires-Dist: pytest-timeout ; extra == 'dev'
40
- Requires-Dist: hypothesis ; extra == 'dev'
41
- Requires-Dist: pygments >=2.14.0 ; extra == 'dev'
42
- Requires-Dist: pdoc-pyo3-sample-library ==1.0.11 ; extra == 'dev'
34
+ Requires-Dist: tox; extra == "dev"
35
+ Requires-Dist: ruff; extra == "dev"
36
+ Requires-Dist: mypy; extra == "dev"
37
+ Requires-Dist: types-pygments; extra == "dev"
38
+ Requires-Dist: pytest; extra == "dev"
39
+ Requires-Dist: pytest-cov; extra == "dev"
40
+ Requires-Dist: pytest-timeout; extra == "dev"
41
+ Requires-Dist: hypothesis; extra == "dev"
42
+ Requires-Dist: pygments>=2.14.0; extra == "dev"
43
+ Requires-Dist: pdoc-pyo3-sample-library==1.0.11; extra == "dev"
43
44
 
44
45
  <p align="center">
45
46
  <a href="https://pdoc.dev/"><img alt="pdoc" src="https://pdoc.dev/logo.svg" width="200" height="100" /></a>
@@ -1,15 +1,15 @@
1
- pdoc/__init__.py,sha256=SR29piNT7ZRfryJ7b-MAHzsmUEN1__5kuwBatXofZ88,21456
1
+ pdoc/__init__.py,sha256=TyBp31CH_-JtsJqTz6oKZ7nA6g89o44CIxxzmVSVyGk,21456
2
2
  pdoc/__main__.py,sha256=7Xrkxw6-qaSfZfCGFlunl3TfR93uWiCSnvoFABuOmmE,8418
3
- pdoc/_compat.py,sha256=wKGKTxTTxfNjEcKPwvtuvNeOyiozPfL52h8PArKky-0,3843
4
- pdoc/doc.py,sha256=WsxiFNwiqqY3Gn12Ee-hafVrN_AbCXCs_SC8jaTTWfQ,48881
5
- pdoc/doc_ast.py,sha256=ChAOF7qcuRQbdWnRamH6OUtvvIo-JOVCetPjRnb2a3w,11299
6
- pdoc/doc_pyi.py,sha256=TT6vbugw53vDgunegloJONSLRAaeXswqKah1_TVuUwA,4567
3
+ pdoc/_compat.py,sha256=OKpXrlLyPCNCHx-csUMLMEuhZTyW6vNe8bznhNH4hL0,4114
4
+ pdoc/doc.py,sha256=BATA6UqK9SU1ZWXIYSMRCZi9os3a19QJm5-gUU-bqMU,49851
5
+ pdoc/doc_ast.py,sha256=A77c3Gq52_Im-lP475_S4fCD4GIJy0_FbEAZye885fo,11389
6
+ pdoc/doc_pyi.py,sha256=-ZGMz_HctkOPm19eV42vR6pB4KM8odGCjgrVoRPvJXM,5078
7
7
  pdoc/doc_types.py,sha256=mIgMntaw-jCPn_yW2fVTdvkqnWwQys0UKmMTucrdI2w,8468
8
8
  pdoc/docstrings.py,sha256=IdjpZYROqRNRfj1tmAKVuB2WfS1HIMqQwOeYpvbvXio,16384
9
9
  pdoc/extract.py,sha256=7QqxtsKfvcpFi2yBvpFPQe-YR6KAW_L5PCh9v9g1m_c,14307
10
10
  pdoc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  pdoc/render.py,sha256=-gbybpZQUJICYABZr4HJLBsX43vsMOl1Vo1FIshz9pU,7054
12
- pdoc/render_helpers.py,sha256=mqMLdsr4NQiUPQFa6FsRh5z2Id78f4CmOz84cUA_tTM,19985
12
+ pdoc/render_helpers.py,sha256=oPQbYkixh0qaAIUHW93QCephB9gacMdtRTHA5wFOeUc,20400
13
13
  pdoc/search.py,sha256=RGFaRftEQOg1Mw4FEOmJVRY9DhBncBYSFi5r4MSknTM,7248
14
14
  pdoc/web.py,sha256=F2AvCZr2ucVf9ZlN_SWBO0XpcSKbZTfiN47ypX85zzo,6896
15
15
  pdoc/markdown2/LICENSE,sha256=BfcOT5Iu-7wDaKcbIta8wkP-pFncOu4yXeBlMfbeYGI,1116
@@ -30,7 +30,7 @@ pdoc/templates/theme.css,sha256=yjnbtPVmqaifTINsA5sK2a3e1aoFO0UlOMgOSKUgLvI,397
30
30
  pdoc/templates/default/error.html.jinja2,sha256=RqH14o6ZV79uBO14gKSjwObXFKKczkcLTMe1orurEJk,970
31
31
  pdoc/templates/default/frame.html.jinja2,sha256=Qp5zlts8Bl-O2f0cxJHH_QdmAkMtJJjNVA0ZU2Mbwl8,2164
32
32
  pdoc/templates/default/index.html.jinja2,sha256=MkzVYMWUuJgscty3jVbDtcEfB1vz-bp3v_sMa5MzevU,2381
33
- pdoc/templates/default/module.html.jinja2,sha256=8IfopEB0v7b24EmNGYbuUSO4zpXmRj0-_YL1YtWB7lE,13241
33
+ pdoc/templates/default/module.html.jinja2,sha256=wUqEgest0bhIML_4SFByQKVfvhSZj9WUbhy-ncddUNQ,13281
34
34
  pdoc/templates/deprecated/README.md,sha256=8HPj4Bu6EPAdHbH5CQVYgcD580zpkhqbwiSFoLAVB1Q,339
35
35
  pdoc/templates/deprecated/bootstrap-reboot.min.css,sha256=-u1DTWljwtvjAc8X-ihFhIixKaPcs4w_uZ_CYvvP9-o,207
36
36
  pdoc/templates/deprecated/box-arrow-in-left.svg,sha256=-u1DTWljwtvjAc8X-ihFhIixKaPcs4w_uZ_CYvvP9-o,207
@@ -47,9 +47,9 @@ pdoc/templates/resources/info-circle-fill.svg,sha256=kO3AMXfWtacpJPzC8Pvihf46OZd
47
47
  pdoc/templates/resources/lightning-fill.svg,sha256=XEyCtbgxeAlwCezdsf7N0NFd5aMjwqyJJDpaFbYYTFA,265
48
48
  pdoc/templates/resources/navtoggle.svg,sha256=WVR0BJIucX0MgwwEawmfX0qYD1i_dSbUhoGnqPef3jw,187
49
49
  pdoc/templates/resources/pdoc-logo.svg,sha256=w5OsMmytDaA2Fr9CobeQQFxBNx4-wFFHtLvkORj0gjk,6989
50
- pdoc-14.6.0.dist-info/LICENSE,sha256=LrhIeJ7gKTDPyOX9YVuZGr9mpmyjpkvqH6LjxvE0szM,898
51
- pdoc-14.6.0.dist-info/METADATA,sha256=CLKP-hbqqFiinS5d2YyqnJRiJWsfyy_Blv68-j3QRak,7285
52
- pdoc-14.6.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
53
- pdoc-14.6.0.dist-info/entry_points.txt,sha256=-bK-S1ZvmqCWqi1hGnsl5nayWkzXB1BEs-Cynh5QZaI,43
54
- pdoc-14.6.0.dist-info/top_level.txt,sha256=rg5eIToBHzwTfZZi1E7NVHgie5joQuSuU1rWV0qKS9k,5
55
- pdoc-14.6.0.dist-info/RECORD,,
50
+ pdoc-14.7.0.dist-info/LICENSE,sha256=LrhIeJ7gKTDPyOX9YVuZGr9mpmyjpkvqH6LjxvE0szM,898
51
+ pdoc-14.7.0.dist-info/METADATA,sha256=CnJAbNSbI-5EBnWL4TRf2jkFiQu57dQ0Gl1ifoylXns,7321
52
+ pdoc-14.7.0.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
53
+ pdoc-14.7.0.dist-info/entry_points.txt,sha256=-bK-S1ZvmqCWqi1hGnsl5nayWkzXB1BEs-Cynh5QZaI,43
54
+ pdoc-14.7.0.dist-info/top_level.txt,sha256=rg5eIToBHzwTfZZi1E7NVHgie5joQuSuU1rWV0qKS9k,5
55
+ pdoc-14.7.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (74.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
File without changes