sumd 0.3.44__tar.gz → 0.3.45__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.
- {sumd-0.3.44 → sumd-0.3.45}/CHANGELOG.md +18 -0
- {sumd-0.3.44 → sumd-0.3.45}/PKG-INFO +7 -7
- {sumd-0.3.44 → sumd-0.3.45}/README.md +6 -6
- {sumd-0.3.44 → sumd-0.3.45}/pyproject.toml +1 -1
- {sumd-0.3.44 → sumd-0.3.45}/sumd/__init__.py +1 -1
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/api_stubs.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/architecture.py +6 -7
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/call_graph.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/configuration.py +4 -5
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/dependencies.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/deployment.py +6 -7
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/extras.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/interfaces.py +4 -4
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/metadata.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/quality.py +4 -5
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/refactor_analysis.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/source_snippets.py +4 -5
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/swop.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/test_contracts.py +4 -5
- sumd-0.3.45/sumd/sections/utils/__init__.py +12 -0
- sumd-0.3.45/sumd/sections/utils/render.py +25 -0
- sumd-0.3.45/sumd/sections/utils/should_render.py +25 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/workflows.py +2 -2
- {sumd-0.3.44 → sumd-0.3.45}/.gitignore +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/LICENSE +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/cli.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/extractor.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/generator.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/mcp_server.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/models.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/parser.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/pipeline.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/renderer.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/__init__.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/base.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/code_analysis.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/sections/environment.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/toon_parser.py +0 -0
- {sumd-0.3.44 → sumd-0.3.45}/sumd/validator.py +0 -0
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.45] - 2026-04-25
|
|
11
|
+
|
|
12
|
+
### Docs
|
|
13
|
+
- Update README.md
|
|
14
|
+
|
|
15
|
+
### Other
|
|
16
|
+
- Update sumd/sections/api_stubs.py
|
|
17
|
+
- Update sumd/sections/architecture.py
|
|
18
|
+
- Update sumd/sections/call_graph.py
|
|
19
|
+
- Update sumd/sections/configuration.py
|
|
20
|
+
- Update sumd/sections/dependencies.py
|
|
21
|
+
- Update sumd/sections/deployment.py
|
|
22
|
+
- Update sumd/sections/extras.py
|
|
23
|
+
- Update sumd/sections/interfaces.py
|
|
24
|
+
- Update sumd/sections/metadata.py
|
|
25
|
+
- Update sumd/sections/quality.py
|
|
26
|
+
- ... and 8 more files
|
|
27
|
+
|
|
10
28
|
## [0.3.44] - 2026-04-24
|
|
11
29
|
|
|
12
30
|
### Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sumd
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.45
|
|
4
4
|
Summary: SUMD - Structured Unified Markdown Descriptor for AI-aware project documentation
|
|
5
5
|
Project-URL: Homepage, https://github.com/tom/statement
|
|
6
6
|
Project-URL: Repository, https://github.com/tom/statement
|
|
@@ -41,17 +41,17 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
|
|
42
42
|
## AI Cost Tracking
|
|
43
43
|
|
|
44
|
-
     
|
|
45
|
+
  
|
|
46
46
|
|
|
47
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
48
|
-
- 👤 **Human dev:** ~$
|
|
47
|
+
- 🤖 **LLM usage:** $7.5000 (75 commits)
|
|
48
|
+
- 👤 **Human dev:** ~$2765 (27.6h @ $100/h, 30min dedup)
|
|
49
49
|
|
|
50
|
-
Generated on 2026-04-
|
|
50
|
+
Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
-
  
|
|
55
55
|
|
|
56
56
|
**SUMD** (Structured Unified Markdown Descriptor) is a semantic project descriptor format in Markdown.
|
|
57
57
|
It defines intent, structure, execution entry points, and the mental model of a system for both humans and LLMs.
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
     
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $7.5000 (75 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$2765 (27.6h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
|
-
Generated on 2026-04-
|
|
12
|
+
Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
  
|
|
17
17
|
|
|
18
18
|
**SUMD** (Structured Unified Markdown Descriptor) is a semantic project descriptor format in Markdown.
|
|
19
19
|
It defines intent, structure, execution entry points, and the mental model of a system for both humans and LLMs.
|
|
@@ -4,7 +4,7 @@ SUMD is a semantic project descriptor format in Markdown that defines intent,
|
|
|
4
4
|
structure, execution entry points, and mental model of a system for both humans and LLMs.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.3.
|
|
7
|
+
__version__ = "0.3.45"
|
|
8
8
|
|
|
9
9
|
from sumd.models import SUMDDocument
|
|
10
10
|
from sumd.parser import (
|
|
@@ -7,6 +7,7 @@ LLM sees function signatures and HTTP method/path without reading full openapi.y
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from sumd.sections.base import RenderContext, Section
|
|
10
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# ---------------------------------------------------------------------------
|
|
@@ -69,8 +70,7 @@ class ApiStubsSection:
|
|
|
69
70
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
70
71
|
return bool(ctx.openapi.get("endpoints"))
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
return _render_api_stubs(ctx.openapi)
|
|
73
|
+
render = call_with_ctx(_render_api_stubs, "openapi")
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
assert isinstance(ApiStubsSection(), Section)
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from sumd.sections.base import RenderContext, Section
|
|
8
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
9
|
+
from sumd.sections.utils.should_render import always
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
# ---------------------------------------------------------------------------
|
|
@@ -143,13 +145,10 @@ class ArchitectureSection:
|
|
|
143
145
|
level = 2
|
|
144
146
|
profiles = frozenset({"minimal", "light", "rich"})
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return _render_architecture(
|
|
151
|
-
ctx.doql, ctx.modules, ctx.name, ctx.proj_dir, ctx.raw_sources
|
|
152
|
-
)
|
|
148
|
+
should_render = always
|
|
149
|
+
render = call_with_ctx(
|
|
150
|
+
_render_architecture, "doql", "modules", "name", "proj_dir", "raw_sources"
|
|
151
|
+
)
|
|
153
152
|
|
|
154
153
|
|
|
155
154
|
assert isinstance(ArchitectureSection(), Section)
|
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
import re
|
|
12
12
|
|
|
13
13
|
from sumd.sections.base import RenderContext, Section
|
|
14
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
# ---------------------------------------------------------------------------
|
|
@@ -149,8 +150,7 @@ class CallGraphSection:
|
|
|
149
150
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
150
151
|
return any("calls.toon" in e.get("file", "") for e in ctx.project_analysis)
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
return _render_call_graph(ctx.project_analysis)
|
|
153
|
+
render = call_with_ctx(_render_call_graph, "project_analysis")
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
assert isinstance(CallGraphSection(), Section)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from sumd.sections.base import RenderContext, Section
|
|
6
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
7
|
+
from sumd.sections.utils.should_render import always
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
# ---------------------------------------------------------------------------
|
|
@@ -34,11 +36,8 @@ class ConfigurationSection:
|
|
|
34
36
|
level = 2
|
|
35
37
|
profiles = frozenset({"light", "rich"})
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def render(self, ctx: RenderContext) -> list[str]:
|
|
41
|
-
return _render_configuration_section(ctx.name, ctx.version)
|
|
39
|
+
should_render = always
|
|
40
|
+
render = call_with_ctx(_render_configuration_section, "name", "version")
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
assert isinstance(ConfigurationSection(), Section)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from sumd.sections.base import RenderContext, Section
|
|
6
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
# ---------------------------------------------------------------------------
|
|
@@ -90,8 +91,7 @@ class DependenciesSection:
|
|
|
90
91
|
or ctx.pkg_json.get("devDependencies")
|
|
91
92
|
)
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
return _render_dependencies(ctx.deps, ctx.dev_deps, ctx.pkg_json)
|
|
94
|
+
render = call_with_ctx(_render_dependencies, "deps", "dev_deps", "pkg_json")
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
assert isinstance(DependenciesSection(), Section)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from sumd.sections.base import RenderContext, Section
|
|
6
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
7
|
+
from sumd.sections.utils.should_render import always
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
# ---------------------------------------------------------------------------
|
|
@@ -99,13 +101,10 @@ class DeploymentSection:
|
|
|
99
101
|
level = 2
|
|
100
102
|
profiles = frozenset({"light", "rich"})
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return _render_deployment(
|
|
107
|
-
ctx.pkg_json, ctx.name, ctx.reqs, ctx.dockerfile, ctx.compose
|
|
108
|
-
)
|
|
104
|
+
should_render = always
|
|
105
|
+
render = call_with_ctx(
|
|
106
|
+
_render_deployment, "pkg_json", "name", "reqs", "dockerfile", "compose"
|
|
107
|
+
)
|
|
109
108
|
|
|
110
109
|
|
|
111
110
|
assert isinstance(DeploymentSection(), Section)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from sumd.sections.base import RenderContext, Section
|
|
6
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
# ---------------------------------------------------------------------------
|
|
@@ -65,8 +66,7 @@ class ExtrasSection:
|
|
|
65
66
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
66
67
|
return bool(ctx.makefile or ctx.pkg_json.get("scripts"))
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
return _render_extras(ctx.makefile, ctx.pkg_json)
|
|
69
|
+
render = call_with_ctx(_render_extras, "makefile", "pkg_json")
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
assert isinstance(ExtrasSection(), Section)
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from sumd.sections.base import RenderContext, Section
|
|
8
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# ---------------------------------------------------------------------------
|
|
@@ -148,10 +149,9 @@ class InterfacesSection:
|
|
|
148
149
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
149
150
|
return bool(ctx.scripts or ctx.openapi.get("endpoints") or ctx.scenarios)
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
)
|
|
152
|
+
render = call_with_ctx(
|
|
153
|
+
_render_interfaces, "scripts", "openapi", "scenarios", "proj_dir", "raw_sources"
|
|
154
|
+
)
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
assert isinstance(InterfacesSection(), Section)
|
|
@@ -8,6 +8,7 @@ This serves as the reference implementation for all other sections.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from sumd.sections.base import RenderContext, Section
|
|
11
|
+
from sumd.sections.utils.should_render import always
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class MetadataSection:
|
|
@@ -17,8 +18,7 @@ class MetadataSection:
|
|
|
17
18
|
level: int = 2
|
|
18
19
|
profiles: frozenset[str] = frozenset({"minimal", "light", "rich"})
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
return True # Always render
|
|
21
|
+
should_render = always
|
|
22
22
|
|
|
23
23
|
def render(self, ctx: RenderContext) -> list[str]:
|
|
24
24
|
L: list[str] = []
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from sumd.sections.base import RenderContext, Section
|
|
8
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
9
|
+
from sumd.sections.utils.should_render import has_attr
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
# ---------------------------------------------------------------------------
|
|
@@ -72,11 +74,8 @@ class QualitySection:
|
|
|
72
74
|
level = 2
|
|
73
75
|
profiles = frozenset({"light", "rich", "refactor"})
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def render(self, ctx: RenderContext) -> list[str]:
|
|
79
|
-
return _render_quality(ctx.pyqual, ctx.proj_dir, ctx.raw_sources)
|
|
77
|
+
should_render = has_attr("pyqual")
|
|
78
|
+
render = call_with_ctx(_render_quality, "pyqual", "proj_dir", "raw_sources")
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
assert isinstance(QualitySection(), Section)
|
|
@@ -17,6 +17,7 @@ Files that don't exist are silently skipped (non-blocking).
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
19
|
from sumd.sections.base import RenderContext, Section
|
|
20
|
+
from sumd.sections.utils.should_render import has_attr
|
|
20
21
|
|
|
21
22
|
# Display order and labels for refactor analysis files.
|
|
22
23
|
_REFACTOR_FILE_LABELS: dict[str, str] = {
|
|
@@ -36,8 +37,7 @@ class RefactorAnalysisSection:
|
|
|
36
37
|
level = 2
|
|
37
38
|
profiles = frozenset({"refactor"})
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
return bool(ctx.project_analysis)
|
|
40
|
+
should_render = has_attr("project_analysis")
|
|
41
41
|
|
|
42
42
|
def render(self, ctx: RenderContext) -> list[str]:
|
|
43
43
|
L: list[str] = []
|
|
@@ -8,6 +8,8 @@ to read full source files.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from sumd.sections.base import RenderContext, Section
|
|
11
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
12
|
+
from sumd.sections.utils.should_render import has_attr
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
# ---------------------------------------------------------------------------
|
|
@@ -59,11 +61,8 @@ class SourceSnippetsSection:
|
|
|
59
61
|
level = 2
|
|
60
62
|
profiles = frozenset({"rich"})
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def render(self, ctx: RenderContext) -> list[str]:
|
|
66
|
-
return _render_source_snippets(ctx.source_snippets)
|
|
64
|
+
should_render = has_attr("source_snippets")
|
|
65
|
+
render = call_with_ctx(_render_source_snippets, "source_snippets")
|
|
67
66
|
|
|
68
67
|
|
|
69
68
|
assert isinstance(SourceSnippetsSection(), Section)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from sumd.sections.base import RenderContext, Section
|
|
6
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def _render_swop_section(swop: dict, raw_sources: bool) -> list[str]:
|
|
@@ -61,8 +62,7 @@ class SwopSection:
|
|
|
61
62
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
62
63
|
return bool(ctx.swop.get("contexts"))
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
return _render_swop_section(ctx.swop, ctx.raw_sources)
|
|
65
|
+
render = call_with_ctx(_render_swop_section, "swop", "raw_sources")
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
assert isinstance(SwopSection(), Section)
|
|
@@ -7,6 +7,8 @@ LLM sees what the system guarantees without reading full scenario files.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from sumd.sections.base import RenderContext, Section
|
|
10
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
11
|
+
from sumd.sections.utils.should_render import has_attr
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
# ---------------------------------------------------------------------------
|
|
@@ -68,11 +70,8 @@ class TestContractsSection:
|
|
|
68
70
|
level = 2
|
|
69
71
|
profiles = frozenset({"rich"})
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def render(self, ctx: RenderContext) -> list[str]:
|
|
75
|
-
return _render_test_contracts(ctx.scenarios)
|
|
73
|
+
should_render = has_attr("scenarios")
|
|
74
|
+
render = call_with_ctx(_render_test_contracts, "scenarios")
|
|
76
75
|
|
|
77
76
|
|
|
78
77
|
assert isinstance(TestContractsSection(), Section)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""sumd.sections.utils — shared utilities for section renderers.
|
|
2
|
+
|
|
3
|
+
Factories for common ``should_render`` and ``render`` patterns so section
|
|
4
|
+
classes stay DRY.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
10
|
+
from sumd.sections.utils.should_render import always, has_attr
|
|
11
|
+
|
|
12
|
+
__all__ = ["always", "has_attr", "call_with_ctx"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Utility factory for common ``render`` patterns.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
class MySection:
|
|
6
|
+
render = call_with_ctx(_render_my_section, "attr1", "attr2")
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Callable
|
|
12
|
+
|
|
13
|
+
from sumd.sections.base import RenderContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def call_with_ctx(render_fn: Callable, *attr_names: str):
|
|
17
|
+
"""Return a ``render`` method that calls *render_fn* with ctx attributes.
|
|
18
|
+
|
|
19
|
+
The returned callable accepts ``(self, ctx)`` so it works when assigned
|
|
20
|
+
as a class attribute (Python binds ``self`` automatically).
|
|
21
|
+
"""
|
|
22
|
+
def render(_self: object, ctx: RenderContext) -> list[str]:
|
|
23
|
+
args = [getattr(ctx, name) for name in attr_names]
|
|
24
|
+
return render_fn(*args)
|
|
25
|
+
return render
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Utility factories for common ``should_render`` patterns.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
class MySection:
|
|
6
|
+
should_render = always
|
|
7
|
+
# or
|
|
8
|
+
should_render = has_attr("my_data")
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from sumd.sections.base import RenderContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def always(_self: object, _ctx: RenderContext) -> bool:
|
|
17
|
+
"""Always render the section."""
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def has_attr(attr: str):
|
|
22
|
+
"""Return a ``should_render`` that checks ``bool(ctx.<attr>)``."""
|
|
23
|
+
def should_render(_self: object, ctx: RenderContext) -> bool:
|
|
24
|
+
return bool(getattr(ctx, attr))
|
|
25
|
+
return should_render
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from sumd.sections.base import RenderContext, Section
|
|
8
|
+
from sumd.sections.utils.render import call_with_ctx
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# ---------------------------------------------------------------------------
|
|
@@ -79,8 +80,7 @@ class WorkflowsSection:
|
|
|
79
80
|
def should_render(self, ctx: RenderContext) -> bool:
|
|
80
81
|
return bool(ctx.tasks or ctx.doql.get("workflows"))
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
return _render_workflows(ctx.doql, ctx.tasks, ctx.proj_dir, ctx.raw_sources)
|
|
83
|
+
render = call_with_ctx(_render_workflows, "doql", "tasks", "proj_dir", "raw_sources")
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
assert isinstance(WorkflowsSection(), Section)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|