hippogriffe 0.1.3__py3-none-any.whl → 0.2.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.
- hippogriffe/_extension.py +58 -32
- hippogriffe/_plugin.py +2 -8
- {hippogriffe-0.1.3.dist-info → hippogriffe-0.2.0.dist-info}/METADATA +12 -10
- {hippogriffe-0.1.3.dist-info → hippogriffe-0.2.0.dist-info}/RECORD +7 -7
- {hippogriffe-0.1.3.dist-info → hippogriffe-0.2.0.dist-info}/WHEEL +0 -0
- {hippogriffe-0.1.3.dist-info → hippogriffe-0.2.0.dist-info}/entry_points.txt +0 -0
- {hippogriffe-0.1.3.dist-info → hippogriffe-0.2.0.dist-info}/licenses/LICENSE +0 -0
hippogriffe/_extension.py
CHANGED
@@ -2,6 +2,7 @@ import ast
|
|
2
2
|
import builtins
|
3
3
|
import contextlib
|
4
4
|
import functools as ft
|
5
|
+
import importlib
|
5
6
|
import inspect
|
6
7
|
import pathlib
|
7
8
|
import re
|
@@ -33,23 +34,38 @@ class _PublicApi:
|
|
33
34
|
pkg: griffe.Module,
|
34
35
|
top_level_public_api: set[str],
|
35
36
|
builtin_modules: list[str],
|
36
|
-
|
37
|
-
extra_public_modules: list[str],
|
37
|
+
extra_public_objects: list[str],
|
38
38
|
):
|
39
39
|
self._objects: set[griffe.Object] = set()
|
40
40
|
self._toplevel_objects: set[griffe.Object] = set()
|
41
41
|
self._data: dict[str, list[str]] = {}
|
42
42
|
self._builtin_modules = builtin_modules
|
43
|
-
|
43
|
+
for object_path in extra_public_objects:
|
44
|
+
object_pieces = object_path.split(".")
|
45
|
+
for i in reversed(range(1, len(object_pieces))):
|
46
|
+
module_name = "".join(object_pieces[:i])
|
47
|
+
object_name = object_pieces[i:]
|
48
|
+
try:
|
49
|
+
object = importlib.import_module(module_name)
|
50
|
+
except Exception:
|
51
|
+
continue
|
52
|
+
for object_piece in object_name:
|
53
|
+
object = getattr(object, object_piece)
|
54
|
+
private_path = f"{object.__module__}.{object.__qualname__}"
|
55
|
+
try:
|
56
|
+
paths = self._data[private_path]
|
57
|
+
except KeyError:
|
58
|
+
paths = self._data[private_path] = []
|
59
|
+
paths.append(object_path)
|
44
60
|
# Don't infinite loop on cycles. We only store Objects, and not Aliases, as in
|
45
61
|
# cycles then the aliases with be distinct: `X.Y.X.Y` is not `X.Y`, though the
|
46
62
|
# underlying object is the same.
|
47
63
|
|
48
|
-
agenda: list[tuple[griffe.Object, bool]] = [(pkg, False)]
|
64
|
+
agenda: list[tuple[griffe.Object, str, bool]] = [(pkg, pkg.path, False)]
|
49
65
|
seen: set[griffe.Object] = {pkg}
|
50
66
|
while len(agenda) > 0:
|
51
|
-
item, force_public = agenda.pop()
|
52
|
-
toplevel_public =
|
67
|
+
item, public_path, force_public = agenda.pop()
|
68
|
+
toplevel_public = public_path in top_level_public_api
|
53
69
|
if force_public or toplevel_public:
|
54
70
|
# If we're in the public API, then we consider all of our children to be
|
55
71
|
# in it as well... (this saves us from having to parse out `filters` and
|
@@ -58,7 +74,7 @@ class _PublicApi:
|
|
58
74
|
paths = self._data[item.path]
|
59
75
|
except KeyError:
|
60
76
|
paths = self._data[item.path] = []
|
61
|
-
paths.append(
|
77
|
+
paths.append(public_path)
|
62
78
|
self._objects.add(item)
|
63
79
|
if toplevel_public:
|
64
80
|
self._toplevel_objects.add(item)
|
@@ -70,7 +86,7 @@ class _PublicApi:
|
|
70
86
|
for member in item.all_members.values():
|
71
87
|
# Skip private elements
|
72
88
|
if member.name.startswith("_") and not (
|
73
|
-
member.name.startswith("__") and
|
89
|
+
member.name.startswith("__") and member.name.endswith("__")
|
74
90
|
):
|
75
91
|
continue
|
76
92
|
if isinstance(member, griffe.Alias):
|
@@ -87,7 +103,7 @@ class _PublicApi:
|
|
87
103
|
final_member = member
|
88
104
|
if final_member in seen:
|
89
105
|
continue
|
90
|
-
agenda.append((final_member, sub_force_public))
|
106
|
+
agenda.append((final_member, member.path, sub_force_public))
|
91
107
|
seen.add(final_member)
|
92
108
|
|
93
109
|
def toplevel(self) -> Iterable[griffe.Object]:
|
@@ -103,16 +119,17 @@ class _PublicApi:
|
|
103
119
|
for m in self._builtin_modules:
|
104
120
|
if key.startswith(m + "."):
|
105
121
|
return key.removeprefix(m + "."), False
|
106
|
-
for m in
|
122
|
+
for m in sys.stdlib_module_names:
|
107
123
|
if key.startswith(m + "."):
|
108
124
|
return key, False
|
109
|
-
#
|
125
|
+
# Note that this message must not have any newlines in it, to display
|
126
|
+
# correctly.
|
110
127
|
raise _NotInPublicApiException(
|
111
|
-
f"Tried and failed to find {key} in the public API. Commons reasons "
|
112
|
-
"for this error are
|
113
|
-
"
|
114
|
-
"
|
115
|
-
"
|
128
|
+
f"Tried and failed to find `{key}` in the public API. Commons reasons "
|
129
|
+
"for this error are (1) if it is from outside this package, then this "
|
130
|
+
"object is not listed (under whatever public path it should be "
|
131
|
+
"displayed as) in `hippogriffe.extra_public_objects`; (2) if it is "
|
132
|
+
"from inside this package, then it may have been written "
|
116
133
|
"`::: somelib.Foo:` with a trailing colon, when just `:::somelib.Foo` "
|
117
134
|
"is correct."
|
118
135
|
) from e
|
@@ -211,20 +228,17 @@ def _resolved_bases(cls: griffe.Class) -> list[str | griffe.Object]:
|
|
211
228
|
return resolved_bases
|
212
229
|
|
213
230
|
|
214
|
-
def _collect_bases(
|
215
|
-
cls: griffe.Class, public_api: _PublicApi, public_modules: set[str]
|
216
|
-
) -> dict[str, bool]:
|
231
|
+
def _collect_bases(cls: griffe.Class, public_api: _PublicApi) -> dict[str, bool]:
|
217
232
|
bases: dict[str, bool] = {}
|
218
233
|
for base in _resolved_bases(cls):
|
219
234
|
if isinstance(base, str):
|
220
235
|
# builtins case above
|
221
|
-
|
222
|
-
bases[base] = False
|
236
|
+
bases[base] = False
|
223
237
|
elif isinstance(base, griffe.Class):
|
224
238
|
try:
|
225
239
|
base, autoref = public_api[base.path]
|
226
240
|
except _NotInPublicApiException:
|
227
|
-
bases.update(_collect_bases(base, public_api
|
241
|
+
bases.update(_collect_bases(base, public_api))
|
228
242
|
else:
|
229
243
|
bases[base] = autoref
|
230
244
|
return bases
|
@@ -324,8 +338,7 @@ class HippogriffeExtension(griffe.Extension):
|
|
324
338
|
pkg,
|
325
339
|
top_level_public_api=self.top_level_public_api,
|
326
340
|
builtin_modules=self.config.builtin_modules,
|
327
|
-
|
328
|
-
extra_public_modules=self.config.extra_public_modules,
|
341
|
+
extra_public_objects=self.config.extra_public_objects,
|
329
342
|
)
|
330
343
|
|
331
344
|
def use_public_name(context: None | dict, obj: Any) -> None | wl.AbstractDoc:
|
@@ -342,21 +355,34 @@ class HippogriffeExtension(griffe.Extension):
|
|
342
355
|
new_path, _ = public_api[f"{obj.__module__}.{obj.__qualname__}"]
|
343
356
|
return wl.TextDoc(new_path)
|
344
357
|
|
345
|
-
public_modules = set(self.config.extra_public_modules) | set(
|
346
|
-
self.config.stdlib_modules
|
347
|
-
)
|
348
358
|
for obj in public_api:
|
349
359
|
if obj.is_function:
|
350
360
|
assert type(obj) is griffe.Function
|
351
|
-
|
352
|
-
|
361
|
+
try:
|
362
|
+
_pretty_fn(obj, use_public_name)
|
363
|
+
except _NotInPublicApiException as e:
|
364
|
+
# Defer error until later -- right now our `public_api` is an
|
365
|
+
# overestimation of the 'true' public API as for a public class
|
366
|
+
# `Foo` then we actually include all of its attributes in the public
|
367
|
+
# API here, even if those aren't documented.
|
368
|
+
# It's fairly common to have nonpublic annotations in nonpublic
|
369
|
+
# methods, and we shouldn't die on those now -- if the method is
|
370
|
+
# never documented then we don't need to worry. Putting this here is
|
371
|
+
# totally sneaky, it's letting jinja think this is a template and
|
372
|
+
# having it raise a TemplateNotFound error when it tries to format
|
373
|
+
# this object.
|
374
|
+
obj.extra["mkdocstrings"]["template"] = (
|
375
|
+
f"{e} This arose whilst pretty-printing `{obj.path}`. You may "
|
376
|
+
"ignore the rest of this error message, which comes from jinja."
|
377
|
+
" "
|
378
|
+
)
|
379
|
+
else:
|
380
|
+
obj.extra["mkdocstrings"]["template"] = "hippogriffe/fn.html.jinja"
|
353
381
|
elif obj.is_class:
|
354
382
|
assert type(obj) is griffe.Class
|
355
383
|
obj.extra["mkdocstrings"]["template"] = "hippogriffe/class.html.jinja"
|
356
384
|
if self.config.show_bases:
|
357
|
-
public_bases = list(
|
358
|
-
_collect_bases(obj, public_api, public_modules).items()
|
359
|
-
)
|
385
|
+
public_bases = list(_collect_bases(obj, public_api).items())
|
360
386
|
obj.extra["hippogriffe"]["public_bases"] = public_bases
|
361
387
|
|
362
388
|
if self.config.show_source_links == "none":
|
hippogriffe/_plugin.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import pathlib
|
2
2
|
import re
|
3
|
-
import sys
|
4
3
|
import typing
|
5
4
|
|
6
5
|
from mkdocs.config import Config
|
@@ -25,13 +24,8 @@ class PluginConfig(Config):
|
|
25
24
|
show_source_links = Choice(["all", "toplevel", "none"], default="toplevel")
|
26
25
|
"""Whether to include [source] links to the repo."""
|
27
26
|
|
28
|
-
|
29
|
-
"""Any third-party
|
30
|
-
|
31
|
-
stdlib_modules = Type(list, default=list(sys.stdlib_module_names))
|
32
|
-
"""A list of stdlib modules. This is concatenated with `extra_public_modules` to
|
33
|
-
form the list of external modules that are allowed in the public API
|
34
|
-
"""
|
27
|
+
extra_public_objects = Type(list, default=[])
|
28
|
+
"""Any third-party objects which are allowed in the public API."""
|
35
29
|
|
36
30
|
builtin_modules = Type(
|
37
31
|
list, default=["builtins", "collections.abc", "typing", "typing_extensions"]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hippogriffe
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: Tweaks for `mkdocstrings[python]`
|
5
5
|
Project-URL: repository, https://github.com/patrick-kidger/hippogriffe
|
6
6
|
Author-email: Patrick Kidger <contact@kidger.site>
|
@@ -265,9 +265,9 @@ plugins:
|
|
265
265
|
- hippogriffe:
|
266
266
|
show_bases: true/false
|
267
267
|
show_source_links: all/toplevel/none
|
268
|
-
|
269
|
-
- foo
|
270
|
-
- bar
|
268
|
+
extra_public_objects:
|
269
|
+
- foo.SomeClass
|
270
|
+
- bar.subpackage.some_function
|
271
271
|
```
|
272
272
|
|
273
273
|
**show_bases:**
|
@@ -278,19 +278,21 @@ If `false` then base classes will not be displayed alongside a class. Defaults t
|
|
278
278
|
|
279
279
|
Sets which objects will have links to their location in the repository (as configured via the usual MkDocs `repo_url`). If `all` then all objects will have links. If `toplevel` then just `::: somelib.value` will have links, but their members will not. If `none` then no links will be added. Defaults to `toplevel`.
|
280
280
|
|
281
|
-
**
|
281
|
+
**extra_public_objects:**
|
282
282
|
|
283
|
-
|
283
|
+
Pretty-formatting of type annotations is done strictly: every annotation must be part of the known public API, else an error will be raised. The public API is defined as the combination of:
|
284
284
|
|
285
285
|
- Everything you document using `::: yourlib.Foo`, and all of their members.
|
286
286
|
- Anything from the standard library.
|
287
|
-
- All objects belonging to any of `
|
287
|
+
- All objects belonging to any of `extra_public_objects`.
|
288
288
|
|
289
289
|
For example,
|
290
290
|
```yml
|
291
291
|
plugins:
|
292
292
|
- hippogriffe:
|
293
|
-
|
294
|
-
- jax
|
295
|
-
- torch
|
293
|
+
extra_public_objects:
|
294
|
+
- jax.Array
|
295
|
+
- torch.Tensor
|
296
296
|
```
|
297
|
+
|
298
|
+
List each object under whatever public path `somelib.Foo` that you would like it to be displayed under (and from which it must be accessilbe), not whatever private path `somelib._internal.foo.Foo` it is defined at.
|
@@ -1,12 +1,12 @@
|
|
1
1
|
hippogriffe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
hippogriffe/_extension.py,sha256=
|
3
|
-
hippogriffe/_plugin.py,sha256=
|
2
|
+
hippogriffe/_extension.py,sha256=5c5Hb4qW4v3UjGn2x2MwXB_8CI6PfLUOQ0kO0soflA0,15992
|
3
|
+
hippogriffe/_plugin.py,sha256=9omje-ROpvo24YYSIcul8XjKwuzFhV7rE9a1f3XDUws,4307
|
4
4
|
hippogriffe/assets/_hippogriffe.css,sha256=BgyZLCaOaZNgQErdUb01vpNso4ilUMmoZ5d_OeVpfRE,147
|
5
5
|
hippogriffe/templates/material/hippogriffe/class.html.jinja,sha256=5i624gIuZfnf5toH3jWclzg1qlekqIZ2ODbpzeZcULw,954
|
6
6
|
hippogriffe/templates/material/hippogriffe/fn.html.jinja,sha256=vxrWOPkPkGD3Po9hbcYlyEjsJKhJmOHic4G5t2J0djc,196
|
7
7
|
hippogriffe/templates/material/hippogriffe/hippogriffe.jinja,sha256=KxTBKY0XqiFzqTT1iqFxhN2GhmtwLT4GWc8S86zyjOc,583
|
8
|
-
hippogriffe-0.
|
9
|
-
hippogriffe-0.
|
10
|
-
hippogriffe-0.
|
11
|
-
hippogriffe-0.
|
12
|
-
hippogriffe-0.
|
8
|
+
hippogriffe-0.2.0.dist-info/METADATA,sha256=Ud_6bXxxqtLRQltCwOPUzH2MfpzAHDcNCeWMfOrpTu8,16449
|
9
|
+
hippogriffe-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
hippogriffe-0.2.0.dist-info/entry_points.txt,sha256=Et3dFNWG-biZ7XCvPI9na-buFT_hht1YT0p0JDNEQQM,158
|
11
|
+
hippogriffe-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
12
|
+
hippogriffe-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|