hippogriffe 0.1.2__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 +88 -60
- hippogriffe/_plugin.py +2 -8
- {hippogriffe-0.1.2.dist-info → hippogriffe-0.2.0.dist-info}/METADATA +13 -11
- {hippogriffe-0.1.2.dist-info → hippogriffe-0.2.0.dist-info}/RECORD +7 -7
- {hippogriffe-0.1.2.dist-info → hippogriffe-0.2.0.dist-info}/WHEEL +0 -0
- {hippogriffe-0.1.2.dist-info → hippogriffe-0.2.0.dist-info}/entry_points.txt +0 -0
- {hippogriffe-0.1.2.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,60 +34,77 @@ 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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
item, force_public = agenda.pop()
|
49
|
-
seen.add(item)
|
50
|
-
# Skip private elements
|
51
|
-
if item.name.startswith("_") and not (
|
52
|
-
item.name.startswith("__") and item.name.endswith("__")
|
53
|
-
):
|
54
|
-
continue
|
55
|
-
if isinstance(item, griffe.Alias):
|
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:]
|
56
48
|
try:
|
57
|
-
|
58
|
-
except
|
59
|
-
continue
|
60
|
-
if item.name != final_item.name:
|
61
|
-
# Renaming during import counts as private.
|
62
|
-
# (In particular this happens for backward compatibility, e.g.
|
63
|
-
# `equinox.nn.inference_mode` and `equinox.tree_inference`.)
|
49
|
+
object = importlib.import_module(module_name)
|
50
|
+
except Exception:
|
64
51
|
continue
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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)
|
60
|
+
# Don't infinite loop on cycles. We only store Objects, and not Aliases, as in
|
61
|
+
# cycles then the aliases with be distinct: `X.Y.X.Y` is not `X.Y`, though the
|
62
|
+
# underlying object is the same.
|
63
|
+
|
64
|
+
agenda: list[tuple[griffe.Object, str, bool]] = [(pkg, pkg.path, False)]
|
65
|
+
seen: set[griffe.Object] = {pkg}
|
66
|
+
while len(agenda) > 0:
|
67
|
+
item, public_path, force_public = agenda.pop()
|
68
|
+
toplevel_public = public_path in top_level_public_api
|
69
69
|
if force_public or toplevel_public:
|
70
70
|
# If we're in the public API, then we consider all of our children to be
|
71
71
|
# in it as well... (this saves us from having to parse out `filters` and
|
72
72
|
# `members` from our documentation)
|
73
|
-
agenda.extend(
|
74
|
-
(x, True) for x in item.all_members.values() if x not in seen
|
75
|
-
)
|
76
73
|
try:
|
77
|
-
paths = self._data[
|
74
|
+
paths = self._data[item.path]
|
78
75
|
except KeyError:
|
79
|
-
paths = self._data[
|
80
|
-
paths.append(
|
81
|
-
self._objects.add(
|
76
|
+
paths = self._data[item.path] = []
|
77
|
+
paths.append(public_path)
|
78
|
+
self._objects.add(item)
|
82
79
|
if toplevel_public:
|
83
|
-
self._toplevel_objects.add(
|
80
|
+
self._toplevel_objects.add(item)
|
81
|
+
sub_force_public = True
|
84
82
|
else:
|
85
83
|
# ...if we're not in the public API then check our members -- some of
|
86
84
|
# them might be in the public API.
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
sub_force_public = False
|
86
|
+
for member in item.all_members.values():
|
87
|
+
# Skip private elements
|
88
|
+
if member.name.startswith("_") and not (
|
89
|
+
member.name.startswith("__") and member.name.endswith("__")
|
90
|
+
):
|
91
|
+
continue
|
92
|
+
if isinstance(member, griffe.Alias):
|
93
|
+
try:
|
94
|
+
final_member = member.final_target
|
95
|
+
except griffe.AliasResolutionError:
|
96
|
+
continue
|
97
|
+
if member.name != final_member.name:
|
98
|
+
# Renaming during import counts as private.
|
99
|
+
# (In particular this happens for backward compatibility, e.g.
|
100
|
+
# `equinox.nn.inference_mode` and `equinox.tree_inference`.)
|
101
|
+
continue
|
102
|
+
else:
|
103
|
+
final_member = member
|
104
|
+
if final_member in seen:
|
105
|
+
continue
|
106
|
+
agenda.append((final_member, member.path, sub_force_public))
|
107
|
+
seen.add(final_member)
|
90
108
|
|
91
109
|
def toplevel(self) -> Iterable[griffe.Object]:
|
92
110
|
return self._toplevel_objects
|
@@ -101,16 +119,17 @@ class _PublicApi:
|
|
101
119
|
for m in self._builtin_modules:
|
102
120
|
if key.startswith(m + "."):
|
103
121
|
return key.removeprefix(m + "."), False
|
104
|
-
for m in
|
122
|
+
for m in sys.stdlib_module_names:
|
105
123
|
if key.startswith(m + "."):
|
106
124
|
return key, False
|
107
|
-
#
|
125
|
+
# Note that this message must not have any newlines in it, to display
|
126
|
+
# correctly.
|
108
127
|
raise _NotInPublicApiException(
|
109
|
-
f"Tried and failed to find {key} in the public API. Commons reasons "
|
110
|
-
"for this error are
|
111
|
-
"
|
112
|
-
"
|
113
|
-
"
|
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 "
|
114
133
|
"`::: somelib.Foo:` with a trailing colon, when just `:::somelib.Foo` "
|
115
134
|
"is correct."
|
116
135
|
) from e
|
@@ -209,20 +228,17 @@ def _resolved_bases(cls: griffe.Class) -> list[str | griffe.Object]:
|
|
209
228
|
return resolved_bases
|
210
229
|
|
211
230
|
|
212
|
-
def _collect_bases(
|
213
|
-
cls: griffe.Class, public_api: _PublicApi, public_modules: set[str]
|
214
|
-
) -> dict[str, bool]:
|
231
|
+
def _collect_bases(cls: griffe.Class, public_api: _PublicApi) -> dict[str, bool]:
|
215
232
|
bases: dict[str, bool] = {}
|
216
233
|
for base in _resolved_bases(cls):
|
217
234
|
if isinstance(base, str):
|
218
235
|
# builtins case above
|
219
|
-
|
220
|
-
bases[base] = False
|
236
|
+
bases[base] = False
|
221
237
|
elif isinstance(base, griffe.Class):
|
222
238
|
try:
|
223
239
|
base, autoref = public_api[base.path]
|
224
240
|
except _NotInPublicApiException:
|
225
|
-
bases.update(_collect_bases(base, public_api
|
241
|
+
bases.update(_collect_bases(base, public_api))
|
226
242
|
else:
|
227
243
|
bases[base] = autoref
|
228
244
|
return bases
|
@@ -322,8 +338,7 @@ class HippogriffeExtension(griffe.Extension):
|
|
322
338
|
pkg,
|
323
339
|
top_level_public_api=self.top_level_public_api,
|
324
340
|
builtin_modules=self.config.builtin_modules,
|
325
|
-
|
326
|
-
extra_public_modules=self.config.extra_public_modules,
|
341
|
+
extra_public_objects=self.config.extra_public_objects,
|
327
342
|
)
|
328
343
|
|
329
344
|
def use_public_name(context: None | dict, obj: Any) -> None | wl.AbstractDoc:
|
@@ -340,21 +355,34 @@ class HippogriffeExtension(griffe.Extension):
|
|
340
355
|
new_path, _ = public_api[f"{obj.__module__}.{obj.__qualname__}"]
|
341
356
|
return wl.TextDoc(new_path)
|
342
357
|
|
343
|
-
public_modules = set(self.config.extra_public_modules) | set(
|
344
|
-
self.config.stdlib_modules
|
345
|
-
)
|
346
358
|
for obj in public_api:
|
347
359
|
if obj.is_function:
|
348
360
|
assert type(obj) is griffe.Function
|
349
|
-
|
350
|
-
|
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"
|
351
381
|
elif obj.is_class:
|
352
382
|
assert type(obj) is griffe.Class
|
353
383
|
obj.extra["mkdocstrings"]["template"] = "hippogriffe/class.html.jinja"
|
354
384
|
if self.config.show_bases:
|
355
|
-
public_bases = list(
|
356
|
-
_collect_bases(obj, public_api, public_modules).items()
|
357
|
-
)
|
385
|
+
public_bases = list(_collect_bases(obj, public_api).items())
|
358
386
|
obj.extra["hippogriffe"]["public_bases"] = public_bases
|
359
387
|
|
360
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,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hippogriffe
|
3
|
-
Version: 0.
|
4
|
-
Summary:
|
3
|
+
Version: 0.2.0
|
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>
|
7
7
|
License: Apache License
|
@@ -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
|