|
10 | 10 | # add these directories to sys.path here. If the directory is relative to the
|
11 | 11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
|
12 | 12 |
|
| 13 | +import re |
13 | 14 | import os
|
14 | 15 | import sys
|
| 16 | +import shutil |
15 | 17 |
|
16 | 18 | ROOT_DIR = os.path.abspath(os.path.join(__file__, "..", ".."))
|
17 | 19 | sys.path.insert(0, ROOT_DIR)
|
|
22 | 24 | import wgpu.gui # noqa: E402
|
23 | 25 |
|
24 | 26 |
|
25 |
| -# -- Tweak wgpu's docs ------------------------------------------------------- |
| 27 | +# -- Tests ------------------------------------------------------------------- |
26 | 28 |
|
27 |
| -# Ensure that all classes are in the docs |
28 |
| -with open(os.path.join(ROOT_DIR, "docs", "reference_classes.rst"), "rb") as f: |
29 |
| - classes_text = f.read().decode() |
30 |
| -for cls_name in wgpu.base.__all__: |
31 |
| - expected = f".. autoclass:: {cls_name}" |
32 |
| - assert ( |
33 |
| - expected in classes_text |
34 |
| - ), f"Missing doc entry {cls_name} in reference_classes.rst" |
35 |
| - |
36 |
| -# Ensure that all classes are references in the alphabetic list, and referenced at least one other time |
37 |
| -with open(os.path.join(ROOT_DIR, "docs", "reference_wgpu.rst"), "rb") as f: |
| 29 | +# Ensure that all classes are references in the alphabetic list, |
| 30 | +# and referenced at least one other time as part of the explanatory text. |
| 31 | +with open(os.path.join(ROOT_DIR, "docs", "wgpu.rst"), "rb") as f: |
38 | 32 | wgpu_text = f.read().decode()
|
| 33 | + wgpu_lines = [line.strip() for line in wgpu_text.splitlines()] |
39 | 34 | for cls_name in wgpu.base.__all__:
|
40 |
| - expected1 = f":class:`{cls_name}`" |
41 |
| - expected2 = f"* :class:`{cls_name}`" |
42 |
| - assert expected2 in wgpu_text, f"Missing doc entry {cls_name} in reference_wgpu.rst" |
43 | 35 | assert (
|
44 |
| - wgpu_text.count(expected1) >= 2 |
45 |
| - ), f"Need at least one reference to {cls_name} in reference_wgpu.rst" |
| 36 | + f"~{cls_name}" in wgpu_lines |
| 37 | + ), f"Class {cls_name} not listed in class list in wgpu.rst" |
| 38 | + assert ( |
| 39 | + f":class:`{cls_name}`" in wgpu_text |
| 40 | + ), f"Class {cls_name} not referenced in the text in wgpu.rst" |
| 41 | + |
46 | 42 |
|
47 |
| -# Make flags and enum appear better in docs |
| 43 | +# -- Hacks to tweak docstrings ----------------------------------------------- |
| 44 | + |
| 45 | +# Make flags and enums appear better in docs |
48 | 46 | wgpu.enums._use_sphinx_repr = True
|
49 | 47 | wgpu.flags._use_sphinx_repr = True
|
50 |
| - |
51 |
| -# Also tweak docstrings of classes and their methods |
52 |
| -for cls_name, cls in wgpu.base.__dict__.items(): |
53 |
| - if cls_name not in wgpu.base.__all__: |
54 |
| - continue |
55 |
| - |
56 |
| - # Change class docstring to include a link to the base class, |
57 |
| - # and the class' signature is not shown |
58 |
| - base_info = "" |
59 |
| - base_classes = [f":class:`.{c.__name__}`" for c in cls.mro()[1:-1]] |
60 |
| - if base_classes: |
61 |
| - base_info = f" *Subclass of* {', '.join(base_classes)}\n\n" |
62 |
| - cls.__doc__ = cls.__name__ + "()\n\n" + base_info + " " + cls.__doc__.lstrip() |
63 |
| - # Change docstring of methods that dont have positional arguments |
64 |
| - for method in cls.__dict__.values(): |
65 |
| - if not (callable(method) and hasattr(method, "__code__")): |
66 |
| - continue |
67 |
| - if method.__code__.co_argcount == 1 and method.__code__.co_kwonlyargcount > 0: |
68 |
| - sig = method.__name__ + "(**parameters)" |
69 |
| - method.__doc__ = sig + "\n\n " + method.__doc__.lstrip() |
| 48 | +wgpu.structs._use_sphinx_repr = True |
| 49 | + |
| 50 | +# Build regular expressions to resolve crossrefs |
| 51 | +func_ref_pattern = re.compile(r"\ (`\w+?\(\)`)", re.MULTILINE) |
| 52 | +ob_ref_pattern = re.compile( |
| 53 | + r"\ (`(GPU|gui\.Wgpu|flags\.|enums\.|structs\.)\w+?`)", re.MULTILINE |
| 54 | +) |
| 55 | +argtype_ref_pattern = re.compile( |
| 56 | + r"\(((GPU|gui\.Wgpu|flags\.|enums\.|structs\.)\w+?)\)", re.MULTILINE |
| 57 | +) |
| 58 | + |
| 59 | + |
| 60 | +def resolve_crossrefs(text): |
| 61 | + text = (text or "").lstrip() |
| 62 | + |
| 63 | + # Turn references to functions into a crossref. |
| 64 | + # E.g. `Foo.bar()` |
| 65 | + i2 = 0 |
| 66 | + while True: |
| 67 | + m = func_ref_pattern.search(text, i2) |
| 68 | + if not m: |
| 69 | + break |
| 70 | + i1, i2 = m.start(1), m.end(1) |
| 71 | + ref_indicator = ":func:" |
| 72 | + text = text[:i1] + ref_indicator + text[i1:] |
| 73 | + |
| 74 | + # Turn references to objects (classes, flags, enums, and structs) into a crossref. |
| 75 | + # E.g. `GPUDevice` or `flags.BufferUsage` |
| 76 | + i2 = 0 |
| 77 | + while True: |
| 78 | + m = ob_ref_pattern.search(text, i2) |
| 79 | + if not m: |
| 80 | + break |
| 81 | + i1, i2 = m.start(1), m.end(1) |
| 82 | + prefix = m.group(2) # e.g. GPU or flags. |
| 83 | + ref_indicator = ":obj:" if prefix.lower() == prefix else ":class:" |
| 84 | + text = text[:i1] + ref_indicator + text[i1:] |
| 85 | + |
| 86 | + # Turn function arg types into a crossref. |
| 87 | + # E.g. (GPUDevice) or (flags.BufferUsage) |
| 88 | + i2 = 0 |
| 89 | + while True: |
| 90 | + m = argtype_ref_pattern.search(text) |
| 91 | + if not m: |
| 92 | + break |
| 93 | + i1, i2 = m.start(1), m.end(1) |
| 94 | + ref_indicator = ":obj:" |
| 95 | + text = text[:i1] + ref_indicator + "`" + text[i1:i2] + "`" + text[i2:] |
| 96 | + |
| 97 | + return text |
| 98 | + |
| 99 | + |
| 100 | +# Tweak docstrings of classes and their methods |
| 101 | +for module, hide_class_signature in [(wgpu.base, True), (wgpu.gui, False)]: |
| 102 | + for cls_name in module.__all__: |
| 103 | + cls = getattr(module, cls_name) |
| 104 | + # Class docstring |
| 105 | + docs = resolve_crossrefs(cls.__doc__) |
| 106 | + if hide_class_signature: |
| 107 | + docs = cls.__name__ + "()\n\n " + docs |
| 108 | + cls.__doc__ = docs or None |
| 109 | + # Docstring of methods |
| 110 | + for method in cls.__dict__.values(): |
| 111 | + if callable(method) and hasattr(method, "__code__"): |
| 112 | + docs = resolve_crossrefs(method.__doc__) |
| 113 | + if ( |
| 114 | + method.__code__.co_argcount == 1 |
| 115 | + and method.__code__.co_kwonlyargcount > 0 |
| 116 | + ): |
| 117 | + sig = method.__name__ + "(**parameters)" |
| 118 | + docs = sig + "\n\n " + docs |
| 119 | + method.__doc__ = docs or None |
70 | 120 |
|
71 | 121 |
|
72 | 122 | # -- Project information -----------------------------------------------------
|
73 | 123 |
|
74 | 124 | project = "wgpu-py"
|
75 |
| -copyright = "2020-2022, Almar Klein, Korijn van Golen" |
| 125 | +copyright = "2020-2023, Almar Klein, Korijn van Golen" |
76 | 126 | author = "Almar Klein, Korijn van Golen"
|
77 | 127 | release = wgpu.__version__
|
78 | 128 |
|
|
85 | 135 | extensions = [
|
86 | 136 | "sphinx.ext.autodoc",
|
87 | 137 | "sphinx.ext.napoleon",
|
| 138 | + "sphinx.ext.autosummary", |
88 | 139 | ]
|
89 | 140 |
|
90 | 141 | # Add any paths that contain templates here, relative to this directory.
|
91 | 142 | templates_path = ["_templates"]
|
92 | 143 |
|
| 144 | +# Just let autosummary produce a new version each time |
| 145 | +shutil.rmtree(os.path.join(os.path.dirname(__file__), "generated"), True) |
| 146 | + |
93 | 147 | # List of patterns, relative to source directory, that match files and
|
94 | 148 | # directories to ignore when looking for source files.
|
95 | 149 | # This pattern also affects html_static_path and html_extra_path.
|
|
102 | 156 |
|
103 | 157 | # The theme to use for HTML and HTML Help pages. See the documentation for
|
104 | 158 | # a list of builtin themes.
|
105 |
| -# |
106 |
| -# html_theme = "sphinx_rtd_theme" |
| 159 | + |
| 160 | +if not (os.getenv("READTHEDOCS") or os.getenv("CI")): |
| 161 | + html_theme = "sphinx_rtd_theme" |
107 | 162 |
|
108 | 163 | # Add any paths that contain custom static files (such as style sheets) here,
|
109 | 164 | # relative to this directory. They are copied after the builtin static files,
|
|
0 commit comments