cpdocs 0.3.5__tar.gz → 0.3.6__tar.gz

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.
Files changed (168) hide show
  1. {cpdocs-0.3.5 → cpdocs-0.3.6}/PKG-INFO +1 -1
  2. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/__init__.py +1 -1
  3. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/build.py +7 -6
  4. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/cli.py +3 -1
  5. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/config.py +0 -1
  6. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/contract.py +5 -6
  7. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/cpp/render.py +3 -1
  8. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/fortran/extract.py +3 -1
  9. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/fortran/render.py +3 -1
  10. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/rust/extract.py +1 -0
  11. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/rust/render.py +3 -1
  12. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/model.py +3 -1
  13. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/plugins/properdocs/plugin.py +2 -5
  14. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/providers/cargo.py +8 -4
  15. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/rendering/common.py +3 -1
  16. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/rendering/html.py +30 -23
  17. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_api_reference.py +8 -2
  18. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_build.py +4 -2
  19. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_config.py +16 -4
  20. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_examples.py +8 -18
  21. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_feature_set_signatures.py +2 -2
  22. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_properdocs_core.py +1 -3
  23. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_rust_example.py +9 -20
  24. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_rust_provider.py +31 -23
  25. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_versioning.py +6 -1
  26. {cpdocs-0.3.5 → cpdocs-0.3.6}/.github/workflows/docs.yml +0 -0
  27. {cpdocs-0.3.5 → cpdocs-0.3.6}/.github/workflows/test.yml +0 -0
  28. {cpdocs-0.3.5 → cpdocs-0.3.6}/.gitignore +0 -0
  29. {cpdocs-0.3.5 → cpdocs-0.3.6}/.reuse/templates/bordered.jinja2 +0 -0
  30. {cpdocs-0.3.5 → cpdocs-0.3.6}/LICENSES/Apache-2.0.txt +0 -0
  31. {cpdocs-0.3.5 → cpdocs-0.3.6}/REUSE.toml +0 -0
  32. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/api/index.md +0 -0
  33. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/cpdocs.yml +0 -0
  34. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/examples/index.md +0 -0
  35. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/explanation/architecture.md +0 -0
  36. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/explanation/feature-sets.md +0 -0
  37. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/how-to/ci.md +0 -0
  38. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/how-to/feature-sets.md +0 -0
  39. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/how-to/generated-headers.md +0 -0
  40. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/how-to/other-build-systems.md +0 -0
  41. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/how-to/properdocs.md +0 -0
  42. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/index.md +0 -0
  43. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/api.md +0 -0
  44. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/cli.md +0 -0
  45. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/cmake.md +0 -0
  46. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/comments.md +0 -0
  47. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/configuration.md +0 -0
  48. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/contract.md +0 -0
  49. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/extraction.md +0 -0
  50. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/reference/links.md +0 -0
  51. {cpdocs-0.3.5 → cpdocs-0.3.6}/docs/web/tutorials/first-reference.md +0 -0
  52. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/.gitignore +0 -0
  53. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/CMakeLists.txt +0 -0
  54. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/docs/api/index.md +0 -0
  55. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/docs/cpdocs.yml +0 -0
  56. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/docs/web/api.md +0 -0
  57. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/docs/web/index.md +0 -0
  58. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/include/acme/math.hpp +0 -0
  59. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/properdocs.yml +0 -0
  60. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/readme.md +0 -0
  61. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/basic-cpp/src/math.cpp +0 -0
  62. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/.gitignore +0 -0
  63. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/CMakeLists.txt +0 -0
  64. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/docs/api/index.md +0 -0
  65. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/docs/cpdocs.yml +0 -0
  66. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/docs/web/api.md +0 -0
  67. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/docs/web/index.md +0 -0
  68. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/include/ring/ring.h +0 -0
  69. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/properdocs.yml +0 -0
  70. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/readme.md +0 -0
  71. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/c-basic/src/ring.c +0 -0
  72. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/.gitignore +0 -0
  73. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/CMakeLists.txt +0 -0
  74. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/docs/api/index.md +0 -0
  75. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/docs/cpdocs-minibuild.yml +0 -0
  76. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/docs/cpdocs.yml +0 -0
  77. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/docs/web/api.md +0 -0
  78. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/docs/web/index.md +0 -0
  79. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/include/demo/solver.hpp +0 -0
  80. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/properdocs.yml +0 -0
  81. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/readme.md +0 -0
  82. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-features/tools/minibuild.py +0 -0
  83. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/.gitignore +0 -0
  84. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/CMakeLists.txt +0 -0
  85. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/docs/api/index.md +0 -0
  86. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/docs/cpdocs.yml +0 -0
  87. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/docs/web/api.md +0 -0
  88. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/docs/web/index.md +0 -0
  89. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/include/meta/meta.hpp +0 -0
  90. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/properdocs.yml +0 -0
  91. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/cpp-templates/readme.md +0 -0
  92. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/CMakeLists.txt +0 -0
  93. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/docs/api.md +0 -0
  94. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/docs/cpdocs.yml +0 -0
  95. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/docs/index.md +0 -0
  96. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/lib/normalize.f90 +0 -0
  97. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/mod/geometry.f90 +0 -0
  98. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/properdocs.yml +0 -0
  99. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/fortran-basic/readme.md +0 -0
  100. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/.gitignore +0 -0
  101. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/CMakeLists.txt +0 -0
  102. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/docs/api/index.md +0 -0
  103. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/docs/cpdocs.yml +0 -0
  104. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/docs/web/api.md +0 -0
  105. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/docs/web/index.md +0 -0
  106. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/generated-template/generated.hpp +0 -0
  107. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/include/acme/core.hpp +0 -0
  108. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/properdocs.yml +0 -0
  109. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/readme.md +0 -0
  110. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/generated-cpp/src/generated.cpp +0 -0
  111. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/.gitignore +0 -0
  112. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/CMakeLists.txt +0 -0
  113. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/docs/api/index.md +0 -0
  114. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/docs/cpdocs.yml +0 -0
  115. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/docs/web/api.md +0 -0
  116. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/docs/web/index.md +0 -0
  117. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/include/geom/vector.hpp +0 -0
  118. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/properdocs.yml +0 -0
  119. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/python/pygeom/__init__.py +0 -0
  120. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/readme.md +0 -0
  121. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/mixed/src/geom.cpp +0 -0
  122. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/.gitignore +0 -0
  123. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/docs/api/index.md +0 -0
  124. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/docs/cpdocs.yml +0 -0
  125. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/docs/web/api.md +0 -0
  126. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/docs/web/index.md +0 -0
  127. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/properdocs.yml +0 -0
  128. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/pyproject.toml +0 -0
  129. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/readme.md +0 -0
  130. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/src/dice/__init__.py +0 -0
  131. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/src/dice/_rng.py +0 -0
  132. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/python-basic/src/dice/core.py +0 -0
  133. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/Cargo.lock +0 -0
  134. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/Cargo.toml +0 -0
  135. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/docs/api.md +0 -0
  136. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/docs/cpdocs.yml +0 -0
  137. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/docs/index.md +0 -0
  138. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/properdocs.yml +0 -0
  139. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/readme.md +0 -0
  140. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/rust-toolchain.toml +0 -0
  141. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/src/geometry.rs +0 -0
  142. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/src/internal.rs +0 -0
  143. {cpdocs-0.3.5 → cpdocs-0.3.6}/examples/rust-basic/src/lib.rs +0 -0
  144. {cpdocs-0.3.5 → cpdocs-0.3.6}/properdocs.yml +0 -0
  145. {cpdocs-0.3.5 → cpdocs-0.3.6}/pyproject.toml +0 -0
  146. {cpdocs-0.3.5 → cpdocs-0.3.6}/readme.md +0 -0
  147. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/__main__.py +0 -0
  148. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/__init__.py +0 -0
  149. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/base.py +0 -0
  150. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/cpp/__init__.py +0 -0
  151. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/cpp/extract.py +0 -0
  152. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/fortran/__init__.py +0 -0
  153. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/python/__init__.py +0 -0
  154. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/python/extract.py +0 -0
  155. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/python/render.py +0 -0
  156. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/languages/rust/__init__.py +0 -0
  157. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/plugins/__init__.py +0 -0
  158. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/plugins/properdocs/__init__.py +0 -0
  159. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/plugins/properdocs/core.py +0 -0
  160. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/providers/__init__.py +0 -0
  161. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/providers/cmake.py +0 -0
  162. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/providers/contract.py +0 -0
  163. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/providers/python.py +0 -0
  164. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/rendering/__init__.py +0 -0
  165. {cpdocs-0.3.5 → cpdocs-0.3.6}/src/cpdocs/versioning.py +0 -0
  166. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/conftest.py +0 -0
  167. {cpdocs-0.3.5 → cpdocs-0.3.6}/test/test_contract.py +0 -0
  168. {cpdocs-0.3.5 → cpdocs-0.3.6}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cpdocs
3
- Version: 0.3.5
3
+ Version: 0.3.6
4
4
  Summary: Semantic, versioned API documentation for C, C++, CUDA, HIP, Fortran, Python and Rust projects
5
5
  Project-URL: Source, https://github.com/ambhora/cpdocs
6
6
  Author-email: Jayesh Badwaik <j.badwaik@fz-juelich.de>
@@ -4,4 +4,4 @@
4
4
  # --------------------------------------------------------------------------------------------------
5
5
  """Language-neutral semantic API documentation generator."""
6
6
 
7
- __version__ = "0.3.5"
7
+ __version__ = "0.3.6"
@@ -46,7 +46,7 @@ class Layout:
46
46
  @classmethod
47
47
  def for_output(cls, output: Path, *, tmp_directory: Path | None = None) -> Layout:
48
48
  root = (output / "cpdocs").resolve()
49
- work_root = (tmp_directory.resolve() / "cpdocs" if tmp_directory else root)
49
+ work_root = tmp_directory.resolve() / "cpdocs" if tmp_directory else root
50
50
  return cls(root=root, work_root=work_root)
51
51
 
52
52
  @property
@@ -61,7 +61,6 @@ class Layout:
61
61
  return self.work_root / "work" / ref.replace("/", "__")
62
62
 
63
63
 
64
-
65
64
  # --------------------------------------------------------------------------------------------------
66
65
  # Extraction
67
66
  # --------------------------------------------------------------------------------------------------
@@ -139,7 +138,11 @@ def _api_display_paths(
139
138
  root_relative = path.name
140
139
  project_relative = relative_source_path(path, project_root)
141
140
  logical = next(
142
- (candidate for candidate in (root_relative, project_relative) if candidate in graph.sources),
141
+ (
142
+ candidate
143
+ for candidate in (root_relative, project_relative)
144
+ if candidate in graph.sources
145
+ ),
143
146
  None,
144
147
  )
145
148
  if logical is not None:
@@ -619,9 +622,7 @@ def build_versions(
619
622
  )
620
623
  ref_config = _config_for_checkout(config, checkout) if ref.checkout else config
621
624
  work = layout.work(ref.name)
622
- graph = build_graph(
623
- ref_config, source=checkout, work=work, version=ref.name
624
- )
625
+ graph = build_graph(ref_config, source=checkout, work=work, version=ref.name)
625
626
  _render(
626
627
  graph,
627
628
  ref_config,
@@ -55,7 +55,9 @@ def _parser() -> argparse.ArgumentParser:
55
55
  python_feature_sets = phases.add_parser("feature-sets", help="write feature-sets.json")
56
56
  python_feature_sets.add_argument("--source", type=_path, required=True)
57
57
  python_feature_sets.add_argument("--output", type=_path, required=True)
58
- python_manifest = phases.add_parser("build", help="build one feature set and write manifest.json")
58
+ python_manifest = phases.add_parser(
59
+ "build", help="build one feature set and write manifest.json"
60
+ )
59
61
  python_manifest.add_argument("--source", type=_path, required=True)
60
62
  python_manifest.add_argument("--feature-set", type=_path, required=True)
61
63
  python_manifest.add_argument("--output", type=_path, required=True)
@@ -66,7 +66,6 @@ def _strings(value: object, name: str, *, default: tuple[str, ...] = ()) -> tupl
66
66
  return result
67
67
 
68
68
 
69
-
70
69
  def _versions(value: object) -> tuple[str, ...]:
71
70
  versions = _strings(value, "versions", default=("main",))
72
71
  if not versions:
@@ -160,7 +160,9 @@ def read_manifest(path: Path, *, source: Path) -> Manifest:
160
160
  if raw.get("cpdocs-manifest") != CONTRACT_VERSION:
161
161
  raise ContractError(f"{path}: unsupported cpdocs-manifest {raw.get('cpdocs-manifest')!r}")
162
162
  option_metadata = raw.get("options", [])
163
- if not isinstance(option_metadata, list) or any(not isinstance(x, dict) for x in option_metadata):
163
+ if not isinstance(option_metadata, list) or any(
164
+ not isinstance(x, dict) for x in option_metadata
165
+ ):
164
166
  raise ContractError(f"{path}: options must be a list of objects")
165
167
  declarations = _units(raw.get("units"), path)
166
168
  units: list[UnitManifest] = []
@@ -202,8 +204,7 @@ def read_manifest(path: Path, *, source: Path) -> Manifest:
202
204
  )
203
205
  options = _mapping(data.get("parse", {}), f"{where}.parse", path)
204
206
  parser_language = str(
205
- options.get("language")
206
- or ("c" if declaration.language == "c" else "cpp")
207
+ options.get("language") or ("c" if declaration.language == "c" else "cpp")
207
208
  )
208
209
  if parser_language not in PARSER_LANGUAGES:
209
210
  raise ContractError(
@@ -238,9 +239,7 @@ def read_manifest(path: Path, *, source: Path) -> Manifest:
238
239
  # Fortran modules carry their public declarations in source files rather than headers.
239
240
  # Build-system manifests expose module roots through the semantic ``modules`` field.
240
241
  if not api and not modules:
241
- raise ContractError(
242
- f"{path}: {where}: a Fortran unit needs an API or modules root"
243
- )
242
+ raise ContractError(f"{path}: {where}: a Fortran unit needs an API or modules root")
244
243
 
245
244
  units.append(
246
245
  UnitManifest(
@@ -45,7 +45,9 @@ def entity_declaration(entity: ApiEntity) -> str:
45
45
 
46
46
  def signature_spelling(entity: ApiEntity, signature: ApiSignature) -> str:
47
47
  attribute_spellings = list(
48
- dict.fromkeys(attribute.spelling for attribute in signature.attributes if attribute.spelling)
48
+ dict.fromkeys(
49
+ attribute.spelling for attribute in signature.attributes if attribute.spelling
50
+ )
49
51
  )
50
52
  attribute_prefix = " ".join(attribute_spellings)
51
53
  if signature.spelling:
@@ -259,7 +259,9 @@ def extract_fortran_graph(
259
259
  for value in arguments
260
260
  )
261
261
  kind = "function" if kind_text.casefold() == "function" else "subroutine"
262
- returns = _declaration_type(prefix) if kind_text.casefold() == "function" else ""
262
+ returns = (
263
+ _declaration_type(prefix) if kind_text.casefold() == "function" else ""
264
+ )
263
265
  qualified = f"{module_name}::{name}"
264
266
  signature = ApiSignature(
265
267
  parameters=parameters,
@@ -54,7 +54,9 @@ RENDERING = LanguageRendering(
54
54
  type_like_kinds=frozenset({"derived_type"}),
55
55
  function_kinds=frozenset({"function", "subroutine"}),
56
56
  member_value_kinds=frozenset({"variable", "attribute", "constant"}),
57
- source_suffixes=frozenset({".f", ".for", ".ftn", ".f77", ".f90", ".f95", ".f03", ".f08", ".f18"}),
57
+ source_suffixes=frozenset(
58
+ {".f", ".for", ".ftn", ".f77", ".f90", ".f95", ".f03", ".f08", ".f18"}
59
+ ),
58
60
  kind_markers={
59
61
  "module": "M",
60
62
  "derived_type": "T",
@@ -249,6 +249,7 @@ def _ownership(index: dict[str, object]) -> tuple[dict[str, str], set[str], set[
249
249
 
250
250
  return owners, inherent_impl_items, trait_impl_items
251
251
 
252
+
252
253
  def build_rust_graph(
253
254
  *, rustdoc_json: Path, project: str, version: str = "main", project_root: Path | None = None
254
255
  ) -> ApiGraph:
@@ -29,7 +29,9 @@ def signature_spelling(entity: ApiEntity, signature: ApiSignature) -> str:
29
29
  modifiers = [value for value in entity.properties if value != "pub"]
30
30
  visibility = "pub " if "pub" in entity.properties else ""
31
31
  modifier_prefix = (" ".join(modifiers) + " ") if modifiers else ""
32
- value = f"{visibility}{modifier_prefix}fn {entity.qualified_name}({parameter_spelling(signature)})"
32
+ value = (
33
+ f"{visibility}{modifier_prefix}fn {entity.qualified_name}({parameter_spelling(signature)})"
34
+ )
33
35
  if signature.returns:
34
36
  value += f" -> {signature.returns}"
35
37
  return value
@@ -110,7 +110,9 @@ class ApiGraph:
110
110
  existing.signature_feature_sets.append([])
111
111
  for index, signature in enumerate(entity.signatures):
112
112
  incoming = (
113
- entity.signature_feature_sets[index] if index < len(entity.signature_feature_sets) else []
113
+ entity.signature_feature_sets[index]
114
+ if index < len(entity.signature_feature_sets)
115
+ else []
114
116
  )
115
117
  if signature not in existing.signatures:
116
118
  existing.signatures.append(signature)
@@ -164,9 +164,7 @@ class CpdocsPlugin(BasePlugin):
164
164
  def _fingerprint(self) -> _Fingerprint:
165
165
  return self._fingerprint_paths(self._watched_files())
166
166
 
167
- def _subsite_fingerprint(
168
- self, project: Path, watched_files: list[Path]
169
- ) -> _Fingerprint:
167
+ def _subsite_fingerprint(self, project: Path, watched_files: list[Path]) -> _Fingerprint:
170
168
  """Fingerprint one subsite and cpdocs itself, but not unrelated project docs."""
171
169
 
172
170
  assert self._project_root is not None
@@ -367,8 +365,7 @@ class CpdocsPlugin(BasePlugin):
367
365
  )
368
366
  if result.returncode != 0:
369
367
  raise PluginError(
370
- f"cpdocs: ProperDocs subsite build failed for {mount.as_posix()}:\n"
371
- f"{result.stdout}"
368
+ f"cpdocs: ProperDocs subsite build failed for {mount.as_posix()}:\n{result.stdout}"
372
369
  )
373
370
  _LOG.info("Built ProperDocs subsite %s", mount.as_posix())
374
371
 
@@ -75,7 +75,9 @@ def _package(metadata: dict[str, Any], source: Path, package: str | None) -> dic
75
75
  if package:
76
76
  matches = [item for item in packages if item.get("name") == package]
77
77
  if len(matches) != 1:
78
- raise RuntimeError(f"cpdocs rust: Cargo package {package!r} is not unique in the workspace")
78
+ raise RuntimeError(
79
+ f"cpdocs rust: Cargo package {package!r} is not unique in the workspace"
80
+ )
79
81
  return matches[0]
80
82
 
81
83
  root_manifest = (source / "Cargo.toml").resolve()
@@ -186,8 +188,10 @@ def _browse_roots(source: Path, package: dict[str, Any], target: dict[str, Any])
186
188
  src_path = target.get("src_path")
187
189
  if isinstance(src_path, str):
188
190
  src = Path(src_path)
189
- if src.exists() and src not in roots and not any(
190
- root.is_dir() and root in src.parents for root in roots
191
+ if (
192
+ src.exists()
193
+ and src not in roots
194
+ and not any(root.is_dir() and root in src.parents for root in roots)
191
195
  ):
192
196
  roots.append(src)
193
197
  return [_relative_or_absolute(root, source) for root in roots]
@@ -261,7 +265,7 @@ def write_manifest(
261
265
  rustdoc_json = target_dir / "doc" / f"{target_name}.json"
262
266
  if not rustdoc_json.is_file():
263
267
  raise RuntimeError(
264
- "cpdocs rust: cargo rustdoc succeeded but did not produce " f"{rustdoc_json}"
268
+ f"cpdocs rust: cargo rustdoc succeeded but did not produce {rustdoc_json}"
265
269
  )
266
270
 
267
271
  manifest = {
@@ -52,6 +52,7 @@ def split_qualified(name: str, separator: str) -> list[str]:
52
52
  def _segments(entity: ApiEntity) -> list[str]:
53
53
  return split_qualified(entity.qualified_name, rendering_for(entity.language).separator)
54
54
 
55
+
55
56
  # Operator names as words, longest spelling first, so that ``operator<<`` is not read as ``<``.
56
57
  _OPERATOR_WORDS = (
57
58
  ("<=>", "spaceship"),
@@ -482,7 +483,8 @@ def _preferred_type_target(name: str, entity: ApiEntity, graph: ApiGraph) -> Api
482
483
  candidates = [
483
484
  candidate
484
485
  for candidate in graph.entities.values()
485
- if candidate.kind in rendering_for(candidate.language).type_like_kinds and candidate.name == name
486
+ if candidate.kind in rendering_for(candidate.language).type_like_kinds
487
+ and candidate.name == name
486
488
  ]
487
489
  if not candidates:
488
490
  return None
@@ -176,7 +176,9 @@ def _kind_label(kind: str, language: str | None = None) -> str:
176
176
 
177
177
  def _kind_badge(kind: str, language: str | None = None) -> str:
178
178
  marker = html.escape(_kind_marker(kind, language))
179
- return f'<span class="api-kind" title="{html.escape(_kind_label(kind, language))}">{marker}</span>'
179
+ return (
180
+ f'<span class="api-kind" title="{html.escape(_kind_label(kind, language))}">{marker}</span>'
181
+ )
180
182
 
181
183
 
182
184
  def _language_label(language: str) -> str:
@@ -209,7 +211,7 @@ def _feature_set_inline(graph: ApiGraph, key: str) -> str:
209
211
  features = _feature_set_features(graph, key)
210
212
  if not features:
211
213
  return '<span class="api-feature-set-empty">default (no features enabled)</span>'
212
- return " ".join(f'<code>{html.escape(feature)}</code>' for feature in features)
214
+ return " ".join(f"<code>{html.escape(feature)}</code>" for feature in features)
213
215
 
214
216
 
215
217
  def _legend(graph: ApiGraph) -> str:
@@ -522,9 +524,7 @@ def _availability(
522
524
  )
523
525
  return (
524
526
  '<div class="api-availability"><strong>Available in feature sets</strong>'
525
- '<ul class="api-availability-list">'
526
- + "".join(rows)
527
- + "</ul></div>"
527
+ '<ul class="api-availability-list">' + "".join(rows) + "</ul></div>"
528
528
  )
529
529
 
530
530
 
@@ -1030,19 +1030,20 @@ def _feature_sets_page(graph: ApiGraph, document: Path) -> tuple[str, list[tuple
1030
1030
  "</tbody></table></section>",
1031
1031
  '<section id="feature-sets" class="api-section"><h2>Feature Sets</h2>',
1032
1032
  '<table class="api-feature-set-table">',
1033
- '<thead><tr><th>Feature set</th><th>Enabled features</th></tr></thead>',
1034
- '<tbody>',
1033
+ "<thead><tr><th>Feature set</th><th>Enabled features</th></tr></thead>",
1034
+ "<tbody>",
1035
1035
  ]
1036
1036
  )
1037
- toc: list[tuple[str, str, int]] = [("Overview", "overview", 1), ("Feature Sets", "feature-sets", 1)]
1037
+ toc: list[tuple[str, str, int]] = [
1038
+ ("Overview", "overview", 1),
1039
+ ("Feature Sets", "feature-sets", 1),
1040
+ ]
1038
1041
  for index, key in enumerate(graph.feature_sets, 1):
1039
1042
  anchor = _feature_set_anchor(key)
1040
1043
  body.append(
1041
1044
  f'<tr id="{html.escape(anchor)}">'
1042
1045
  f'<th scope="row">{index}</th>'
1043
- '<td>'
1044
- + _feature_set_inline(graph, key)
1045
- + '</td></tr>'
1046
+ "<td>" + _feature_set_inline(graph, key) + "</td></tr>"
1046
1047
  )
1047
1048
  body.append("</tbody></table></section>")
1048
1049
  # Option metadata are documentation of each resolved build, not extra feature axes.
@@ -1052,18 +1053,25 @@ def _feature_sets_page(graph: ApiGraph, document: Path) -> tuple[str, list[tuple
1052
1053
  first = next(iter(graph.feature_sets), None)
1053
1054
  options = configurations.get(first, [])
1054
1055
  if isinstance(options, list) and options:
1055
- body.extend([
1056
- '<section id="configuration-options" class="api-section">',
1057
- "<h2>Configuration options</h2>",
1058
- "<p>Effective CMake cache values from the first documented feature set. "
1059
- "Only project features determine the feature-set combinations above.</p>",
1060
- '<table><thead><tr><th>Group</th><th>Option</th><th>Value</th><th>Description</th></tr></thead><tbody>',
1061
- ])
1056
+ body.extend(
1057
+ [
1058
+ '<section id="configuration-options" class="api-section">',
1059
+ "<h2>Configuration options</h2>",
1060
+ "<p>Effective CMake cache values from the first documented feature set. "
1061
+ "Only project features determine the feature-set combinations above.</p>",
1062
+ "<table><thead><tr><th>Group</th><th>Option</th><th>Value</th><th>Description</th></tr></thead><tbody>",
1063
+ ]
1064
+ )
1062
1065
  for option in options:
1063
1066
  if not isinstance(option, dict):
1064
1067
  continue
1065
- cells = [str(option.get(field, "")) for field in ("group", "name", "value", "description")]
1066
- body.append("<tr>" + "".join(f"<td>{html.escape(value)}</td>" for value in cells) + "</tr>")
1068
+ cells = [
1069
+ str(option.get(field, ""))
1070
+ for field in ("group", "name", "value", "description")
1071
+ ]
1072
+ body.append(
1073
+ "<tr>" + "".join(f"<td>{html.escape(value)}</td>" for value in cells) + "</tr>"
1074
+ )
1067
1075
  body.append("</tbody></table></section>")
1068
1076
  toc.append(("Configuration options", "configuration-options", 1))
1069
1077
  return "".join(body), toc
@@ -1100,9 +1108,8 @@ def _source_page(
1100
1108
  display_path = _display_source_path(source_path, graph)
1101
1109
  generated_sources = graph.metadata.get("generated_sources", [])
1102
1110
  generated_files = graph.metadata.get("generated_files", [])
1103
- generated = (
1104
- (isinstance(generated_sources, list) and source_path in generated_sources)
1105
- or (isinstance(generated_files, list) and source_path in generated_files)
1111
+ generated = (isinstance(generated_sources, list) and source_path in generated_sources) or (
1112
+ isinstance(generated_files, list) and source_path in generated_files
1106
1113
  )
1107
1114
  note = (
1108
1115
  '<p class="api-section-intro">Generated by the build; not part of the repository.</p>'
@@ -293,7 +293,10 @@ def test_renderer_generates_standalone_code_oriented_html_site(tmp_path: Path) -
293
293
  graph.add(package)
294
294
  graph.add(macro)
295
295
  graph.sources["dice/version.hpp"] = "#pragma once\nnamespace dice { namespace meta {} }\n"
296
- graph.feature_sets["feature-set-001"] = {"features": ["cpu"], "selection": {"features": ["cpu"]}}
296
+ graph.feature_sets["feature-set-001"] = {
297
+ "features": ["cpu"],
298
+ "selection": {"features": ["cpu"]},
299
+ }
297
300
  graph.metadata = {
298
301
  "units": [{"id": "dice", "language": "cpp"}],
299
302
  "generator": "test",
@@ -395,7 +398,10 @@ def test_renderer_generates_standalone_code_oriented_html_site(tmp_path: Path) -
395
398
  assert "grid-template-columns: 1.45rem minmax(0, 1fr) auto auto" in css
396
399
  assert ".api-outline-language { min-width: 0; justify-self: end; overflow: hidden; }" in css
397
400
  assert "text-overflow: ellipsis" in css
398
- assert '<span class="api-outline-language"><span class="api-language-badge">C++</span></span>' in home_page
401
+ assert (
402
+ '<span class="api-outline-language"><span class="api-language-badge">C++</span></span>'
403
+ in home_page
404
+ )
399
405
  assert "border: 1px solid var(--api-border)" in css
400
406
  assert '"dice::foo"' in (site / "symbols.json").read_text(encoding="utf-8")
401
407
 
@@ -40,7 +40,9 @@ def test_semantic_file_layout_flattens_manifest_roots(tmp_path: Path) -> None:
40
40
  source.write_text("// source\n", encoding="utf-8")
41
41
 
42
42
  assert _semantic_display_path(header, root=include, generated=False) == "include/kprof/cuda.hpp"
43
- assert _semantic_display_path(source, root=library, generated=False) == "lib/kprof/synchronize.cu"
43
+ assert (
44
+ _semantic_display_path(source, root=library, generated=False) == "lib/kprof/synchronize.cu"
45
+ )
44
46
  assert (
45
47
  _semantic_display_path(header, root=include, generated=True)
46
48
  == "_generated/include/kprof/cuda.hpp"
@@ -86,7 +88,7 @@ def test_failing_build_command_reports_its_output(tmp_path: Path) -> None:
86
88
  (project / "docs" / "cpdocs.yml").write_text(
87
89
  "contract: 3\nbuild:\n"
88
90
  " feature-sets: >-\n python -c \"import sys; print('boom'); sys.exit(3)\"\n"
89
- " build: \"true\"\n",
91
+ ' build: "true"\n',
90
92
  encoding="utf-8",
91
93
  )
92
94
  with pytest.raises(RuntimeError, match="boom"):
@@ -81,10 +81,22 @@ checks:
81
81
  ("build:\n feature-sets: make\n build: make\n", "contract must be 3"),
82
82
  ("contract: 3\n", "build.feature-sets"),
83
83
  ("contract: 3\nbuild:\n feature-sets: [make]\n build: make\n", "command string"),
84
- ("contract: 3\nbuild:\n feature-sets: make\n build: make\n manifest: make\n", "unknown build keys"),
85
- ("contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: {tags: all}\n", "versions must be a list"),
86
- ("contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: [main, main]\n", "duplicates"),
87
- ("contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: []\n", "at least one version"),
84
+ (
85
+ "contract: 3\nbuild:\n feature-sets: make\n build: make\n manifest: make\n",
86
+ "unknown build keys",
87
+ ),
88
+ (
89
+ "contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: {tags: all}\n",
90
+ "versions must be a list",
91
+ ),
92
+ (
93
+ "contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: [main, main]\n",
94
+ "duplicates",
95
+ ),
96
+ (
97
+ "contract: 3\nbuild:\n feature-sets: make\n build: make\nversions: []\n",
98
+ "at least one version",
99
+ ),
88
100
  ],
89
101
  )
90
102
  def test_invalid_configs_are_rejected(tmp_path: Path, text: str, message: str) -> None:
@@ -50,14 +50,17 @@ def test_cpp_features_example_documents_every_feature_set(tmp_path: Path) -> Non
50
50
  return {tuple(graph.feature_sets[key]["features"]) for key in entity.feature_sets}
51
51
 
52
52
  assert feature_sets("struct", "demo::settings") == {(), ("mpi",), ("mpi", "cuda")}
53
- assert feature_sets("struct", "demo::communicator") == {( "mpi",), ("mpi", "cuda")}
54
- assert feature_sets("struct", "demo::device") == {( "mpi", "cuda")}
53
+ assert feature_sets("struct", "demo::communicator") == {("mpi",), ("mpi", "cuda")}
54
+ assert feature_sets("struct", "demo::device") == {("mpi", "cuda")}
55
55
  solve = graph.entities[stable_entity_id("cpp", "function", "demo::solve")]
56
56
  assert len(solve.signatures) == 3
57
57
  by_parameters = {
58
58
  signature.compact: (set(feature_sets), signature.documentation)
59
- for signature, feature_sets in zip(solve.signatures, solve.signature_feature_sets, strict=True)
59
+ for signature, feature_sets in zip(
60
+ solve.signatures, solve.signature_feature_sets, strict=True
61
+ )
60
62
  }
63
+
61
64
  def resolved(keys: set[str]) -> set[tuple[str, ...]]:
62
65
  return {tuple(graph.feature_sets[key]["features"]) for key in keys}
63
66
 
@@ -74,13 +77,7 @@ def test_cpp_features_example_documents_every_feature_set(tmp_path: Path) -> Non
74
77
 
75
78
  feature_sets = json.loads(
76
79
  (
77
- project
78
- / "build"
79
- / "cpdocs"
80
- / "work"
81
- / "current"
82
- / "feature-sets"
83
- / "feature-sets.json"
80
+ project / "build" / "cpdocs" / "work" / "current" / "feature-sets" / "feature-sets.json"
84
81
  ).read_text()
85
82
  )
86
83
  selected = [item["features"] for item in feature_sets["feature-sets"]]
@@ -202,14 +199,7 @@ def test_cpp_file_hierarchy_includes_implementation_sources(tmp_path: Path) -> N
202
199
  project, graph = _build(tmp_path, "basic-cpp")
203
200
  assert "src/math.cpp" in graph.files
204
201
  implementation = (
205
- project
206
- / "build"
207
- / "cpdocs"
208
- / "current"
209
- / "_sources"
210
- / "src"
211
- / "math.cpp"
212
- / "index.html"
202
+ project / "build" / "cpdocs" / "current" / "_sources" / "src" / "math.cpp" / "index.html"
213
203
  )
214
204
  assert implementation.is_file()
215
205
  assert "File: src/math.cpp" in implementation.read_text(encoding="utf-8")
@@ -16,7 +16,7 @@ from cpdocs.config import load_config
16
16
  from cpdocs.model import stable_entity_id
17
17
  from cpdocs.rendering.common import entity_document
18
18
 
19
- PROVIDER = r'''
19
+ PROVIDER = r"""
20
20
  import json, pathlib, sys
21
21
 
22
22
  command = sys.argv[1]
@@ -48,7 +48,7 @@ else:
48
48
  }
49
49
  output.parent.mkdir(parents=True, exist_ok=True)
50
50
  output.write_text(json.dumps(value), encoding="utf-8")
51
- '''
51
+ """
52
52
 
53
53
 
54
54
  def test_same_function_can_have_cpp_and_cuda_definitions(tmp_path: Path) -> None:
@@ -135,9 +135,7 @@ def test_serve_reuses_cached_subsites_until_their_inputs_change(
135
135
  del project_arg, mount, strict
136
136
  builds.append(destination)
137
137
  destination.mkdir(parents=True, exist_ok=True)
138
- (destination / "index.html").write_text(
139
- f"build {len(builds)}\n", encoding="utf-8"
140
- )
138
+ (destination / "index.html").write_text(f"build {len(builds)}\n", encoding="utf-8")
141
139
 
142
140
  monkeypatch.setattr(plugin, "_run_subsite_build", fake_build)
143
141
 
@@ -44,39 +44,28 @@ def test_rust_example_uses_disjoint_cargo_features_and_rustdoc_json(tmp_path: Pa
44
44
  ["checked"],
45
45
  ]
46
46
 
47
- vector = graph.entities[
48
- stable_entity_id("rust", "struct", "rust_geometry::geometry::Vector3")
49
- ]
47
+ vector = graph.entities[stable_entity_id("rust", "struct", "rust_geometry::geometry::Vector3")]
50
48
  assert vector.documentation == "A three-dimensional vector."
51
- dot = graph.entities[
52
- stable_entity_id("rust", "function", "rust_geometry::geometry::dot")
53
- ]
49
+ dot = graph.entities[stable_entity_id("rust", "function", "rust_geometry::geometry::dot")]
54
50
  assert dot.documentation == "Return the dot product of two vectors."
55
51
 
56
52
  dot_many = graph.entities[
57
53
  stable_entity_id("rust", "function", "rust_geometry::geometry::dot_many")
58
54
  ]
59
- assert {
60
- tuple(graph.feature_sets[key]["features"]) for key in dot_many.feature_sets
61
- } == {("batch",)}
55
+ assert {tuple(graph.feature_sets[key]["features"]) for key in dot_many.feature_sets} == {
56
+ ("batch",)
57
+ }
62
58
  checked_dot = graph.entities[
63
59
  stable_entity_id("rust", "function", "rust_geometry::geometry::checked_dot")
64
60
  ]
65
- assert {
66
- tuple(graph.feature_sets[key]["features"]) for key in checked_dot.feature_sets
67
- } == {("checked",)}
61
+ assert {tuple(graph.feature_sets[key]["features"]) for key in checked_dot.feature_sets} == {
62
+ ("checked",)
63
+ }
68
64
 
69
65
  assert "src/internal.rs" in graph.files
70
66
  assert not any(entity.name == "norm_squared" for entity in graph.entities.values())
71
67
  source_page = (
72
- project
73
- / "build"
74
- / "cpdocs"
75
- / "current"
76
- / "_sources"
77
- / "src"
78
- / "internal.rs"
79
- / "index.html"
68
+ project / "build" / "cpdocs" / "current" / "_sources" / "src" / "internal.rs" / "index.html"
80
69
  )
81
70
  assert source_page.is_file()
82
71
  assert "norm_squared" in source_page.read_text(encoding="utf-8")