python-dependency-linter 0.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 (43) hide show
  1. python_dependency_linter-0.1.0/.github/dependabot.yml +10 -0
  2. python_dependency_linter-0.1.0/.github/workflows/ci.yaml +32 -0
  3. python_dependency_linter-0.1.0/.github/workflows/publish.yaml +59 -0
  4. python_dependency_linter-0.1.0/.gitignore +8 -0
  5. python_dependency_linter-0.1.0/.pre-commit-config.yaml +7 -0
  6. python_dependency_linter-0.1.0/.pre-commit-hooks.yaml +6 -0
  7. python_dependency_linter-0.1.0/LICENSE +21 -0
  8. python_dependency_linter-0.1.0/PKG-INFO +200 -0
  9. python_dependency_linter-0.1.0/README.md +175 -0
  10. python_dependency_linter-0.1.0/cliff.toml +38 -0
  11. python_dependency_linter-0.1.0/docs/superpowers/plans/2026-03-30-python-dependency-linter.md +1343 -0
  12. python_dependency_linter-0.1.0/docs/superpowers/specs/2026-03-30-python-dependency-linter-design.md +235 -0
  13. python_dependency_linter-0.1.0/pyproject.toml +49 -0
  14. python_dependency_linter-0.1.0/python_dependency_linter/__init__.py +1 -0
  15. python_dependency_linter-0.1.0/python_dependency_linter/checker.py +94 -0
  16. python_dependency_linter-0.1.0/python_dependency_linter/cli.py +96 -0
  17. python_dependency_linter-0.1.0/python_dependency_linter/config.py +76 -0
  18. python_dependency_linter-0.1.0/python_dependency_linter/matcher.py +61 -0
  19. python_dependency_linter-0.1.0/python_dependency_linter/parser.py +27 -0
  20. python_dependency_linter-0.1.0/python_dependency_linter/reporter.py +17 -0
  21. python_dependency_linter-0.1.0/python_dependency_linter/resolver.py +26 -0
  22. python_dependency_linter-0.1.0/tests/fixtures/sample_config.yaml +12 -0
  23. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/__init__.py +0 -0
  24. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/auth/__init__.py +0 -0
  25. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/auth/application/__init__.py +0 -0
  26. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/auth/application/service.py +1 -0
  27. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/auth/domain/__init__.py +0 -0
  28. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/auth/domain/models.py +1 -0
  29. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/__init__.py +0 -0
  30. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/adapters/__init__.py +0 -0
  31. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/adapters/repository.py +3 -0
  32. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/application/__init__.py +0 -0
  33. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/application/service.py +2 -0
  34. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/domain/__init__.py +0 -0
  35. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/domain/models.py +7 -0
  36. python_dependency_linter-0.1.0/tests/fixtures/sample_pyproject.toml +10 -0
  37. python_dependency_linter-0.1.0/tests/test_checker.py +152 -0
  38. python_dependency_linter-0.1.0/tests/test_cli.py +69 -0
  39. python_dependency_linter-0.1.0/tests/test_config.py +43 -0
  40. python_dependency_linter-0.1.0/tests/test_matcher.py +87 -0
  41. python_dependency_linter-0.1.0/tests/test_parser.py +30 -0
  42. python_dependency_linter-0.1.0/tests/test_reporter.py +50 -0
  43. python_dependency_linter-0.1.0/tests/test_resolver.py +28 -0
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ lint:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.13"
16
+ - run: uv pip install --system -e ".[dev]"
17
+ - run: ruff check .
18
+ - run: ruff format --check .
19
+
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: astral-sh/setup-uv@v4
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+ - run: uv pip install --system -e ".[dev]"
32
+ - run: pytest -v
@@ -0,0 +1,59 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ changelog:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+ - name: Generate full changelog
18
+ uses: orhun/git-cliff-action@v4
19
+ with:
20
+ config: cliff.toml
21
+ args: --verbose
22
+ env:
23
+ OUTPUT: CHANGELOG.md
24
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25
+ - name: Commit CHANGELOG.md
26
+ run: |
27
+ git config user.name "github-actions[bot]"
28
+ git config user.email "github-actions[bot]@users.noreply.github.com"
29
+ git add CHANGELOG.md
30
+ git commit -m "📝 docs: Update CHANGELOG for ${{ github.ref_name }}" || true
31
+ git push origin HEAD:main
32
+ - name: Generate release notes
33
+ id: release_notes
34
+ uses: orhun/git-cliff-action@v4
35
+ with:
36
+ config: cliff.toml
37
+ args: --verbose --latest --strip header
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+ - name: Create GitHub Release
41
+ uses: softprops/action-gh-release@v2
42
+ with:
43
+ body: ${{ steps.release_notes.outputs.content }}
44
+ env:
45
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+
47
+ publish:
48
+ needs: changelog
49
+ runs-on: ubuntu-latest
50
+ permissions:
51
+ id-token: write
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ - uses: actions/setup-python@v5
55
+ with:
56
+ python-version: "3.13"
57
+ - run: pip install build
58
+ - run: python -m build
59
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.4.8
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,6 @@
1
+ - id: python-dependency-linter
2
+ name: Python Dependency Linter
3
+ entry: pdl check
4
+ language: python
5
+ types: [python]
6
+ pass_filenames: false
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 heumsi
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.
@@ -0,0 +1,200 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-dependency-linter
3
+ Version: 0.1.0
4
+ Summary: A dependency linter for Python projects
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: click>=8.0
18
+ Requires-Dist: pyyaml>=6.0
19
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
20
+ Provides-Extra: dev
21
+ Requires-Dist: pre-commit>=3.0; extra == 'dev'
22
+ Requires-Dist: pytest>=7.0; extra == 'dev'
23
+ Requires-Dist: ruff>=0.4; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # python-dependency-linter
27
+
28
+ A dependency linter for Python projects. Define rules for which modules can depend on what, and catch violations.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install python-dependency-linter
34
+ ```
35
+
36
+ Or with uv:
37
+
38
+ ```bash
39
+ uv add python-dependency-linter
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ Create `.python-dependency-linter.yaml` in your project root:
45
+
46
+ ```yaml
47
+ rules:
48
+ - name: domain-isolation
49
+ modules: contexts.*.domain
50
+ allow:
51
+ standard_library: [dataclasses, typing]
52
+ third_party: [pydantic]
53
+ local: [contexts.*.domain]
54
+
55
+ - name: application-dependency
56
+ modules: contexts.*.application
57
+ allow:
58
+ standard_library: ["*"]
59
+ third_party: [pydantic]
60
+ local:
61
+ - contexts.*.application
62
+ - contexts.*.domain
63
+ ```
64
+
65
+ Run:
66
+
67
+ ```bash
68
+ pdl check
69
+ ```
70
+
71
+ Output:
72
+
73
+ ```
74
+ contexts/boards/domain/models.py:6
75
+ [domain-isolation] contexts.boards.domain.models → contexts.boards.application.service (local)
76
+
77
+ contexts/boards/domain/models.py:9
78
+ [domain-isolation] contexts.boards.domain.models → sqlalchemy (third_party)
79
+
80
+ Found 2 violation(s).
81
+ ```
82
+
83
+ ## Configuration
84
+
85
+ ### Rule Structure
86
+
87
+ Each rule has:
88
+
89
+ - `name` — Rule identifier, shown in violation output
90
+ - `modules` — Module pattern to apply the rule to (supports `*` wildcard)
91
+ - `allow` — Whitelist: only listed dependencies are allowed
92
+ - `deny` — Blacklist: listed dependencies are denied
93
+
94
+ ```yaml
95
+ rules:
96
+ - name: rule-name
97
+ modules: my_package.*.domain
98
+ allow:
99
+ standard_library: [dataclasses]
100
+ third_party: [pydantic]
101
+ local: [my_package.*.domain]
102
+ deny:
103
+ third_party: [boto3]
104
+ ```
105
+
106
+ ### Import Categories
107
+
108
+ Dependencies are classified into three categories (per PEP 8):
109
+
110
+ - `standard_library` — Python built-in modules (`os`, `sys`, `typing`, ...)
111
+ - `third_party` — Installed packages (`pydantic`, `sqlalchemy`, ...)
112
+ - `local` — Modules in your project
113
+
114
+ ### Behavior
115
+
116
+ - **No rule** — Everything is allowed
117
+ - **`allow` only** — Whitelist mode. Only listed dependencies are allowed
118
+ - **`deny` only** — Blacklist mode. Listed dependencies are denied, rest allowed
119
+ - **`allow` + `deny`** — Allow first, then deny removes exceptions
120
+ - If `allow` exists but a category is omitted, that category allows all
121
+
122
+ Use `"*"` to allow all within a category:
123
+
124
+ ```yaml
125
+ allow:
126
+ standard_library: ["*"] # allow all standard library imports
127
+ ```
128
+
129
+ ### Wildcard
130
+
131
+ `*` matches a single level in dotted module paths:
132
+
133
+ ```yaml
134
+ modules: contexts.*.domain # matches contexts.boards.domain, contexts.auth.domain, ...
135
+ ```
136
+
137
+ ### Rule Merging
138
+
139
+ When multiple rules match a module, they are merged. Specific rules override wildcard rules per field:
140
+
141
+ ```yaml
142
+ rules:
143
+ - name: base
144
+ modules: contexts.*.domain
145
+ allow:
146
+ third_party: [pydantic]
147
+
148
+ - name: boards-extra
149
+ modules: contexts.boards.domain
150
+ allow:
151
+ third_party: [attrs] # merged: [pydantic, attrs]
152
+ ```
153
+
154
+ ### pyproject.toml
155
+
156
+ You can also configure in `pyproject.toml`:
157
+
158
+ ```toml
159
+ [[tool.python-dependency-linter.rules]]
160
+ name = "domain-isolation"
161
+ modules = "contexts.*.domain"
162
+
163
+ [tool.python-dependency-linter.rules.allow]
164
+ standard_library = ["dataclasses", "typing"]
165
+ third_party = ["pydantic"]
166
+ local = ["contexts.*.domain"]
167
+ ```
168
+
169
+ ## CLI
170
+
171
+ ```bash
172
+ # Check with default config (.python-dependency-linter.yaml)
173
+ pdl check
174
+
175
+ # Specify config file
176
+ pdl check --config path/to/config.yaml
177
+
178
+ # Specify project root
179
+ pdl check --project-root path/to/project
180
+ ```
181
+
182
+ Exit codes:
183
+
184
+ - `0` — No violations
185
+ - `1` — Violations found
186
+
187
+ ## Pre-commit
188
+
189
+ Add to `.pre-commit-config.yaml`:
190
+
191
+ ```yaml
192
+ - repo: https://github.com/heumsi/python-dependency-linter
193
+ rev: v0.1.0
194
+ hooks:
195
+ - id: python-dependency-linter
196
+ ```
197
+
198
+ ## License
199
+
200
+ MIT
@@ -0,0 +1,175 @@
1
+ # python-dependency-linter
2
+
3
+ A dependency linter for Python projects. Define rules for which modules can depend on what, and catch violations.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install python-dependency-linter
9
+ ```
10
+
11
+ Or with uv:
12
+
13
+ ```bash
14
+ uv add python-dependency-linter
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ Create `.python-dependency-linter.yaml` in your project root:
20
+
21
+ ```yaml
22
+ rules:
23
+ - name: domain-isolation
24
+ modules: contexts.*.domain
25
+ allow:
26
+ standard_library: [dataclasses, typing]
27
+ third_party: [pydantic]
28
+ local: [contexts.*.domain]
29
+
30
+ - name: application-dependency
31
+ modules: contexts.*.application
32
+ allow:
33
+ standard_library: ["*"]
34
+ third_party: [pydantic]
35
+ local:
36
+ - contexts.*.application
37
+ - contexts.*.domain
38
+ ```
39
+
40
+ Run:
41
+
42
+ ```bash
43
+ pdl check
44
+ ```
45
+
46
+ Output:
47
+
48
+ ```
49
+ contexts/boards/domain/models.py:6
50
+ [domain-isolation] contexts.boards.domain.models → contexts.boards.application.service (local)
51
+
52
+ contexts/boards/domain/models.py:9
53
+ [domain-isolation] contexts.boards.domain.models → sqlalchemy (third_party)
54
+
55
+ Found 2 violation(s).
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ ### Rule Structure
61
+
62
+ Each rule has:
63
+
64
+ - `name` — Rule identifier, shown in violation output
65
+ - `modules` — Module pattern to apply the rule to (supports `*` wildcard)
66
+ - `allow` — Whitelist: only listed dependencies are allowed
67
+ - `deny` — Blacklist: listed dependencies are denied
68
+
69
+ ```yaml
70
+ rules:
71
+ - name: rule-name
72
+ modules: my_package.*.domain
73
+ allow:
74
+ standard_library: [dataclasses]
75
+ third_party: [pydantic]
76
+ local: [my_package.*.domain]
77
+ deny:
78
+ third_party: [boto3]
79
+ ```
80
+
81
+ ### Import Categories
82
+
83
+ Dependencies are classified into three categories (per PEP 8):
84
+
85
+ - `standard_library` — Python built-in modules (`os`, `sys`, `typing`, ...)
86
+ - `third_party` — Installed packages (`pydantic`, `sqlalchemy`, ...)
87
+ - `local` — Modules in your project
88
+
89
+ ### Behavior
90
+
91
+ - **No rule** — Everything is allowed
92
+ - **`allow` only** — Whitelist mode. Only listed dependencies are allowed
93
+ - **`deny` only** — Blacklist mode. Listed dependencies are denied, rest allowed
94
+ - **`allow` + `deny`** — Allow first, then deny removes exceptions
95
+ - If `allow` exists but a category is omitted, that category allows all
96
+
97
+ Use `"*"` to allow all within a category:
98
+
99
+ ```yaml
100
+ allow:
101
+ standard_library: ["*"] # allow all standard library imports
102
+ ```
103
+
104
+ ### Wildcard
105
+
106
+ `*` matches a single level in dotted module paths:
107
+
108
+ ```yaml
109
+ modules: contexts.*.domain # matches contexts.boards.domain, contexts.auth.domain, ...
110
+ ```
111
+
112
+ ### Rule Merging
113
+
114
+ When multiple rules match a module, they are merged. Specific rules override wildcard rules per field:
115
+
116
+ ```yaml
117
+ rules:
118
+ - name: base
119
+ modules: contexts.*.domain
120
+ allow:
121
+ third_party: [pydantic]
122
+
123
+ - name: boards-extra
124
+ modules: contexts.boards.domain
125
+ allow:
126
+ third_party: [attrs] # merged: [pydantic, attrs]
127
+ ```
128
+
129
+ ### pyproject.toml
130
+
131
+ You can also configure in `pyproject.toml`:
132
+
133
+ ```toml
134
+ [[tool.python-dependency-linter.rules]]
135
+ name = "domain-isolation"
136
+ modules = "contexts.*.domain"
137
+
138
+ [tool.python-dependency-linter.rules.allow]
139
+ standard_library = ["dataclasses", "typing"]
140
+ third_party = ["pydantic"]
141
+ local = ["contexts.*.domain"]
142
+ ```
143
+
144
+ ## CLI
145
+
146
+ ```bash
147
+ # Check with default config (.python-dependency-linter.yaml)
148
+ pdl check
149
+
150
+ # Specify config file
151
+ pdl check --config path/to/config.yaml
152
+
153
+ # Specify project root
154
+ pdl check --project-root path/to/project
155
+ ```
156
+
157
+ Exit codes:
158
+
159
+ - `0` — No violations
160
+ - `1` — Violations found
161
+
162
+ ## Pre-commit
163
+
164
+ Add to `.pre-commit-config.yaml`:
165
+
166
+ ```yaml
167
+ - repo: https://github.com/heumsi/python-dependency-linter
168
+ rev: v0.1.0
169
+ hooks:
170
+ - id: python-dependency-linter
171
+ ```
172
+
173
+ ## License
174
+
175
+ MIT
@@ -0,0 +1,38 @@
1
+ [changelog]
2
+ header = """# Changelog\n
3
+ All notable changes to this project will be documented in this file.\n
4
+ """
5
+ body = """
6
+ {%- macro remote_url() -%}
7
+ https://github.com/heumsi/python-dependency-linter
8
+ {%- endmacro -%}
9
+
10
+ {% if version -%}
11
+ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
12
+ {% else -%}
13
+ ## [Unreleased]
14
+ {% endif -%}
15
+
16
+ {% for group, commits in commits | group_by(attribute="group") %}
17
+ ### {{ group | upper_first }}
18
+ {% for commit in commits %}
19
+ - {{ commit.message | split(pat=":") | last | trim }}\
20
+ {% endfor %}
21
+ {% endfor %}
22
+ """
23
+ trim = true
24
+
25
+ [git]
26
+ conventional_commits = true
27
+ filter_unconventional = true
28
+ commit_parsers = [
29
+ { message = "^.*feat", group = "Features" },
30
+ { message = "^.*fix", group = "Bug Fixes" },
31
+ { message = "^.*refactor", group = "Refactor" },
32
+ { message = "^.*perf", group = "Performance" },
33
+ { message = "^.*doc", group = "Documentation" },
34
+ { message = "^.*test", group = "Testing" },
35
+ { message = "^.*chore", group = "Miscellaneous" },
36
+ { message = "^.*ci", group = "CI/CD" },
37
+ ]
38
+ tag_pattern = "v[0-9].*"