cpdocs 0.3.0__tar.gz → 0.3.2__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.
- {cpdocs-0.3.0 → cpdocs-0.3.2}/PKG-INFO +1 -1
- cpdocs-0.3.2/examples/fortran-basic/docs/api.md +12 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/properdocs.yml +1 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/pyproject.toml +1 -1
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/__init__.py +1 -1
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/build.py +93 -135
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/cli.py +4 -4
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/contract.py +5 -5
- cpdocs-0.3.2/src/cpdocs/languages/__init__.py +138 -0
- cpdocs-0.3.2/src/cpdocs/languages/base.py +63 -0
- cpdocs-0.3.2/src/cpdocs/languages/cpp/__init__.py +8 -0
- cpdocs-0.3.0/src/cpdocs/clang_backend.py → cpdocs-0.3.2/src/cpdocs/languages/cpp/extract.py +75 -5
- cpdocs-0.3.2/src/cpdocs/languages/cpp/render.py +125 -0
- cpdocs-0.3.2/src/cpdocs/languages/fortran/__init__.py +8 -0
- cpdocs-0.3.0/src/cpdocs/fortran_backend.py → cpdocs-0.3.2/src/cpdocs/languages/fortran/extract.py +1 -1
- cpdocs-0.3.2/src/cpdocs/languages/fortran/render.py +70 -0
- cpdocs-0.3.2/src/cpdocs/languages/python/__init__.py +8 -0
- cpdocs-0.3.0/src/cpdocs/python_backend.py → cpdocs-0.3.2/src/cpdocs/languages/python/extract.py +1 -1
- cpdocs-0.3.2/src/cpdocs/languages/python/render.py +119 -0
- cpdocs-0.3.2/src/cpdocs/languages/rust/__init__.py +8 -0
- cpdocs-0.3.0/src/cpdocs/rust_backend.py → cpdocs-0.3.2/src/cpdocs/languages/rust/extract.py +1 -1
- cpdocs-0.3.2/src/cpdocs/languages/rust/render.py +80 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/model.py +9 -0
- cpdocs-0.3.2/src/cpdocs/plugins/__init__.py +3 -0
- cpdocs-0.3.2/src/cpdocs/plugins/properdocs/__init__.py +7 -0
- cpdocs-0.3.0/src/cpdocs/properdocs_plugin.py → cpdocs-0.3.2/src/cpdocs/plugins/properdocs/plugin.py +1 -1
- cpdocs-0.3.2/src/cpdocs/providers/__init__.py +3 -0
- cpdocs-0.3.0/src/cpdocs/rust_manifest.py → cpdocs-0.3.2/src/cpdocs/providers/cargo.py +1 -1
- cpdocs-0.3.2/src/cpdocs/providers/cmake.py +8 -0
- cpdocs-0.3.2/src/cpdocs/providers/contract.py +125 -0
- cpdocs-0.3.0/src/cpdocs/python_manifest.py → cpdocs-0.3.2/src/cpdocs/providers/python.py +1 -1
- cpdocs-0.3.2/src/cpdocs/rendering/__init__.py +7 -0
- cpdocs-0.3.0/src/cpdocs/render.py → cpdocs-0.3.2/src/cpdocs/rendering/common.py +29 -205
- cpdocs-0.3.0/src/cpdocs/html_renderer.py → cpdocs-0.3.2/src/cpdocs/rendering/html.py +73 -122
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/conftest.py +17 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_api_reference.py +21 -10
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_build.py +23 -1
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_examples.py +3 -2
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_feature_set_signatures.py +1 -2
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_properdocs_core.py +4 -4
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_rust_provider.py +3 -3
- cpdocs-0.3.0/src/cpdocs/html_renderer.py.rej +0 -12
- cpdocs-0.3.0/test/test_api_reference.py.rej +0 -9
- {cpdocs-0.3.0 → cpdocs-0.3.2}/.github/workflows/docs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/.github/workflows/test.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/LICENSES/Apache-2.0.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/REUSE.toml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/examples/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/explanation/architecture.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/explanation/feature-sets.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/how-to/ci.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/how-to/feature-sets.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/how-to/generated-headers.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/how-to/other-build-systems.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/how-to/properdocs.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/cli.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/cmake.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/comments.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/configuration.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/contract.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/extraction.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/reference/links.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/docs/web/tutorials/first-reference.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/include/acme/math.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/basic-cpp/src/math.cpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/include/ring/ring.h +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/c-basic/src/ring.c +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/docs/cpdocs-minibuild.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/include/demo/solver.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-features/tools/minibuild.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/include/meta/meta.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/cpp-templates/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/docs/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/lib/normalize.f90 +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/mod/geometry.f90 +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/fortran-basic/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/generated-template/generated.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/include/acme/core.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/generated-cpp/src/generated.cpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/CMakeLists.txt +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/include/geom/vector.hpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/python/pygeom/__init__.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/mixed/src/geom.cpp +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/.gitignore +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/docs/api/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/docs/web/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/docs/web/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/pyproject.toml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/src/dice/__init__.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/src/dice/_rng.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/python-basic/src/dice/core.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/Cargo.lock +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/Cargo.toml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/docs/api.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/docs/cpdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/docs/index.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/rust-toolchain.toml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/src/geometry.rs +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/src/internal.rs +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/examples/rust-basic/src/lib.rs +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/properdocs.yml +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/readme.md +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/__main__.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/config.py +0 -0
- /cpdocs-0.3.0/src/cpdocs/properdocs_core.py → /cpdocs-0.3.2/src/cpdocs/plugins/properdocs/core.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/src/cpdocs/versioning.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_config.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_contract.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_rust_example.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/test/test_versioning.py +0 -0
- {cpdocs-0.3.0 → cpdocs-0.3.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cpdocs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 cpdocs developers
|
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# API reference
|
|
7
|
+
|
|
8
|
+
The API reference is generated from the public Fortran module for every documented feature set.
|
|
9
|
+
|
|
10
|
+
## Versions
|
|
11
|
+
|
|
12
|
+
@apidocs-versions
|
|
@@ -29,7 +29,7 @@ Source = "https://github.com/ambhora/cpdocs"
|
|
|
29
29
|
cpdocs = "cpdocs.cli:main"
|
|
30
30
|
|
|
31
31
|
[project.entry-points."properdocs.plugins"]
|
|
32
|
-
cpdocs = "cpdocs.
|
|
32
|
+
cpdocs = "cpdocs.plugins.properdocs.plugin:CpdocsPlugin"
|
|
33
33
|
|
|
34
34
|
[dependency-groups]
|
|
35
35
|
dev = ["pytest>=8", "ruff>=0.8", "properdocs>=1.6.7", "mkdocs-materialx"]
|
|
@@ -15,35 +15,24 @@ import dataclasses
|
|
|
15
15
|
import fnmatch
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
|
-
import re
|
|
19
18
|
import shutil
|
|
20
19
|
import subprocess
|
|
21
20
|
import sys
|
|
22
21
|
import tempfile
|
|
23
22
|
from pathlib import Path, PurePosixPath
|
|
24
23
|
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
write_feature_set,
|
|
36
|
-
)
|
|
37
|
-
from .fortran_backend import extract_fortran_graph
|
|
38
|
-
from .html_renderer import render_html_site
|
|
39
|
-
from .model import ApiGraph, merge_graphs
|
|
40
|
-
from .python_backend import build_python_graph
|
|
41
|
-
from .rust_backend import build_rust_graph
|
|
24
|
+
from .config import CpdocsConfig
|
|
25
|
+
from .contract import ContractError, UnitManifest
|
|
26
|
+
from .languages import rendering_for
|
|
27
|
+
from .languages.cpp.extract import extract_clang_graph
|
|
28
|
+
from .languages.fortran.extract import extract_fortran_graph
|
|
29
|
+
from .languages.python.extract import build_python_graph
|
|
30
|
+
from .languages.rust.extract import build_rust_graph
|
|
31
|
+
from .model import ApiGraph, merge_graphs, relative_source_path
|
|
32
|
+
from .providers.contract import discover_feature_sets, resolve_manifest
|
|
33
|
+
from .rendering.html import render_html_site
|
|
42
34
|
from .versioning import SelectedRef, current_branch, select_refs
|
|
43
35
|
|
|
44
|
-
_PLACEHOLDER = re.compile(r"\{\{([a-z][a-z0-9-]*)\}\}")
|
|
45
|
-
_CONTAINER_KINDS = {"namespace", "package", "module"}
|
|
46
|
-
|
|
47
36
|
|
|
48
37
|
@dataclasses.dataclass(frozen=True)
|
|
49
38
|
class Layout:
|
|
@@ -71,113 +60,6 @@ class Layout:
|
|
|
71
60
|
|
|
72
61
|
|
|
73
62
|
|
|
74
|
-
# --------------------------------------------------------------------------------------------------
|
|
75
|
-
# Talking to the build system
|
|
76
|
-
# --------------------------------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _expand(command: str, values: dict[str, str], *, allowed: set[str], name: str) -> str:
|
|
80
|
-
placeholders = set(_PLACEHOLDER.findall(command))
|
|
81
|
-
unknown = sorted(placeholders - allowed)
|
|
82
|
-
if unknown:
|
|
83
|
-
raise RuntimeError(
|
|
84
|
-
f"cpdocs.yml: build.{name} uses unknown placeholders: {', '.join(unknown)}"
|
|
85
|
-
)
|
|
86
|
-
return _PLACEHOLDER.sub(lambda match: values[match.group(1)], command)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _run_command(
|
|
90
|
-
command: str,
|
|
91
|
-
*,
|
|
92
|
-
source: Path,
|
|
93
|
-
values: dict[str, str],
|
|
94
|
-
allowed: set[str],
|
|
95
|
-
name: str,
|
|
96
|
-
log: Path,
|
|
97
|
-
) -> None:
|
|
98
|
-
expanded = _expand(command, values, allowed=allowed, name=name)
|
|
99
|
-
environment = dict(os.environ)
|
|
100
|
-
# Commands invoked from a source checkout should be able to use cpdocs' own helper CLI just as
|
|
101
|
-
# they can when cpdocs is installed. This is only an execution-environment convenience; the
|
|
102
|
-
# build-system contract itself remains the JSON files and command placeholders.
|
|
103
|
-
package_root = str(Path(__file__).resolve().parents[1])
|
|
104
|
-
pythonpath = environment.get("PYTHONPATH")
|
|
105
|
-
environment["PYTHONPATH"] = package_root + (os.pathsep + pythonpath if pythonpath else "")
|
|
106
|
-
log.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
-
with log.open("w", encoding="utf-8") as stream:
|
|
108
|
-
stream.write("$ " + expanded + "\n")
|
|
109
|
-
stream.flush()
|
|
110
|
-
result = subprocess.run(
|
|
111
|
-
expanded,
|
|
112
|
-
cwd=source,
|
|
113
|
-
shell=True,
|
|
114
|
-
check=False,
|
|
115
|
-
text=True,
|
|
116
|
-
stdout=subprocess.PIPE,
|
|
117
|
-
stderr=subprocess.STDOUT,
|
|
118
|
-
env=environment,
|
|
119
|
-
)
|
|
120
|
-
stream.write(result.stdout)
|
|
121
|
-
if result.returncode != 0:
|
|
122
|
-
raise RuntimeError(
|
|
123
|
-
f"build-system command failed (build.{name}):\n {expanded}\n\n"
|
|
124
|
-
+ result.stdout.rstrip()
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def discover_feature_sets(command: str, *, source: Path, work: Path) -> FeatureSets:
|
|
129
|
-
"""Run ``build.feature-sets`` and read ``feature-sets.json``."""
|
|
130
|
-
|
|
131
|
-
directory = work / "feature-sets"
|
|
132
|
-
shutil.rmtree(directory, ignore_errors=True)
|
|
133
|
-
tmpdir = directory / "tmp"
|
|
134
|
-
tmpdir.mkdir(parents=True)
|
|
135
|
-
output = directory / "feature-sets.json"
|
|
136
|
-
_run_command(
|
|
137
|
-
command,
|
|
138
|
-
source=source.resolve(),
|
|
139
|
-
values={
|
|
140
|
-
"source": str(source.resolve()),
|
|
141
|
-
"tmpdir": str(tmpdir.resolve()),
|
|
142
|
-
"output-file": str(output.resolve()),
|
|
143
|
-
"python": sys.executable,
|
|
144
|
-
},
|
|
145
|
-
allowed={"source", "tmpdir", "output-file", "python"},
|
|
146
|
-
name="feature-sets",
|
|
147
|
-
log=directory / "log.txt",
|
|
148
|
-
)
|
|
149
|
-
return read_feature_sets(output)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def resolve_manifest(
|
|
153
|
-
command: str, feature_set: FeatureSetDeclaration, *, source: Path, work: Path
|
|
154
|
-
) -> Manifest:
|
|
155
|
-
"""Run ``build.build`` once for one concrete feature set and read ``manifest.json``."""
|
|
156
|
-
|
|
157
|
-
directory = work / "feature-sets" / feature_set.key
|
|
158
|
-
shutil.rmtree(directory, ignore_errors=True)
|
|
159
|
-
tmpdir = directory / "tmp"
|
|
160
|
-
tmpdir.mkdir(parents=True)
|
|
161
|
-
feature_set_file = directory / "feature-set.json"
|
|
162
|
-
output = directory / "manifest.json"
|
|
163
|
-
write_feature_set(feature_set_file, feature_set)
|
|
164
|
-
_run_command(
|
|
165
|
-
command,
|
|
166
|
-
source=source.resolve(),
|
|
167
|
-
values={
|
|
168
|
-
"source": str(source.resolve()),
|
|
169
|
-
"tmpdir": str(tmpdir.resolve()),
|
|
170
|
-
"feature-set": str(feature_set_file.resolve()),
|
|
171
|
-
"output-file": str(output.resolve()),
|
|
172
|
-
"python": sys.executable,
|
|
173
|
-
},
|
|
174
|
-
allowed={"source", "tmpdir", "feature-set", "output-file", "python"},
|
|
175
|
-
name="build",
|
|
176
|
-
log=directory / "log.txt",
|
|
177
|
-
)
|
|
178
|
-
return read_manifest(output, source=source.resolve())
|
|
179
|
-
|
|
180
|
-
|
|
181
63
|
# --------------------------------------------------------------------------------------------------
|
|
182
64
|
# Extraction
|
|
183
65
|
# --------------------------------------------------------------------------------------------------
|
|
@@ -199,8 +81,8 @@ def _browse_logical_path(
|
|
|
199
81
|
try:
|
|
200
82
|
return path.resolve().relative_to(project_root.resolve()).as_posix()
|
|
201
83
|
except (OSError, ValueError):
|
|
202
|
-
#
|
|
203
|
-
#
|
|
84
|
+
# Keep a stable internal key for files outside the checkout. This path is not presented to
|
|
85
|
+
# readers; rendering uses the semantic layout recorded in ``source_display_paths``.
|
|
204
86
|
base = Path("_generated") / unit.id / category
|
|
205
87
|
try:
|
|
206
88
|
relative = (
|
|
@@ -211,11 +93,69 @@ def _browse_logical_path(
|
|
|
211
93
|
return (base / relative).as_posix()
|
|
212
94
|
|
|
213
95
|
|
|
96
|
+
def _semantic_display_path(path: Path, *, root: Path, generated: bool) -> str:
|
|
97
|
+
"""Return the compiler-facing layout path shown in the file browser.
|
|
98
|
+
|
|
99
|
+
Manifest roots are semantic roots. A file below ``.../src/cuda/include`` is therefore shown
|
|
100
|
+
below ``include`` and a file below ``.../src/cuda/lib`` below ``lib``. Build-generated roots
|
|
101
|
+
use the same flattened layout under one ``_generated`` prefix.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
if root.is_dir():
|
|
106
|
+
relative = path.resolve().relative_to(root.resolve())
|
|
107
|
+
root_name = root.name
|
|
108
|
+
else:
|
|
109
|
+
relative = Path(path.name)
|
|
110
|
+
root_name = root.parent.name
|
|
111
|
+
except (OSError, ValueError):
|
|
112
|
+
relative = Path(path.name)
|
|
113
|
+
root_name = root.name if root.name != path.name else root.parent.name
|
|
114
|
+
|
|
115
|
+
base = Path("_generated") if generated else Path()
|
|
116
|
+
if root_name:
|
|
117
|
+
base /= root_name
|
|
118
|
+
return (base / relative).as_posix()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _api_display_paths(
|
|
122
|
+
unit: UnitManifest, graph: ApiGraph, *, project_root: Path
|
|
123
|
+
) -> dict[str, str]:
|
|
124
|
+
"""Map API source keys to their semantic include/module layout."""
|
|
125
|
+
|
|
126
|
+
values: dict[str, str] = {}
|
|
127
|
+
for api_root in unit.api:
|
|
128
|
+
root = api_root.path
|
|
129
|
+
if not root.is_dir():
|
|
130
|
+
continue
|
|
131
|
+
for path in sorted(root.rglob("*")):
|
|
132
|
+
if not path.is_file():
|
|
133
|
+
continue
|
|
134
|
+
try:
|
|
135
|
+
root_relative = path.resolve().relative_to(root.resolve()).as_posix()
|
|
136
|
+
except (OSError, ValueError):
|
|
137
|
+
root_relative = path.name
|
|
138
|
+
project_relative = relative_source_path(path, project_root)
|
|
139
|
+
logical = next(
|
|
140
|
+
(candidate for candidate in (root_relative, project_relative) if candidate in graph.sources),
|
|
141
|
+
None,
|
|
142
|
+
)
|
|
143
|
+
if logical is not None:
|
|
144
|
+
values.setdefault(
|
|
145
|
+
logical,
|
|
146
|
+
_semantic_display_path(
|
|
147
|
+
path, root=root, generated=unit.generated or api_root.generated
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
return values
|
|
151
|
+
|
|
152
|
+
|
|
214
153
|
def _collect_browse_files(
|
|
215
154
|
unit: UnitManifest, *, project_root: Path
|
|
216
|
-
) -> tuple[dict[str, str], list[str]]:
|
|
155
|
+
) -> tuple[dict[str, str], list[str], dict[str, str]]:
|
|
217
156
|
files: dict[str, str] = {}
|
|
218
157
|
generated: list[str] = []
|
|
158
|
+
display_paths: dict[str, str] = {}
|
|
219
159
|
# Public headers are already present in ``graph.sources`` after API extraction. These roots
|
|
220
160
|
# add implementation-only material without duplicating public headers in the file hierarchy.
|
|
221
161
|
groups = (
|
|
@@ -242,9 +182,13 @@ def _collect_browse_files(
|
|
|
242
182
|
path, project_root=project_root, unit=unit, category=category, root=root
|
|
243
183
|
)
|
|
244
184
|
files.setdefault(logical, content)
|
|
185
|
+
display_paths.setdefault(
|
|
186
|
+
logical,
|
|
187
|
+
_semantic_display_path(path, root=root, generated=unit.generated),
|
|
188
|
+
)
|
|
245
189
|
if unit.generated and logical not in generated:
|
|
246
190
|
generated.append(logical)
|
|
247
|
-
return files, generated
|
|
191
|
+
return files, generated, display_paths
|
|
248
192
|
|
|
249
193
|
|
|
250
194
|
def _generated_sources(unit: UnitManifest, graph: ApiGraph) -> list[str]:
|
|
@@ -325,6 +269,11 @@ def _merge_metadata(target: dict[str, object], source: dict[str, object]) -> Non
|
|
|
325
269
|
existing = target.setdefault(key, [])
|
|
326
270
|
assert isinstance(existing, list)
|
|
327
271
|
existing.extend(item for item in value if item not in existing)
|
|
272
|
+
elif isinstance(value, dict):
|
|
273
|
+
existing = target.setdefault(key, {})
|
|
274
|
+
assert isinstance(existing, dict)
|
|
275
|
+
for item_key, item_value in value.items():
|
|
276
|
+
existing.setdefault(item_key, item_value)
|
|
328
277
|
else:
|
|
329
278
|
target.setdefault(key, value)
|
|
330
279
|
|
|
@@ -374,7 +323,8 @@ def _check_undocumented(graph: ApiGraph, level: str) -> None:
|
|
|
374
323
|
missing = sorted(
|
|
375
324
|
entity.qualified_name
|
|
376
325
|
for entity in graph.entities.values()
|
|
377
|
-
if entity.kind not in
|
|
326
|
+
if entity.kind not in rendering_for(entity.language).container_kinds
|
|
327
|
+
and not entity.documentation.strip()
|
|
378
328
|
)
|
|
379
329
|
if not missing:
|
|
380
330
|
return
|
|
@@ -427,9 +377,18 @@ def build_graph(
|
|
|
427
377
|
work=work / "feature-sets" / feature_set.key,
|
|
428
378
|
config=config,
|
|
429
379
|
)
|
|
430
|
-
|
|
380
|
+
display_paths = unit_graph.metadata.setdefault("source_display_paths", {})
|
|
381
|
+
assert isinstance(display_paths, dict)
|
|
382
|
+
for path, display in _api_display_paths(unit, unit_graph, project_root=source).items():
|
|
383
|
+
display_paths.setdefault(path, display)
|
|
384
|
+
|
|
385
|
+
browse_files, generated_files, browse_display_paths = _collect_browse_files(
|
|
386
|
+
unit, project_root=source
|
|
387
|
+
)
|
|
431
388
|
for path, content in browse_files.items():
|
|
432
389
|
unit_graph.files.setdefault(path, content)
|
|
390
|
+
for path, display in browse_display_paths.items():
|
|
391
|
+
display_paths.setdefault(path, display)
|
|
433
392
|
if generated_files:
|
|
434
393
|
existing = unit_graph.metadata.setdefault("generated_files", [])
|
|
435
394
|
assert isinstance(existing, list)
|
|
@@ -688,7 +647,6 @@ __all__ = [
|
|
|
688
647
|
"build_current",
|
|
689
648
|
"build_graph",
|
|
690
649
|
"build_versions",
|
|
691
|
-
"cmake_module",
|
|
692
650
|
"discover_feature_sets",
|
|
693
651
|
"recorded_default_version",
|
|
694
652
|
"resolve_manifest",
|
|
@@ -10,10 +10,10 @@ from pathlib import Path
|
|
|
10
10
|
from . import __version__
|
|
11
11
|
from .build import build_current, build_versions
|
|
12
12
|
from .config import DEFAULT_CONFIG, load_config
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
13
|
+
from .providers.python import write_feature_sets as write_python_feature_sets
|
|
14
|
+
from .providers.python import write_manifest as write_python_manifest
|
|
15
|
+
from .providers.cargo import write_feature_sets as write_rust_feature_sets
|
|
16
|
+
from .providers.cargo import write_manifest as write_rust_manifest
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _path(value: str) -> Path:
|
|
@@ -173,16 +173,16 @@ def read_manifest(path: Path, *, source: Path) -> Manifest:
|
|
|
173
173
|
)
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
-
def paths(field: str) -> tuple[Path, ...]:
|
|
176
|
+
def paths(field: str, *, data: dict[str, Any], where: str) -> tuple[Path, ...]:
|
|
177
177
|
return tuple(
|
|
178
178
|
_path(item, source)
|
|
179
179
|
for item in _string_tuple(data.get(field, []), f"{where}.{field}", path)
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
-
headers = paths("headers")
|
|
183
|
-
sources = paths("sources")
|
|
184
|
-
modules = paths("modules")
|
|
185
|
-
executables = paths("executables")
|
|
182
|
+
headers = paths("headers", data=data, where=where)
|
|
183
|
+
sources = paths("sources", data=data, where=where)
|
|
184
|
+
modules = paths("modules", data=data, where=where)
|
|
185
|
+
executables = paths("executables", data=data, where=where)
|
|
186
186
|
generated = bool(data.get("generated", False))
|
|
187
187
|
|
|
188
188
|
parse: ParseOptions | None = None
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 cpdocs developers
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Programming-language adapters for extraction and presentation."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from .base import LanguageRendering
|
|
10
|
+
from .cpp.render import RENDERING as CPP
|
|
11
|
+
from .fortran.render import RENDERING as FORTRAN
|
|
12
|
+
from .python.render import RENDERING as PYTHON
|
|
13
|
+
from .rust.render import RENDERING as RUST
|
|
14
|
+
|
|
15
|
+
_RENDERINGS = {
|
|
16
|
+
"c": CPP,
|
|
17
|
+
"cpp": CPP,
|
|
18
|
+
"cuda": CPP,
|
|
19
|
+
"hip": CPP,
|
|
20
|
+
"fortran": FORTRAN,
|
|
21
|
+
"python": PYTHON,
|
|
22
|
+
"rust": RUST,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_LANGUAGE_LABELS = {
|
|
26
|
+
"c": "C",
|
|
27
|
+
"cpp": "C++",
|
|
28
|
+
"cuda": "CUDA",
|
|
29
|
+
"hip": "HIP",
|
|
30
|
+
"fortran": "Fortran",
|
|
31
|
+
"python": "Python",
|
|
32
|
+
"rust": "Rust",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_KIND_LABELS = {
|
|
36
|
+
"namespace": "namespace",
|
|
37
|
+
"module": "module",
|
|
38
|
+
"package": "package",
|
|
39
|
+
"class": "class",
|
|
40
|
+
"struct": "struct",
|
|
41
|
+
"derived_type": "derived type",
|
|
42
|
+
"union": "union",
|
|
43
|
+
"enum": "enum",
|
|
44
|
+
"trait": "trait",
|
|
45
|
+
"protocol": "protocol",
|
|
46
|
+
"type_alias": "type alias",
|
|
47
|
+
"concept": "concept",
|
|
48
|
+
"function": "function",
|
|
49
|
+
"subroutine": "subroutine",
|
|
50
|
+
"method": "method",
|
|
51
|
+
"constructor": "constructor",
|
|
52
|
+
"property": "property",
|
|
53
|
+
"attribute": "attribute",
|
|
54
|
+
"variable": "variable",
|
|
55
|
+
"constant": "constant",
|
|
56
|
+
"macro": "macro",
|
|
57
|
+
"deduction_guide": "deduction guide",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_KIND_MARKERS = {
|
|
61
|
+
"namespace": "N",
|
|
62
|
+
"module": "M",
|
|
63
|
+
"package": "P",
|
|
64
|
+
"class": "C",
|
|
65
|
+
"struct": "S",
|
|
66
|
+
"derived_type": "T",
|
|
67
|
+
"union": "U",
|
|
68
|
+
"enum": "E",
|
|
69
|
+
"trait": "T",
|
|
70
|
+
"protocol": "P",
|
|
71
|
+
"function": "F",
|
|
72
|
+
"subroutine": "F",
|
|
73
|
+
"method": "F",
|
|
74
|
+
"constructor": "F",
|
|
75
|
+
"type_alias": "T",
|
|
76
|
+
"variable": "V",
|
|
77
|
+
"attribute": "A",
|
|
78
|
+
"property": "P",
|
|
79
|
+
"constant": "C",
|
|
80
|
+
"macro": "D",
|
|
81
|
+
"concept": "K",
|
|
82
|
+
"deduction_guide": "G",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def rendering_for(language: str) -> LanguageRendering:
|
|
87
|
+
"""Return rendering semantics for one programming language."""
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
return _RENDERINGS[language]
|
|
91
|
+
except KeyError as error:
|
|
92
|
+
raise ValueError(f"unsupported API language: {language}") from error
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def language_label(language: str) -> str:
|
|
96
|
+
return _LANGUAGE_LABELS.get(language, language)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def kind_label(kind: str, language: str | None = None) -> str:
|
|
100
|
+
if language in _RENDERINGS:
|
|
101
|
+
specific = _RENDERINGS[language].kind_labels.get(kind)
|
|
102
|
+
if specific is not None:
|
|
103
|
+
return specific
|
|
104
|
+
return _KIND_LABELS.get(kind, kind.replace("_", " "))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def kind_marker(kind: str, language: str | None = None) -> str:
|
|
108
|
+
if language in _RENDERINGS:
|
|
109
|
+
specific = _RENDERINGS[language].kind_markers.get(kind)
|
|
110
|
+
if specific is not None:
|
|
111
|
+
return specific
|
|
112
|
+
return _KIND_MARKERS.get(kind, "·")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def source_language(path: str) -> str:
|
|
116
|
+
suffix = Path(path).suffix.casefold()
|
|
117
|
+
if suffix in {".cu", ".cuh"}:
|
|
118
|
+
return "cuda"
|
|
119
|
+
if suffix == ".hip":
|
|
120
|
+
return "hip"
|
|
121
|
+
if suffix in {".c"}:
|
|
122
|
+
return "c"
|
|
123
|
+
if suffix in {".h", ".hh", ".hpp", ".hxx", ".cc", ".cpp", ".cxx"}:
|
|
124
|
+
return "cpp"
|
|
125
|
+
for language in ("fortran", "python", "rust"):
|
|
126
|
+
if suffix in _RENDERINGS[language].source_suffixes:
|
|
127
|
+
return language
|
|
128
|
+
return "text"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
__all__ = [
|
|
132
|
+
"LanguageRendering",
|
|
133
|
+
"kind_label",
|
|
134
|
+
"kind_marker",
|
|
135
|
+
"language_label",
|
|
136
|
+
"rendering_for",
|
|
137
|
+
"source_language",
|
|
138
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 cpdocs developers
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Language-specific presentation contract used by generic renderers."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from ..model import ApiEntity, ApiSignature
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class LanguageRendering:
|
|
15
|
+
"""Rendering rules that belong to a programming language, not to HTML."""
|
|
16
|
+
|
|
17
|
+
separator: str
|
|
18
|
+
container_kinds: frozenset[str]
|
|
19
|
+
class_like_kinds: frozenset[str]
|
|
20
|
+
type_page_kinds: frozenset[str]
|
|
21
|
+
type_like_kinds: frozenset[str]
|
|
22
|
+
function_kinds: frozenset[str]
|
|
23
|
+
member_value_kinds: frozenset[str]
|
|
24
|
+
source_suffixes: frozenset[str]
|
|
25
|
+
kind_markers: dict[str, str]
|
|
26
|
+
kind_labels: dict[str, str]
|
|
27
|
+
signature_spelling: Callable[[ApiEntity, ApiSignature], str]
|
|
28
|
+
entity_declaration: Callable[[ApiEntity], str]
|
|
29
|
+
member_summary: Callable[[ApiEntity], str]
|
|
30
|
+
member_group: Callable[[ApiEntity], str | None]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def parameter_spelling(signature: ApiSignature) -> str:
|
|
34
|
+
"""Render conventional typed parameters used by C-family, Fortran and Rust APIs."""
|
|
35
|
+
|
|
36
|
+
values: list[str] = []
|
|
37
|
+
for parameter in signature.parameters:
|
|
38
|
+
value = parameter.type or parameter.name
|
|
39
|
+
if parameter.name and parameter.type:
|
|
40
|
+
value += f" {parameter.name}"
|
|
41
|
+
if parameter.default is not None:
|
|
42
|
+
value += f" = {parameter.default}"
|
|
43
|
+
values.append(value)
|
|
44
|
+
return ", ".join(values)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def assigned_property(entity: ApiEntity) -> str:
|
|
48
|
+
"""Return an ``= value`` property without its marker."""
|
|
49
|
+
|
|
50
|
+
return next((value[2:] for value in entity.properties if value.startswith("= ")), "")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def typed_property(entity: ApiEntity, *, ignored: frozenset[str] = frozenset()) -> str:
|
|
54
|
+
"""Return the first property that represents a type rather than an assignment/flag."""
|
|
55
|
+
|
|
56
|
+
return next(
|
|
57
|
+
(
|
|
58
|
+
value
|
|
59
|
+
for value in entity.properties
|
|
60
|
+
if not value.startswith("=") and value not in ignored
|
|
61
|
+
),
|
|
62
|
+
"",
|
|
63
|
+
)
|