modwire 1.0.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.
- modwire-1.0.0/.github/workflows/ci.yml +59 -0
- modwire-1.0.0/.github/workflows/release.yml +215 -0
- modwire-1.0.0/.gitignore +10 -0
- modwire-1.0.0/LICENSE +21 -0
- modwire-1.0.0/PKG-INFO +111 -0
- modwire-1.0.0/README.md +78 -0
- modwire-1.0.0/pyproject.toml +82 -0
- modwire-1.0.0/setup.cfg +4 -0
- modwire-1.0.0/show_test_source_files.py +28 -0
- modwire-1.0.0/src/modwire/__init__.py +15 -0
- modwire-1.0.0/src/modwire/_version.py +24 -0
- modwire-1.0.0/src/modwire/architecture/__init__.py +10 -0
- modwire-1.0.0/src/modwire/architecture/analyzers.py +140 -0
- modwire-1.0.0/src/modwire/architecture/matching.py +58 -0
- modwire-1.0.0/src/modwire/architecture/policy.py +63 -0
- modwire-1.0.0/src/modwire/architecture/render.py +98 -0
- modwire-1.0.0/src/modwire/architecture/violations.py +24 -0
- modwire-1.0.0/src/modwire/definitions.py +101 -0
- modwire-1.0.0/src/modwire/extraction.py +73 -0
- modwire-1.0.0/src/modwire/extractors/__init__.py +5 -0
- modwire-1.0.0/src/modwire/extractors/base.py +177 -0
- modwire-1.0.0/src/modwire/extractors/loader.py +31 -0
- modwire-1.0.0/src/modwire/extractors/php.py +170 -0
- modwire-1.0.0/src/modwire/extractors/python.py +113 -0
- modwire-1.0.0/src/modwire/extractors/scripts/php_extractor.php +816 -0
- modwire-1.0.0/src/modwire/extractors/scripts/python_extractor.py +398 -0
- modwire-1.0.0/src/modwire/extractors/scripts/typescript_extractor.js +1030 -0
- modwire-1.0.0/src/modwire/extractors/typescript.py +48 -0
- modwire-1.0.0/src/modwire/graph.py +56 -0
- modwire-1.0.0/src/modwire.egg-info/PKG-INFO +111 -0
- modwire-1.0.0/src/modwire.egg-info/SOURCES.txt +54 -0
- modwire-1.0.0/src/modwire.egg-info/dependency_links.txt +1 -0
- modwire-1.0.0/src/modwire.egg-info/requires.txt +7 -0
- modwire-1.0.0/src/modwire.egg-info/top_level.txt +1 -0
- modwire-1.0.0/tests/apps/php/ignored/generated.php +6 -0
- modwire-1.0.0/tests/apps/php/src/application/use_cases/activate.php +28 -0
- modwire-1.0.0/tests/apps/php/src/domain/model/user.php +16 -0
- modwire-1.0.0/tests/apps/php/src/domain/services/policy.php +17 -0
- modwire-1.0.0/tests/apps/php/src/interfaces/http/controller.php +18 -0
- modwire-1.0.0/tests/apps/python/ignored/generated.py +5 -0
- modwire-1.0.0/tests/apps/python/src/application/use_cases/activate.py +26 -0
- modwire-1.0.0/tests/apps/python/src/domain/model/user.py +11 -0
- modwire-1.0.0/tests/apps/python/src/domain/services/policy.py +10 -0
- modwire-1.0.0/tests/apps/python/src/interfaces/http/controller.py +14 -0
- modwire-1.0.0/tests/apps/typescript/ignored/generated.ts +3 -0
- modwire-1.0.0/tests/apps/typescript/src/application/use_cases/activate.ts +24 -0
- modwire-1.0.0/tests/apps/typescript/src/domain/model/profile.tsx +5 -0
- modwire-1.0.0/tests/apps/typescript/src/domain/model/user.ts +11 -0
- modwire-1.0.0/tests/apps/typescript/src/domain/services/audit.js +3 -0
- modwire-1.0.0/tests/apps/typescript/src/domain/services/policy.ts +11 -0
- modwire-1.0.0/tests/apps/typescript/src/interfaces/http/controller.ts +13 -0
- modwire-1.0.0/tests/apps/typescript/src/interfaces/http/view.jsx +3 -0
- modwire-1.0.0/tests/test_api.py +1077 -0
- modwire-1.0.0/tests/test_architecture_api.py +43 -0
- modwire-1.0.0/tests/test_standalone.py +44 -0
- modwire-1.0.0/uv.lock +785 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out repository
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Set up Node.js
|
|
30
|
+
uses: actions/setup-node@v4
|
|
31
|
+
with:
|
|
32
|
+
node-version: "20"
|
|
33
|
+
|
|
34
|
+
- name: Set up PHP
|
|
35
|
+
uses: shivammathur/setup-php@v2
|
|
36
|
+
with:
|
|
37
|
+
php-version: "8.3"
|
|
38
|
+
|
|
39
|
+
- name: Clean packaging artifacts
|
|
40
|
+
run: rm -rf .dev build dist src/*.egg-info
|
|
41
|
+
|
|
42
|
+
- name: Install package and packaging tools
|
|
43
|
+
run: |
|
|
44
|
+
python -m pip install --upgrade pip
|
|
45
|
+
python -m pip install -e .[dev]
|
|
46
|
+
|
|
47
|
+
- name: Run unit tests
|
|
48
|
+
run: python -m pytest
|
|
49
|
+
|
|
50
|
+
- name: Validate extractor syntax
|
|
51
|
+
run: |
|
|
52
|
+
node --check src/modwire/extractors/scripts/typescript_extractor.js
|
|
53
|
+
php -l src/modwire/extractors/scripts/php_extractor.php
|
|
54
|
+
|
|
55
|
+
- name: Build distributions
|
|
56
|
+
run: python -m build --outdir dist
|
|
57
|
+
|
|
58
|
+
- name: Check distribution metadata
|
|
59
|
+
run: python -m twine check dist/*
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types:
|
|
6
|
+
- published
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build and verify release artifacts
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Check out repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
|
|
25
|
+
- name: Check out release tag
|
|
26
|
+
if: github.event_name == 'release'
|
|
27
|
+
run: |
|
|
28
|
+
git fetch --force --tags origin "refs/tags/${{ github.event.release.tag_name }}:refs/tags/${{ github.event.release.tag_name }}"
|
|
29
|
+
git -c advice.detachedHead=false checkout --force "refs/tags/${{ github.event.release.tag_name }}"
|
|
30
|
+
git clean -ffdx
|
|
31
|
+
|
|
32
|
+
- name: Verify release checkout
|
|
33
|
+
if: github.event_name == 'release'
|
|
34
|
+
run: |
|
|
35
|
+
tag_name="${{ github.event.release.tag_name }}"
|
|
36
|
+
tag_sha="$(git rev-list -n 1 "$tag_name")"
|
|
37
|
+
head_sha="$(git rev-parse HEAD)"
|
|
38
|
+
|
|
39
|
+
echo "Release tag: $tag_name"
|
|
40
|
+
echo "Tag commit: $tag_sha"
|
|
41
|
+
echo "HEAD commit: $head_sha"
|
|
42
|
+
git describe --tags --dirty --always
|
|
43
|
+
|
|
44
|
+
if [ "$head_sha" != "$tag_sha" ]; then
|
|
45
|
+
echo "Release checkout mismatch: HEAD is not the release tag commit." >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
|
|
54
|
+
- name: Set up Node.js
|
|
55
|
+
uses: actions/setup-node@v4
|
|
56
|
+
with:
|
|
57
|
+
node-version: "20"
|
|
58
|
+
|
|
59
|
+
- name: Set up PHP
|
|
60
|
+
uses: shivammathur/setup-php@v2
|
|
61
|
+
with:
|
|
62
|
+
php-version: "8.3"
|
|
63
|
+
|
|
64
|
+
- name: Clean packaging artifacts
|
|
65
|
+
run: rm -rf .dev build dist src/*.egg-info
|
|
66
|
+
|
|
67
|
+
- name: Install package and release tooling
|
|
68
|
+
run: |
|
|
69
|
+
python -m pip install --upgrade pip
|
|
70
|
+
python -m pip install -e .[dev]
|
|
71
|
+
|
|
72
|
+
- name: Verify release tag matches derived package version
|
|
73
|
+
if: github.event_name == 'release'
|
|
74
|
+
run: |
|
|
75
|
+
python - <<'PY'
|
|
76
|
+
import sys
|
|
77
|
+
from importlib.metadata import version
|
|
78
|
+
|
|
79
|
+
package_version = version("modwire")
|
|
80
|
+
tag_name = "${{ github.event.release.tag_name }}"
|
|
81
|
+
expected_tag = f"v{package_version}"
|
|
82
|
+
|
|
83
|
+
if tag_name != expected_tag:
|
|
84
|
+
print(
|
|
85
|
+
f"Release tag/version mismatch: tag is '{tag_name}' but derived package version is '{package_version}'. Expected tag '{expected_tag}'.",
|
|
86
|
+
file=sys.stderr,
|
|
87
|
+
)
|
|
88
|
+
raise SystemExit(1)
|
|
89
|
+
|
|
90
|
+
print(f"Verified release tag {tag_name} matches package version {package_version}.")
|
|
91
|
+
PY
|
|
92
|
+
|
|
93
|
+
- name: Run tests
|
|
94
|
+
run: python -m pytest
|
|
95
|
+
|
|
96
|
+
- name: Validate extractor syntax
|
|
97
|
+
run: |
|
|
98
|
+
node --check src/modwire/extractors/scripts/typescript_extractor.js
|
|
99
|
+
php -l src/modwire/extractors/scripts/php_extractor.php
|
|
100
|
+
|
|
101
|
+
- name: Build distributions
|
|
102
|
+
run: python -m build --outdir dist
|
|
103
|
+
|
|
104
|
+
- name: Verify built artifact version
|
|
105
|
+
if: github.event_name == 'release'
|
|
106
|
+
run: |
|
|
107
|
+
python - <<'PY'
|
|
108
|
+
import sys
|
|
109
|
+
import zipfile
|
|
110
|
+
from email.parser import Parser
|
|
111
|
+
from pathlib import Path
|
|
112
|
+
|
|
113
|
+
expected_version = "${{ github.event.release.tag_name }}".removeprefix("v")
|
|
114
|
+
wheels = sorted(Path("dist").glob("*.whl"))
|
|
115
|
+
versions: dict[str, str] = {}
|
|
116
|
+
|
|
117
|
+
for wheel in wheels:
|
|
118
|
+
with zipfile.ZipFile(wheel) as archive:
|
|
119
|
+
metadata_name = next(
|
|
120
|
+
name for name in archive.namelist()
|
|
121
|
+
if name.endswith(".dist-info/METADATA")
|
|
122
|
+
)
|
|
123
|
+
metadata = Parser().parsestr(
|
|
124
|
+
archive.read(metadata_name).decode("utf-8")
|
|
125
|
+
)
|
|
126
|
+
versions[metadata["Name"]] = metadata["Version"]
|
|
127
|
+
|
|
128
|
+
package_version = versions.get("modwire")
|
|
129
|
+
if package_version != expected_version:
|
|
130
|
+
print(
|
|
131
|
+
f"Built artifact version mismatch: modwire is "
|
|
132
|
+
f"'{package_version}' but release tag expects "
|
|
133
|
+
f"'{expected_version}'.",
|
|
134
|
+
file=sys.stderr,
|
|
135
|
+
)
|
|
136
|
+
raise SystemExit(1)
|
|
137
|
+
|
|
138
|
+
print(f"Verified built artifact matches {expected_version}.")
|
|
139
|
+
PY
|
|
140
|
+
|
|
141
|
+
- name: Check distribution metadata
|
|
142
|
+
run: python -m twine check dist/*
|
|
143
|
+
|
|
144
|
+
- name: Upload distribution artifacts
|
|
145
|
+
uses: actions/upload-artifact@v4
|
|
146
|
+
with:
|
|
147
|
+
name: python-package-distributions
|
|
148
|
+
path: dist/
|
|
149
|
+
if-no-files-found: error
|
|
150
|
+
|
|
151
|
+
github-release-assets:
|
|
152
|
+
name: Attach artifacts to GitHub release
|
|
153
|
+
if: github.event_name == 'release'
|
|
154
|
+
needs: build
|
|
155
|
+
runs-on: ubuntu-latest
|
|
156
|
+
permissions:
|
|
157
|
+
contents: write
|
|
158
|
+
|
|
159
|
+
steps:
|
|
160
|
+
- name: Download distribution artifacts
|
|
161
|
+
uses: actions/download-artifact@v4
|
|
162
|
+
with:
|
|
163
|
+
name: python-package-distributions
|
|
164
|
+
path: dist/
|
|
165
|
+
|
|
166
|
+
- name: Upload release assets
|
|
167
|
+
uses: softprops/action-gh-release@v2
|
|
168
|
+
with:
|
|
169
|
+
files: dist/*
|
|
170
|
+
|
|
171
|
+
publish-testpypi:
|
|
172
|
+
name: Publish to TestPyPI
|
|
173
|
+
if: github.event_name == 'release' && github.event.release.prerelease
|
|
174
|
+
needs: build
|
|
175
|
+
runs-on: ubuntu-latest
|
|
176
|
+
environment:
|
|
177
|
+
name: testpypi
|
|
178
|
+
url: https://test.pypi.org/project/modwire/
|
|
179
|
+
permissions:
|
|
180
|
+
id-token: write
|
|
181
|
+
|
|
182
|
+
steps:
|
|
183
|
+
- name: Download distribution artifacts
|
|
184
|
+
uses: actions/download-artifact@v4
|
|
185
|
+
with:
|
|
186
|
+
name: python-package-distributions
|
|
187
|
+
path: dist/
|
|
188
|
+
|
|
189
|
+
- name: Publish to TestPyPI with Trusted Publishing
|
|
190
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
191
|
+
with:
|
|
192
|
+
repository-url: https://test.pypi.org/legacy/
|
|
193
|
+
|
|
194
|
+
publish-pypi:
|
|
195
|
+
name: Publish to PyPI
|
|
196
|
+
if: github.event_name == 'release' && !github.event.release.prerelease
|
|
197
|
+
needs:
|
|
198
|
+
- build
|
|
199
|
+
- github-release-assets
|
|
200
|
+
runs-on: ubuntu-latest
|
|
201
|
+
environment:
|
|
202
|
+
name: pypi
|
|
203
|
+
url: https://pypi.org/project/modwire/
|
|
204
|
+
permissions:
|
|
205
|
+
id-token: write
|
|
206
|
+
|
|
207
|
+
steps:
|
|
208
|
+
- name: Download distribution artifacts
|
|
209
|
+
uses: actions/download-artifact@v4
|
|
210
|
+
with:
|
|
211
|
+
name: python-package-distributions
|
|
212
|
+
path: dist/
|
|
213
|
+
|
|
214
|
+
- name: Publish to PyPI with Trusted Publishing
|
|
215
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
modwire-1.0.0/.gitignore
ADDED
modwire-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tomasz Szpak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
modwire-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modwire
|
|
3
|
+
Version: 1.0.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/codemap
|
|
8
|
+
Project-URL: Repository, https://github.com/9orky/codemap
|
|
9
|
+
Project-URL: Issues, https://github.com/9orky/codemap/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 extract_code
|
|
56
|
+
|
|
57
|
+
result = extract_code(
|
|
58
|
+
"python",
|
|
59
|
+
Path("src"),
|
|
60
|
+
exclusions=("**/__pycache__/**",),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
print(result.extraction_result.summary.files_checked)
|
|
64
|
+
print(result.graph.node_ids())
|
|
65
|
+
print([(edge.from_id, edge.to_id) for edge in result.graph.edges])
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Graph nodes use canonical extensionless source IDs, so equivalent Python,
|
|
69
|
+
TypeScript, and PHP projects can be compared through the same graph shape.
|
|
70
|
+
|
|
71
|
+
## Supported Languages
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from modwire import supported_languages
|
|
75
|
+
|
|
76
|
+
print(supported_languages())
|
|
77
|
+
# ("python", "typescript", "php")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Language-specific source IDs can be normalized without running a full
|
|
81
|
+
extraction:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from modwire import normalize_source_id
|
|
85
|
+
|
|
86
|
+
print(normalize_source_id("typescript", "src/view.tsx"))
|
|
87
|
+
# "src/view"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Architecture Policy API
|
|
91
|
+
|
|
92
|
+
`modwire.architecture` exposes policy evaluation helpers for checking import
|
|
93
|
+
boundaries and common dependency-flow rules.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from modwire.architecture import ArchitecturePolicyEvaluator, supported_analyzers
|
|
97
|
+
|
|
98
|
+
print(supported_analyzers())
|
|
99
|
+
# ("backward-flow", "no-reentry", "no-cycles")
|
|
100
|
+
|
|
101
|
+
evaluator = ArchitecturePolicyEvaluator()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Development
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv run ruff check
|
|
108
|
+
uv run pytest
|
|
109
|
+
uv run python -m build --outdir dist
|
|
110
|
+
uv run twine check dist/*
|
|
111
|
+
```
|
modwire-1.0.0/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
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 extract_code
|
|
23
|
+
|
|
24
|
+
result = extract_code(
|
|
25
|
+
"python",
|
|
26
|
+
Path("src"),
|
|
27
|
+
exclusions=("**/__pycache__/**",),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
print(result.extraction_result.summary.files_checked)
|
|
31
|
+
print(result.graph.node_ids())
|
|
32
|
+
print([(edge.from_id, edge.to_id) for edge in result.graph.edges])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Graph nodes use canonical extensionless source IDs, so equivalent Python,
|
|
36
|
+
TypeScript, and PHP projects can be compared through the same graph shape.
|
|
37
|
+
|
|
38
|
+
## Supported Languages
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from modwire import supported_languages
|
|
42
|
+
|
|
43
|
+
print(supported_languages())
|
|
44
|
+
# ("python", "typescript", "php")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Language-specific source IDs can be normalized without running a full
|
|
48
|
+
extraction:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from modwire import normalize_source_id
|
|
52
|
+
|
|
53
|
+
print(normalize_source_id("typescript", "src/view.tsx"))
|
|
54
|
+
# "src/view"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Architecture Policy API
|
|
58
|
+
|
|
59
|
+
`modwire.architecture` exposes policy evaluation helpers for checking import
|
|
60
|
+
boundaries and common dependency-flow rules.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from modwire.architecture import ArchitecturePolicyEvaluator, supported_analyzers
|
|
64
|
+
|
|
65
|
+
print(supported_analyzers())
|
|
66
|
+
# ("backward-flow", "no-reentry", "no-cycles")
|
|
67
|
+
|
|
68
|
+
evaluator = ArchitecturePolicyEvaluator()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
uv run ruff check
|
|
75
|
+
uv run pytest
|
|
76
|
+
uv run python -m build --outdir dist
|
|
77
|
+
uv run twine check dist/*
|
|
78
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "setuptools-scm[toml]>=8", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modwire"
|
|
7
|
+
description = "Extract source-code dependencies and build dependency graphs."
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tomasz Szpak"},
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"architecture",
|
|
17
|
+
"code-analysis",
|
|
18
|
+
"dependency-graph",
|
|
19
|
+
"static-analysis",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Programming Language :: Python",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Topic :: Software Development",
|
|
32
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
34
|
+
]
|
|
35
|
+
dependencies = [
|
|
36
|
+
"pydantic>=2.8",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/9orky/codemap"
|
|
41
|
+
Repository = "https://github.com/9orky/codemap"
|
|
42
|
+
Issues = "https://github.com/9orky/codemap/issues"
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
dev = [
|
|
46
|
+
"build>=1.2",
|
|
47
|
+
"pytest>=8.0",
|
|
48
|
+
"ruff>=0.8",
|
|
49
|
+
"twine>=5.1",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"build>=1.2",
|
|
55
|
+
"pytest>=8.0",
|
|
56
|
+
"ruff>=0.8",
|
|
57
|
+
"twine>=5.1",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
62
|
+
cache_dir = ".dev/pytest_cache"
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
cache-dir = ".dev/ruff_cache"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools]
|
|
68
|
+
package-dir = {"" = "src"}
|
|
69
|
+
include-package-data = true
|
|
70
|
+
|
|
71
|
+
[tool.setuptools.packages.find]
|
|
72
|
+
where = ["src"]
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.package-data]
|
|
75
|
+
modwire = [
|
|
76
|
+
"extractors/scripts/*",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.setuptools_scm]
|
|
80
|
+
fallback_version = "0.0.0.dev0"
|
|
81
|
+
local_scheme = "no-local-version"
|
|
82
|
+
version_file = "src/modwire/_version.py"
|
modwire-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
PACKAGE_ROOT = Path(__file__).parent
|
|
9
|
+
APPS_ROOT = PACKAGE_ROOT / "tests" / "apps"
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, str(PACKAGE_ROOT / "src"))
|
|
12
|
+
|
|
13
|
+
from modwire import extract_code # noqa: E402
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> int:
|
|
17
|
+
for language in ("python", "typescript", "php"):
|
|
18
|
+
result = extract_code(language, APPS_ROOT / language, ("ignored/**",))
|
|
19
|
+
print(f"\n=== {language.upper()} SourceFile JSONs ===")
|
|
20
|
+
for source_id, source_file in sorted(result.extraction_result.files.items()):
|
|
21
|
+
print(f"\n--- {source_id} ---")
|
|
22
|
+
print(json.dumps(source_file.model_dump(), indent=2))
|
|
23
|
+
|
|
24
|
+
return 0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if __name__ == "__main__":
|
|
28
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .extraction import CodeMap, extract_code
|
|
2
|
+
from .extractors.loader import normalize_source_id, supported_languages
|
|
3
|
+
from .graph import DependencyGraph, Edge, Node, build_dependency_graph
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"CodeMap",
|
|
8
|
+
"DependencyGraph",
|
|
9
|
+
"Edge",
|
|
10
|
+
"Node",
|
|
11
|
+
"build_dependency_graph",
|
|
12
|
+
"extract_code",
|
|
13
|
+
"normalize_source_id",
|
|
14
|
+
"supported_languages",
|
|
15
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '1.0.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'gb2874aa61'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .analyzers import supported_analyzers
|
|
2
|
+
from .policy import ArchitecturePolicyEvaluator
|
|
3
|
+
from .violations import EdgeRuleViolation, FlowViolation
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"ArchitecturePolicyEvaluator",
|
|
7
|
+
"EdgeRuleViolation",
|
|
8
|
+
"FlowViolation",
|
|
9
|
+
"supported_analyzers",
|
|
10
|
+
]
|