modwire 1.1.1__tar.gz → 2.1.0__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 (94) hide show
  1. {modwire-1.1.1 → modwire-2.1.0}/CONTRIBUTING.md +2 -8
  2. modwire-2.1.0/PKG-INFO +252 -0
  3. modwire-2.1.0/README.md +219 -0
  4. {modwire-1.1.1 → modwire-2.1.0}/docs/wiki/Development-checks.md +3 -0
  5. {modwire-1.1.1 → modwire-2.1.0}/docs/wiki/Home.md +3 -0
  6. modwire-2.1.0/src/modwire/__init__.py +96 -0
  7. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/_version.py +3 -3
  8. modwire-2.1.0/src/modwire/architecture/__init__.py +59 -0
  9. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/architecture/analyzers.py +19 -0
  10. modwire-2.1.0/src/modwire/architecture/config.py +126 -0
  11. modwire-2.1.0/src/modwire/architecture/insights.py +246 -0
  12. modwire-2.1.0/src/modwire/architecture/matching.py +153 -0
  13. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/architecture/policy.py +20 -14
  14. modwire-2.1.0/src/modwire/architecture/render.py +99 -0
  15. modwire-2.1.0/src/modwire/architecture/violations.py +47 -0
  16. modwire-2.1.0/src/modwire/callables.py +162 -0
  17. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/definitions.py +64 -0
  18. modwire-2.1.0/src/modwire/extraction/__init__.py +33 -0
  19. modwire-2.1.0/src/modwire/extraction/cache.py +92 -0
  20. modwire-2.1.0/src/modwire/extraction/manifest.py +97 -0
  21. modwire-2.1.0/src/modwire/extraction/models.py +199 -0
  22. modwire-2.1.0/src/modwire/extraction/roots.py +100 -0
  23. modwire-2.1.0/src/modwire/extraction/serialization.py +107 -0
  24. modwire-2.1.0/src/modwire/extraction/service.py +100 -0
  25. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/base.py +18 -2
  26. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/loader.py +6 -1
  27. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/php.py +5 -1
  28. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/scripts/php_extractor.php +459 -5
  29. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/scripts/python_extractor.py +393 -2
  30. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/scripts/typescript_extractor.js +380 -2
  31. modwire-2.1.0/src/modwire/graph.py +116 -0
  32. modwire-2.1.0/src/modwire/metadata.py +128 -0
  33. modwire-2.1.0/src/modwire/shape/__init__.py +16 -0
  34. modwire-2.1.0/src/modwire/shape/config.py +93 -0
  35. modwire-2.1.0/src/modwire/shape/evaluator.py +32 -0
  36. modwire-2.1.0/src/modwire/shape/rules.py +389 -0
  37. modwire-2.1.0/src/modwire/shape/violations.py +23 -0
  38. modwire-2.1.0/src/modwire/testing/__init__.py +24 -0
  39. modwire-2.1.0/src/modwire/testing/factories.py +277 -0
  40. modwire-2.1.0/src/modwire.egg-info/PKG-INFO +252 -0
  41. {modwire-1.1.1 → modwire-2.1.0}/src/modwire.egg-info/SOURCES.txt +18 -1
  42. {modwire-1.1.1 → modwire-2.1.0}/tests/test_api.py +650 -1
  43. modwire-2.1.0/tests/test_architecture_api.py +360 -0
  44. modwire-1.1.1/PKG-INFO +0 -127
  45. modwire-1.1.1/README.md +0 -94
  46. modwire-1.1.1/src/modwire/__init__.py +0 -18
  47. modwire-1.1.1/src/modwire/architecture/__init__.py +0 -10
  48. modwire-1.1.1/src/modwire/architecture/matching.py +0 -58
  49. modwire-1.1.1/src/modwire/architecture/render.py +0 -98
  50. modwire-1.1.1/src/modwire/architecture/violations.py +0 -24
  51. modwire-1.1.1/src/modwire/extraction.py +0 -73
  52. modwire-1.1.1/src/modwire/graph.py +0 -56
  53. modwire-1.1.1/src/modwire.egg-info/PKG-INFO +0 -127
  54. modwire-1.1.1/tests/test_architecture_api.py +0 -43
  55. {modwire-1.1.1 → modwire-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  56. {modwire-1.1.1 → modwire-2.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  57. {modwire-1.1.1 → modwire-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  58. {modwire-1.1.1 → modwire-2.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  59. {modwire-1.1.1 → modwire-2.1.0}/.github/workflows/ci.yml +0 -0
  60. {modwire-1.1.1 → modwire-2.1.0}/.github/workflows/release.yml +0 -0
  61. {modwire-1.1.1 → modwire-2.1.0}/.gitignore +0 -0
  62. {modwire-1.1.1 → modwire-2.1.0}/LICENSE +0 -0
  63. {modwire-1.1.1 → modwire-2.1.0}/docs/wiki/Reporting-bugs.md +0 -0
  64. {modwire-1.1.1 → modwire-2.1.0}/docs/wiki/Requesting-features.md +0 -0
  65. {modwire-1.1.1 → modwire-2.1.0}/pyproject.toml +0 -0
  66. {modwire-1.1.1 → modwire-2.1.0}/setup.cfg +0 -0
  67. {modwire-1.1.1 → modwire-2.1.0}/show_test_source_files.py +0 -0
  68. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/exports.py +0 -0
  69. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/__init__.py +0 -0
  70. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/python.py +0 -0
  71. {modwire-1.1.1 → modwire-2.1.0}/src/modwire/extractors/typescript.py +0 -0
  72. {modwire-1.1.1 → modwire-2.1.0}/src/modwire.egg-info/dependency_links.txt +0 -0
  73. {modwire-1.1.1 → modwire-2.1.0}/src/modwire.egg-info/requires.txt +0 -0
  74. {modwire-1.1.1 → modwire-2.1.0}/src/modwire.egg-info/top_level.txt +0 -0
  75. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/php/ignored/generated.php +0 -0
  76. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/php/src/application/use_cases/activate.php +0 -0
  77. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/php/src/domain/model/user.php +0 -0
  78. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/php/src/domain/services/policy.php +0 -0
  79. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/php/src/interfaces/http/controller.php +0 -0
  80. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/python/ignored/generated.py +0 -0
  81. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/python/src/application/use_cases/activate.py +0 -0
  82. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/python/src/domain/model/user.py +0 -0
  83. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/python/src/domain/services/policy.py +0 -0
  84. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/python/src/interfaces/http/controller.py +0 -0
  85. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/ignored/generated.ts +0 -0
  86. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/application/use_cases/activate.ts +0 -0
  87. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/domain/model/profile.tsx +0 -0
  88. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/domain/model/user.ts +0 -0
  89. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/domain/services/audit.js +0 -0
  90. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/domain/services/policy.ts +0 -0
  91. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/interfaces/http/controller.ts +0 -0
  92. {modwire-1.1.1 → modwire-2.1.0}/tests/apps/typescript/src/interfaces/http/view.jsx +0 -0
  93. {modwire-1.1.1 → modwire-2.1.0}/tests/test_standalone.py +0 -0
  94. {modwire-1.1.1 → modwire-2.1.0}/uv.lock +0 -0
@@ -32,14 +32,8 @@ actual output, and relevant versions for Python, Modwire, Node.js, or PHP.
32
32
 
33
33
  ## Pull Requests
34
34
 
35
- Before opening a pull request, run the local checks:
36
-
37
- ```bash
38
- uv run ruff check
39
- uv run pytest
40
- uv run python -m build --outdir dist
41
- uv run twine check dist/*
42
- ```
35
+ Before opening a pull request, run the local checks documented in
36
+ [Development checks](docs/wiki/Development-checks.md).
43
37
 
44
38
  Extractor changes should include focused tests under `tests/` and, when
45
39
  relevant, a small fixture under `tests/apps/`.
modwire-2.1.0/PKG-INFO ADDED
@@ -0,0 +1,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: modwire
3
+ Version: 2.1.0
4
+ Summary: Extract source-code dependencies and build dependency graphs.
5
+ Author: Tomasz Szpak
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/9orky/modwire
8
+ Project-URL: Repository, https://github.com/9orky/modwire
9
+ Project-URL: Issues, https://github.com/9orky/modwire/issues
10
+ Keywords: architecture,code-analysis,dependency-graph,static-analysis
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: pydantic>=2.8
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == "dev"
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Requires-Dist: ruff>=0.8; extra == "dev"
31
+ Requires-Dist: twine>=5.1; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # modwire
35
+
36
+ `modwire` extracts source-code structure and import dependencies from Python,
37
+ TypeScript/JavaScript, and PHP projects. It returns typed Python objects that
38
+ you can use to build dependency graphs, inspect symbols, and evaluate
39
+ architecture rules.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ python -m pip install modwire
45
+ ```
46
+
47
+ The Python extractor works with Python alone. TypeScript/JavaScript extraction
48
+ requires Node.js at runtime, and PHP extraction requires PHP at runtime.
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ from pathlib import Path
54
+
55
+ from modwire import discover_sources, extract_code
56
+
57
+ manifest = discover_sources(
58
+ "python",
59
+ Path("src"),
60
+ exclusions=("**/__pycache__/**",),
61
+ )
62
+ result = extract_code("python", Path("src"), exclusions=manifest.exclusions)
63
+
64
+ print(result.extraction_result.summary.files_checked)
65
+ print(result.graph.node_ids())
66
+ print([(edge.from_id, edge.to_id) for edge in result.graph.edges])
67
+ ```
68
+
69
+ Graph nodes use canonical extensionless source IDs, so equivalent Python,
70
+ TypeScript, and PHP projects can be compared through the same graph shape.
71
+
72
+ ## Supported Languages
73
+
74
+
75
+ ```python
76
+ from modwire import supported_languages
77
+
78
+ print(supported_languages())
79
+ # ("python", "typescript", "php")
80
+ ```
81
+
82
+ Language-specific source IDs can be normalized without running a full
83
+ extraction:
84
+
85
+ ```python
86
+ from modwire import normalize_source_id
87
+
88
+ print(normalize_source_id("typescript", "src/view.tsx"))
89
+ # "src/view"
90
+ ```
91
+
92
+ ## Extraction Options
93
+
94
+ Use `SourceRoots` when source IDs should be relative to a workspace root or to a
95
+ logical package prefix:
96
+
97
+ ```python
98
+ from pathlib import Path
99
+
100
+ from modwire import SourceRoots, extract_code
101
+
102
+ code_map = extract_code(
103
+ "python",
104
+ Path("packages/billing/src"),
105
+ source_roots=SourceRoots(
106
+ workspace_root=Path("."),
107
+ source_id_mode="relative_to_workspace_root",
108
+ ),
109
+ )
110
+ ```
111
+
112
+ Use `ExtractionCache` to reuse extraction output when files and extractor
113
+ implementations have not changed:
114
+
115
+ ```python
116
+ from pathlib import Path
117
+
118
+ from modwire import ExtractionCache, extract_code
119
+
120
+ code_map = extract_code(
121
+ "python",
122
+ Path("src"),
123
+ cache=ExtractionCache(Path(".modwire-cache")),
124
+ )
125
+
126
+ print(code_map.cache_status)
127
+ ```
128
+
129
+ ## Architecture Policy API
130
+
131
+ `modwire.architecture` exposes policy evaluation helpers for checking import
132
+ boundaries and common dependency-flow rules.
133
+
134
+ ```python
135
+ from modwire import extract_code
136
+ from modwire.architecture import (
137
+ ArchitectureBoundaryRule,
138
+ ArchitectureConfig,
139
+ ArchitectureFlowRules,
140
+ ArchitecturePolicyEvaluator,
141
+ ArchitectureRules,
142
+ ArchitectureTagRule,
143
+ render_violations,
144
+ supported_analyzers,
145
+ )
146
+
147
+ print(supported_analyzers())
148
+ # ("backward-flow", "no-reentry", "no-cycles")
149
+
150
+ code_map = extract_code("python", "src")
151
+ config = ArchitectureConfig(
152
+ language="python",
153
+ architecture_root="src",
154
+ rules=ArchitectureRules(
155
+ tags=(
156
+ ArchitectureTagRule(name="module", match="features/*"),
157
+ ArchitectureTagRule(name="ui", match="features/*/ui"),
158
+ ArchitectureTagRule(name="domain", match="features/*/domain"),
159
+ ),
160
+ boundaries=(
161
+ ArchitectureBoundaryRule(
162
+ source="features/*/ui",
163
+ disallow=("features/*/domain",),
164
+ allow_same_match=True,
165
+ ),
166
+ ),
167
+ flow=ArchitectureFlowRules(
168
+ layers=("domain", "ui"),
169
+ module_tag="module",
170
+ analyzers=("no-cycles",),
171
+ ),
172
+ ),
173
+ )
174
+
175
+ violations = ArchitecturePolicyEvaluator().evaluate(code_map.graph, config)
176
+ print(render_violations(tuple(violations)))
177
+ ```
178
+
179
+ Architecture insight helpers summarize ownership and graph pressure:
180
+
181
+ ```python
182
+ from modwire.architecture import coherence_summary, find_hotspots, map_code
183
+
184
+ architecture_map = map_code(code_map, config)
185
+ hotspots = find_hotspots(code_map, limit=5)
186
+ coherence = coherence_summary(code_map)
187
+
188
+ print(architecture_map.cross_module_dependencies)
189
+ print(hotspots)
190
+ print(coherence.external_dependencies)
191
+ ```
192
+
193
+ ## Shape Policy API
194
+
195
+ Shape policies evaluate file, symbol, callable, property, and import metadata:
196
+
197
+ ```python
198
+ from modwire import ShapePolicyEvaluator, evaluate_shape
199
+
200
+ violations = evaluate_shape(
201
+ code_map,
202
+ {
203
+ "max_functions_per_file": 5,
204
+ "max_methods_per_class": 10,
205
+ "allow_import_aliases": False,
206
+ "require_joined_imports": True,
207
+ },
208
+ )
209
+
210
+ same_result = ShapePolicyEvaluator().evaluate(code_map, {})
211
+ print([violation.to_dict() for violation in violations])
212
+ print(same_result)
213
+ ```
214
+
215
+ ## Serialization And Exports
216
+
217
+ `CodeMap` results can be serialized for later analysis, and export metadata can
218
+ be used to find currently unused public symbols:
219
+
220
+ ```python
221
+ from modwire import (
222
+ deserialize_code_map,
223
+ find_unused_exports,
224
+ serialize_code_map,
225
+ )
226
+
227
+ payload = serialize_code_map(code_map)
228
+ loaded = deserialize_code_map(payload)
229
+
230
+ unused = find_unused_exports(loaded.extraction_result)
231
+ print([(export.source_id, export.name) for export in unused])
232
+ ```
233
+
234
+ ## Development
235
+
236
+ See [Development checks](docs/wiki/Development-checks.md) for the local command
237
+ set used before pull requests and releases.
238
+
239
+ ## Contributing
240
+
241
+ Feature requests and bug reports are tracked through GitHub Issues:
242
+
243
+ - Open a feature request for new language support, graph metadata, architecture
244
+ rules, export formats, or documentation examples.
245
+ - Open a bug report for incorrect extraction results, graph edges, architecture
246
+ violations, packaging problems, or runtime failures.
247
+
248
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the information to include and the
249
+ checks to run before opening a pull request.
250
+
251
+ Starter wiki pages are tracked under [docs/wiki](docs/wiki) so the GitHub Wiki
252
+ can be initialized with the same guidance.
@@ -0,0 +1,219 @@
1
+ # modwire
2
+
3
+ `modwire` extracts source-code structure and import dependencies from Python,
4
+ TypeScript/JavaScript, and PHP projects. It returns typed Python objects that
5
+ you can use to build dependency graphs, inspect symbols, and evaluate
6
+ architecture rules.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ python -m pip install modwire
12
+ ```
13
+
14
+ The Python extractor works with Python alone. TypeScript/JavaScript extraction
15
+ requires Node.js at runtime, and PHP extraction requires PHP at runtime.
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ from pathlib import Path
21
+
22
+ from modwire import discover_sources, extract_code
23
+
24
+ manifest = discover_sources(
25
+ "python",
26
+ Path("src"),
27
+ exclusions=("**/__pycache__/**",),
28
+ )
29
+ result = extract_code("python", Path("src"), exclusions=manifest.exclusions)
30
+
31
+ print(result.extraction_result.summary.files_checked)
32
+ print(result.graph.node_ids())
33
+ print([(edge.from_id, edge.to_id) for edge in result.graph.edges])
34
+ ```
35
+
36
+ Graph nodes use canonical extensionless source IDs, so equivalent Python,
37
+ TypeScript, and PHP projects can be compared through the same graph shape.
38
+
39
+ ## Supported Languages
40
+
41
+
42
+ ```python
43
+ from modwire import supported_languages
44
+
45
+ print(supported_languages())
46
+ # ("python", "typescript", "php")
47
+ ```
48
+
49
+ Language-specific source IDs can be normalized without running a full
50
+ extraction:
51
+
52
+ ```python
53
+ from modwire import normalize_source_id
54
+
55
+ print(normalize_source_id("typescript", "src/view.tsx"))
56
+ # "src/view"
57
+ ```
58
+
59
+ ## Extraction Options
60
+
61
+ Use `SourceRoots` when source IDs should be relative to a workspace root or to a
62
+ logical package prefix:
63
+
64
+ ```python
65
+ from pathlib import Path
66
+
67
+ from modwire import SourceRoots, extract_code
68
+
69
+ code_map = extract_code(
70
+ "python",
71
+ Path("packages/billing/src"),
72
+ source_roots=SourceRoots(
73
+ workspace_root=Path("."),
74
+ source_id_mode="relative_to_workspace_root",
75
+ ),
76
+ )
77
+ ```
78
+
79
+ Use `ExtractionCache` to reuse extraction output when files and extractor
80
+ implementations have not changed:
81
+
82
+ ```python
83
+ from pathlib import Path
84
+
85
+ from modwire import ExtractionCache, extract_code
86
+
87
+ code_map = extract_code(
88
+ "python",
89
+ Path("src"),
90
+ cache=ExtractionCache(Path(".modwire-cache")),
91
+ )
92
+
93
+ print(code_map.cache_status)
94
+ ```
95
+
96
+ ## Architecture Policy API
97
+
98
+ `modwire.architecture` exposes policy evaluation helpers for checking import
99
+ boundaries and common dependency-flow rules.
100
+
101
+ ```python
102
+ from modwire import extract_code
103
+ from modwire.architecture import (
104
+ ArchitectureBoundaryRule,
105
+ ArchitectureConfig,
106
+ ArchitectureFlowRules,
107
+ ArchitecturePolicyEvaluator,
108
+ ArchitectureRules,
109
+ ArchitectureTagRule,
110
+ render_violations,
111
+ supported_analyzers,
112
+ )
113
+
114
+ print(supported_analyzers())
115
+ # ("backward-flow", "no-reentry", "no-cycles")
116
+
117
+ code_map = extract_code("python", "src")
118
+ config = ArchitectureConfig(
119
+ language="python",
120
+ architecture_root="src",
121
+ rules=ArchitectureRules(
122
+ tags=(
123
+ ArchitectureTagRule(name="module", match="features/*"),
124
+ ArchitectureTagRule(name="ui", match="features/*/ui"),
125
+ ArchitectureTagRule(name="domain", match="features/*/domain"),
126
+ ),
127
+ boundaries=(
128
+ ArchitectureBoundaryRule(
129
+ source="features/*/ui",
130
+ disallow=("features/*/domain",),
131
+ allow_same_match=True,
132
+ ),
133
+ ),
134
+ flow=ArchitectureFlowRules(
135
+ layers=("domain", "ui"),
136
+ module_tag="module",
137
+ analyzers=("no-cycles",),
138
+ ),
139
+ ),
140
+ )
141
+
142
+ violations = ArchitecturePolicyEvaluator().evaluate(code_map.graph, config)
143
+ print(render_violations(tuple(violations)))
144
+ ```
145
+
146
+ Architecture insight helpers summarize ownership and graph pressure:
147
+
148
+ ```python
149
+ from modwire.architecture import coherence_summary, find_hotspots, map_code
150
+
151
+ architecture_map = map_code(code_map, config)
152
+ hotspots = find_hotspots(code_map, limit=5)
153
+ coherence = coherence_summary(code_map)
154
+
155
+ print(architecture_map.cross_module_dependencies)
156
+ print(hotspots)
157
+ print(coherence.external_dependencies)
158
+ ```
159
+
160
+ ## Shape Policy API
161
+
162
+ Shape policies evaluate file, symbol, callable, property, and import metadata:
163
+
164
+ ```python
165
+ from modwire import ShapePolicyEvaluator, evaluate_shape
166
+
167
+ violations = evaluate_shape(
168
+ code_map,
169
+ {
170
+ "max_functions_per_file": 5,
171
+ "max_methods_per_class": 10,
172
+ "allow_import_aliases": False,
173
+ "require_joined_imports": True,
174
+ },
175
+ )
176
+
177
+ same_result = ShapePolicyEvaluator().evaluate(code_map, {})
178
+ print([violation.to_dict() for violation in violations])
179
+ print(same_result)
180
+ ```
181
+
182
+ ## Serialization And Exports
183
+
184
+ `CodeMap` results can be serialized for later analysis, and export metadata can
185
+ be used to find currently unused public symbols:
186
+
187
+ ```python
188
+ from modwire import (
189
+ deserialize_code_map,
190
+ find_unused_exports,
191
+ serialize_code_map,
192
+ )
193
+
194
+ payload = serialize_code_map(code_map)
195
+ loaded = deserialize_code_map(payload)
196
+
197
+ unused = find_unused_exports(loaded.extraction_result)
198
+ print([(export.source_id, export.name) for export in unused])
199
+ ```
200
+
201
+ ## Development
202
+
203
+ See [Development checks](docs/wiki/Development-checks.md) for the local command
204
+ set used before pull requests and releases.
205
+
206
+ ## Contributing
207
+
208
+ Feature requests and bug reports are tracked through GitHub Issues:
209
+
210
+ - Open a feature request for new language support, graph metadata, architecture
211
+ rules, export formats, or documentation examples.
212
+ - Open a bug report for incorrect extraction results, graph edges, architecture
213
+ violations, packaging problems, or runtime failures.
214
+
215
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the information to include and the
216
+ checks to run before opening a pull request.
217
+
218
+ Starter wiki pages are tracked under [docs/wiki](docs/wiki) so the GitHub Wiki
219
+ can be initialized with the same guidance.
@@ -9,6 +9,9 @@ uv run python -m build --outdir dist
9
9
  uv run twine check dist/*
10
10
  ```
11
11
 
12
+ This page is the canonical local-check list; README and contributor guidance
13
+ link here to avoid release-process drift.
14
+
12
15
  Extractor changes should include focused tests under `tests/` and, when useful,
13
16
  small source fixtures under `tests/apps/`.
14
17
 
@@ -7,6 +7,9 @@ rules.
7
7
 
8
8
  ## Start Here
9
9
 
10
+ - README examples cover extraction manifests, source roots, caching,
11
+ architecture policies, architecture insights, shape policies, serialization,
12
+ and unused export checks.
10
13
  - [Reporting bugs](Reporting-bugs.md)
11
14
  - [Requesting features](Requesting-features.md)
12
15
  - [Development checks](Development-checks.md)
@@ -0,0 +1,96 @@
1
+ from ._version import __version__
2
+ from .callables import (
3
+ CallableReportEntry,
4
+ callable_report_entries,
5
+ render_callable_report,
6
+ structured_callable_report,
7
+ )
8
+ from .extraction import (
9
+ CodeMap,
10
+ CodeMapSerializationError,
11
+ ExtractionCache,
12
+ SourceChangedDuringExtractionError,
13
+ SourceManifest,
14
+ SourceManifestEntry,
15
+ SourceRoots,
16
+ deserialize_code_map,
17
+ discover_sources,
18
+ extract_code,
19
+ serialize_code_map,
20
+ )
21
+ from .extractors.loader import (
22
+ UnsupportedLanguageError,
23
+ normalize_source_id,
24
+ supported_languages,
25
+ )
26
+ from .exports import UnusedExport, find_unused_exports
27
+ from .graph import DependencyGraph, Edge, Node, build_dependency_graph
28
+ from .metadata import (
29
+ EXTRACTION_SCHEMA_VERSION,
30
+ PUBLIC_API_STABILITY,
31
+ ExtractorRuntimeError,
32
+ LanguageInfo,
33
+ MissingRuntimeError,
34
+ RuntimeInfo,
35
+ extraction_implementation_stamp,
36
+ language,
37
+ languages,
38
+ require_runtime,
39
+ runtime_diagnostics,
40
+ )
41
+ from .shape import (
42
+ ShapeConfig,
43
+ ShapeConfigError,
44
+ ShapeConfigIssue,
45
+ ShapePolicyEvaluator,
46
+ ShapeViolation,
47
+ evaluate_shape,
48
+ validate_shape_config,
49
+ )
50
+
51
+
52
+ __all__ = [
53
+ "CodeMap",
54
+ "CodeMapSerializationError",
55
+ "CallableReportEntry",
56
+ "DependencyGraph",
57
+ "Edge",
58
+ "EXTRACTION_SCHEMA_VERSION",
59
+ "ExtractionCache",
60
+ "ExtractorRuntimeError",
61
+ "LanguageInfo",
62
+ "MissingRuntimeError",
63
+ "Node",
64
+ "PUBLIC_API_STABILITY",
65
+ "RuntimeInfo",
66
+ "ShapeConfig",
67
+ "ShapeConfigError",
68
+ "ShapeConfigIssue",
69
+ "ShapePolicyEvaluator",
70
+ "ShapeViolation",
71
+ "SourceChangedDuringExtractionError",
72
+ "SourceManifest",
73
+ "SourceManifestEntry",
74
+ "SourceRoots",
75
+ "UnsupportedLanguageError",
76
+ "UnusedExport",
77
+ "__version__",
78
+ "build_dependency_graph",
79
+ "callable_report_entries",
80
+ "deserialize_code_map",
81
+ "discover_sources",
82
+ "evaluate_shape",
83
+ "extraction_implementation_stamp",
84
+ "extract_code",
85
+ "find_unused_exports",
86
+ "language",
87
+ "languages",
88
+ "normalize_source_id",
89
+ "require_runtime",
90
+ "render_callable_report",
91
+ "runtime_diagnostics",
92
+ "serialize_code_map",
93
+ "structured_callable_report",
94
+ "supported_languages",
95
+ "validate_shape_config",
96
+ ]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.1.1'
22
- __version_tuple__ = version_tuple = (1, 1, 1)
21
+ __version__ = version = '2.1.0'
22
+ __version_tuple__ = version_tuple = (2, 1, 0)
23
23
 
24
- __commit_id__ = commit_id = 'gbfbd18f94'
24
+ __commit_id__ = commit_id = 'g54f33f5a3'
@@ -0,0 +1,59 @@
1
+ from .analyzers import AnalyzerInfo, analyzer_metadata, supported_analyzers
2
+ from .config import (
3
+ ArchitectureBoundaryRule,
4
+ ArchitectureConfig,
5
+ ArchitectureConfigError,
6
+ ArchitectureConfigIssue,
7
+ ArchitectureFlowRules,
8
+ ArchitectureRules,
9
+ ArchitectureTagRule,
10
+ validate_policy_config,
11
+ )
12
+ from .insights import (
13
+ ArchitectureCluster,
14
+ ArchitectureMap,
15
+ CoherenceSummary,
16
+ CrossModuleDependency,
17
+ DependencyHotspot,
18
+ cluster_code,
19
+ coherence_summary,
20
+ find_hotspots,
21
+ map_code,
22
+ )
23
+ from .matching import TagMap, TagMatch, TagMatcher
24
+ from .policy import ArchitecturePolicyEvaluator
25
+ from .render import render_violation_payload, render_violations, structured_groups
26
+ from .violations import EdgeRuleViolation, FlowViolation, violation_to_dict
27
+
28
+ __all__ = [
29
+ "AnalyzerInfo",
30
+ "ArchitectureCluster",
31
+ "ArchitectureBoundaryRule",
32
+ "ArchitectureConfig",
33
+ "ArchitectureConfigError",
34
+ "ArchitectureConfigIssue",
35
+ "ArchitectureFlowRules",
36
+ "ArchitectureMap",
37
+ "ArchitecturePolicyEvaluator",
38
+ "ArchitectureRules",
39
+ "ArchitectureTagRule",
40
+ "CoherenceSummary",
41
+ "CrossModuleDependency",
42
+ "DependencyHotspot",
43
+ "EdgeRuleViolation",
44
+ "FlowViolation",
45
+ "TagMap",
46
+ "TagMatch",
47
+ "TagMatcher",
48
+ "analyzer_metadata",
49
+ "cluster_code",
50
+ "coherence_summary",
51
+ "find_hotspots",
52
+ "map_code",
53
+ "render_violation_payload",
54
+ "render_violations",
55
+ "structured_groups",
56
+ "supported_analyzers",
57
+ "validate_policy_config",
58
+ "violation_to_dict",
59
+ ]
@@ -13,10 +13,29 @@ class FlowAnalyzer:
13
13
  run: Callable
14
14
 
15
15
 
16
+ @dataclass(frozen=True)
17
+ class AnalyzerInfo:
18
+ name: str
19
+ title: str
20
+
21
+ def to_dict(self) -> dict[str, str]:
22
+ return {
23
+ "name": self.name,
24
+ "title": self.title,
25
+ }
26
+
27
+
16
28
  def supported_analyzers() -> tuple[str, ...]:
17
29
  return tuple(_ANALYZERS)
18
30
 
19
31
 
32
+ def analyzer_metadata() -> tuple[AnalyzerInfo, ...]:
33
+ return tuple(
34
+ AnalyzerInfo(name=analyzer.name, title=analyzer.title)
35
+ for analyzer in _ANALYZERS.values()
36
+ )
37
+
38
+
20
39
  def analyzer_title(name: str) -> str:
21
40
  return _ANALYZERS[name].title
22
41