python-dependency-linter 0.1.0__tar.gz → 0.3.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 (60) hide show
  1. python_dependency_linter-0.3.0/.claude/skills/commit/SKILL.md +24 -0
  2. python_dependency_linter-0.3.0/.claude/skills/release/SKILL.md +14 -0
  3. python_dependency_linter-0.3.0/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  4. python_dependency_linter-0.3.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. python_dependency_linter-0.3.0/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
  6. python_dependency_linter-0.3.0/.github/pull_request_template.md +27 -0
  7. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.github/workflows/ci.yaml +8 -4
  8. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.github/workflows/publish.yaml +4 -4
  9. python_dependency_linter-0.3.0/CHANGELOG.md +73 -0
  10. python_dependency_linter-0.3.0/CLAUDE.md +9 -0
  11. python_dependency_linter-0.3.0/CONTRIBUTING.md +84 -0
  12. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/PKG-INFO +159 -2
  13. python_dependency_linter-0.3.0/README.md +332 -0
  14. python_dependency_linter-0.3.0/pyproject.toml +95 -0
  15. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/cli.py +40 -5
  16. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/config.py +12 -2
  17. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/matcher.py +19 -7
  18. python_dependency_linter-0.3.0/python_dependency_linter/parser.py +60 -0
  19. python_dependency_linter-0.3.0/tests/fixtures/sample_project/contexts/boards/__init__.py +1 -0
  20. python_dependency_linter-0.3.0/tests/fixtures/sample_project/contexts/boards/adapters/repository.py +7 -0
  21. python_dependency_linter-0.3.0/tests/test_cli.py +171 -0
  22. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/test_config.py +40 -0
  23. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/test_matcher.py +32 -0
  24. python_dependency_linter-0.3.0/tests/test_parser.py +74 -0
  25. python_dependency_linter-0.3.0/uv.lock +368 -0
  26. python_dependency_linter-0.1.0/README.md +0 -175
  27. python_dependency_linter-0.1.0/cliff.toml +0 -38
  28. python_dependency_linter-0.1.0/docs/superpowers/plans/2026-03-30-python-dependency-linter.md +0 -1343
  29. python_dependency_linter-0.1.0/docs/superpowers/specs/2026-03-30-python-dependency-linter-design.md +0 -235
  30. python_dependency_linter-0.1.0/pyproject.toml +0 -49
  31. python_dependency_linter-0.1.0/python_dependency_linter/parser.py +0 -27
  32. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/adapters/repository.py +0 -3
  33. python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/domain/__init__.py +0 -0
  34. python_dependency_linter-0.1.0/tests/test_cli.py +0 -69
  35. python_dependency_linter-0.1.0/tests/test_parser.py +0 -30
  36. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.github/dependabot.yml +0 -0
  37. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.gitignore +0 -0
  38. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.pre-commit-config.yaml +0 -0
  39. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/.pre-commit-hooks.yaml +0 -0
  40. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/LICENSE +0 -0
  41. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/__init__.py +0 -0
  42. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/checker.py +0 -0
  43. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/reporter.py +0 -0
  44. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/python_dependency_linter/resolver.py +0 -0
  45. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_config.yaml +0 -0
  46. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/__init__.py +0 -0
  47. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/auth/__init__.py +0 -0
  48. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/auth/application/__init__.py +0 -0
  49. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/auth/application/service.py +0 -0
  50. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/auth/domain/__init__.py +0 -0
  51. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/auth/domain/models.py +0 -0
  52. {python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards → python_dependency_linter-0.3.0/tests/fixtures/sample_project/contexts/boards/adapters}/__init__.py +0 -0
  53. {python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/adapters → python_dependency_linter-0.3.0/tests/fixtures/sample_project/contexts/boards/application}/__init__.py +0 -0
  54. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/boards/application/service.py +0 -0
  55. {python_dependency_linter-0.1.0/tests/fixtures/sample_project/contexts/boards/application → python_dependency_linter-0.3.0/tests/fixtures/sample_project/contexts/boards/domain}/__init__.py +0 -0
  56. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_project/contexts/boards/domain/models.py +0 -0
  57. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/fixtures/sample_pyproject.toml +0 -0
  58. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/test_checker.py +0 -0
  59. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/test_reporter.py +0 -0
  60. {python_dependency_linter-0.1.0 → python_dependency_linter-0.3.0}/tests/test_resolver.py +0 -0
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: commit
3
+ description: Create a git commit following the project's commit convention
4
+ ---
5
+
6
+ Create a git commit following the project's commit convention defined in [CONTRIBUTING.md](../../../CONTRIBUTING.md).
7
+
8
+ ## Steps
9
+
10
+ 1. Read `CONTRIBUTING.md` to check the commit convention.
11
+ 2. Run `git status` and `git diff` to review all changes.
12
+ 3. Draft a commit message following the convention.
13
+ 4. Stage only relevant files (do NOT use `git add -A`). Exclude secrets and unrelated files.
14
+ 5. Commit using a HEREDOC for proper formatting:
15
+ ```bash
16
+ git commit -m "$(cat <<'EOF'
17
+ <commit message here>
18
+
19
+ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20
+ EOF
21
+ )"
22
+ ```
23
+ 6. Run `git status` to verify success.
24
+ 7. If pre-commit hook fails, fix the issue and create a **new** commit (never amend).
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: release
3
+ description: Create a new release by calculating the next version from conventional commits and pushing a git tag
4
+ ---
5
+
6
+ Create a new release following the project's release process defined in [CONTRIBUTING.md](../../../CONTRIBUTING.md).
7
+
8
+ The release workflow is defined in [.github/workflows/publish.yaml](../../../.github/workflows/publish.yaml).
9
+
10
+ ## Steps
11
+
12
+ 1. Read `CONTRIBUTING.md` to check the release process.
13
+ 2. Follow the steps described in the Release section.
14
+ 3. Ask the user to confirm the version before creating and pushing the tag.
@@ -0,0 +1,62 @@
1
+ name: Bug Report
2
+ description: Report a bug or unexpected behavior
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting a bug! Please fill out the sections below.
9
+
10
+ - type: textarea
11
+ id: description
12
+ attributes:
13
+ label: Description
14
+ description: A clear description of what the bug is.
15
+ validations:
16
+ required: true
17
+
18
+ - type: textarea
19
+ id: reproduce
20
+ attributes:
21
+ label: Steps to reproduce
22
+ description: Steps to reproduce the behavior.
23
+ placeholder: |
24
+ 1. Create a config file with ...
25
+ 2. Run `pdl check`
26
+ 3. See error ...
27
+ validations:
28
+ required: true
29
+
30
+ - type: textarea
31
+ id: expected
32
+ attributes:
33
+ label: Expected behavior
34
+ description: What you expected to happen.
35
+ validations:
36
+ required: true
37
+
38
+ - type: textarea
39
+ id: config
40
+ attributes:
41
+ label: Configuration
42
+ description: Your `.python-dependency-linter.yaml` (remove sensitive info).
43
+ render: yaml
44
+
45
+ - type: textarea
46
+ id: environment
47
+ attributes:
48
+ label: Environment
49
+ description: Please provide the following info.
50
+ value: |
51
+ - OS:
52
+ - Python version:
53
+ - python-dependency-linter version:
54
+ validations:
55
+ required: true
56
+
57
+ - type: textarea
58
+ id: logs
59
+ attributes:
60
+ label: Error output / logs
61
+ description: Paste any relevant CLI output.
62
+ render: shell
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question / Discussion
4
+ url: https://github.com/heumsi/python-layer-dependency-linter/discussions
5
+ about: Ask questions or start a discussion here.
@@ -0,0 +1,37 @@
1
+ name: Feature Request
2
+ description: Suggest a new feature or improvement
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for suggesting a feature! Please describe your idea below.
9
+
10
+ - type: textarea
11
+ id: problem
12
+ attributes:
13
+ label: Problem
14
+ description: What problem does this feature solve? What's missing?
15
+ placeholder: "e.g., I want to allow wildcard patterns in third_party rules, but currently ..."
16
+ validations:
17
+ required: true
18
+
19
+ - type: textarea
20
+ id: solution
21
+ attributes:
22
+ label: Proposed solution
23
+ description: How do you think this should work?
24
+ validations:
25
+ required: true
26
+
27
+ - type: textarea
28
+ id: alternatives
29
+ attributes:
30
+ label: Alternatives considered
31
+ description: Any other approaches you've thought about.
32
+
33
+ - type: textarea
34
+ id: context
35
+ attributes:
36
+ label: Additional context
37
+ description: Any other context, examples, or config snippets.
@@ -0,0 +1,27 @@
1
+ <!--
2
+ ⚠️ PR Title Convention
3
+ PRs are squash merged, so the PR title becomes the final commit message.
4
+ Please use the following format:
5
+
6
+ <gitmoji> <type>: <Description>
7
+
8
+ Examples:
9
+ ✨ feat: Add support for relative imports
10
+ 🐛 fix: Use exit code 2 for config file not found
11
+
12
+ See CONTRIBUTING.md for the full list of types.
13
+ -->
14
+
15
+ ## What does this PR do?
16
+
17
+ <!-- A brief description of the change. -->
18
+
19
+ ## Related issue
20
+
21
+ <!-- Link to the issue this PR addresses, e.g., Closes #123 -->
22
+
23
+ ## Checklist
24
+
25
+ - [ ] Tests added / updated
26
+ - [ ] `pdl check` runs without errors on the example config
27
+ - [ ] Documentation updated (if applicable)
@@ -3,14 +3,18 @@ name: CI
3
3
  on:
4
4
  pull_request:
5
5
  branches: [main]
6
+ paths:
7
+ - "python_dependency_linter/**"
8
+ - "tests/**"
9
+ - "pyproject.toml"
6
10
 
7
11
  jobs:
8
12
  lint:
9
13
  runs-on: ubuntu-latest
10
14
  steps:
11
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
12
16
  - uses: astral-sh/setup-uv@v4
13
- - uses: actions/setup-python@v5
17
+ - uses: actions/setup-python@v6
14
18
  with:
15
19
  python-version: "3.13"
16
20
  - run: uv pip install --system -e ".[dev]"
@@ -23,9 +27,9 @@ jobs:
23
27
  matrix:
24
28
  python-version: ["3.10", "3.11", "3.12", "3.13"]
25
29
  steps:
26
- - uses: actions/checkout@v4
30
+ - uses: actions/checkout@v6
27
31
  - uses: astral-sh/setup-uv@v4
28
- - uses: actions/setup-python@v5
32
+ - uses: actions/setup-python@v6
29
33
  with:
30
34
  python-version: ${{ matrix.python-version }}
31
35
  - run: uv pip install --system -e ".[dev]"
@@ -11,13 +11,13 @@ jobs:
11
11
  permissions:
12
12
  contents: write
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v6
15
15
  with:
16
16
  fetch-depth: 0
17
17
  - name: Generate full changelog
18
18
  uses: orhun/git-cliff-action@v4
19
19
  with:
20
- config: cliff.toml
20
+ config: pyproject.toml
21
21
  args: --verbose
22
22
  env:
23
23
  OUTPUT: CHANGELOG.md
@@ -33,7 +33,7 @@ jobs:
33
33
  id: release_notes
34
34
  uses: orhun/git-cliff-action@v4
35
35
  with:
36
- config: cliff.toml
36
+ config: pyproject.toml
37
37
  args: --verbose --latest --strip header
38
38
  env:
39
39
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -50,7 +50,7 @@ jobs:
50
50
  permissions:
51
51
  id-token: write
52
52
  steps:
53
- - uses: actions/checkout@v4
53
+ - uses: actions/checkout@v6
54
54
  - uses: actions/setup-python@v5
55
55
  with:
56
56
  python-version: "3.13"
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.2.0] - 2026-03-30
6
+
7
+ ### Documentation
8
+
9
+ - Remove design spec and implementation plan docs
10
+ - Add GitHub issue and PR templates
11
+ - Add example for omitted category behavior in allow rules
12
+ - Add architecture examples to README
13
+ - Add pre-commit hook custom options example
14
+ - Expand pyproject.toml examples with multiple rules and deny
15
+ - Add "What It Does" section to README
16
+
17
+ ### Features
18
+
19
+ - Support `**` glob pattern for matching nested submodules (#6)
20
+ - Add undocumented features to README
21
+
22
+ ### Miscellaneous
23
+
24
+ - Use hatch-vcs for dynamic versioning from git tags
25
+ - Add gitmoji preprocessor to git-cliff config
26
+ - Move git-cliff config from cliff.toml to pyproject.toml
27
+ - Skip CHANGELOG update commits in git-cliff output
28
+
29
+ ### Testing
30
+
31
+ - Limit CI to source and test file changes
32
+
33
+ ### Build
34
+
35
+ - Bump actions/checkout from 4 to 6 (#3)
36
+ - Bump actions/setup-python from 5 to 6 (#2)
37
+ ## [0.1.0] - 2026-03-30
38
+
39
+ ### Bug Fixes
40
+
41
+ - Add isort config and fix plan typo
42
+ - Add tomli fallback for Python 3.10 compatibility
43
+ - Add import content to fixture files
44
+
45
+ ### CI/CD
46
+
47
+ - Add GitHub Actions for CI, PyPI publish, and Dependabot
48
+ - Add git-cliff changelog generation on release
49
+ - Add GitHub Release creation on tag
50
+
51
+ ### Documentation
52
+
53
+ - Add design spec for python-dependency-linter
54
+ - Add implementation plan
55
+ - Add README
56
+ - Add MIT LICENSE
57
+
58
+ ### Features
59
+
60
+ - Add config loading for YAML and pyproject.toml
61
+ - Add AST-based import parser
62
+ - Add import resolver for classification
63
+ - Add wildcard matcher and rule merging
64
+ - Add dependency checker with allow/deny logic
65
+ - Add violation reporter
66
+ - Add CLI with check command
67
+ - Add pre-commit hook definition
68
+
69
+ ### Miscellaneous
70
+
71
+ - Initialize project scaffolding
72
+ - Add license, readme, and classifiers to pyproject.toml
73
+ - Add .gitignore
@@ -0,0 +1,9 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project Overview
4
+
5
+ Python dependency linter (`pdl`) - A CLI tool that lints layer dependency rules in Python projects.
6
+
7
+ ## Contributing
8
+
9
+ Follow the conventions in [CONTRIBUTING.md](./CONTRIBUTING.md).
@@ -0,0 +1,84 @@
1
+ # Contributing
2
+
3
+ ## Commit Convention
4
+
5
+ Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/) with [gitmoji](https://gitmoji.dev/) prefix.
6
+
7
+ ### Format
8
+
9
+ ```
10
+ <gitmoji> <type>: <description>
11
+ ```
12
+
13
+ - The first letter after the colon must be **capitalized**.
14
+ - The description must be in **English**.
15
+
16
+ ### Types
17
+
18
+ | Gitmoji | Type | Description |
19
+ |---------|------------|--------------------------|
20
+ | ✨ | `feat` | New feature |
21
+ | 🐛 | `fix` | Bug fix |
22
+ | ♻️ | `refactor` | Code refactoring |
23
+ | 📝 | `docs` | Documentation |
24
+ | ✅ | `test` | Adding or updating tests |
25
+ | 🔧 | `chore` | Maintenance tasks |
26
+ | 👷 | `ci` | CI/CD changes |
27
+ | ⚡ | `perf` | Performance improvement |
28
+
29
+ ### Examples
30
+
31
+ ```
32
+ ✨ feat: Add support for relative imports
33
+ 🐛 fix: Use exit code 2 for config file not found
34
+ ♻️ refactor: Simplify module resolver logic
35
+ ```
36
+
37
+ ## Pull Request Convention
38
+
39
+ - PRs are always **squash merged**, so the PR title becomes the final commit message.
40
+ - PR titles must follow the same format as commit messages (`<gitmoji> <type>: <description>`).
41
+ - PR descriptions must be written in **English**.
42
+
43
+ ## Pre-commit Hooks
44
+
45
+ This project uses [pre-commit](https://pre-commit.com/) with [ruff](https://docs.astral.sh/ruff/) for linting and formatting.
46
+
47
+ ```bash
48
+ # Install pre-commit hooks
49
+ pre-commit install
50
+
51
+ # Run manually
52
+ pre-commit run --all-files
53
+ ```
54
+
55
+ All commits must pass the pre-commit hooks before being accepted.
56
+
57
+ ## Release
58
+
59
+ Releases are automated via GitHub Actions. You only need to create and push a version tag.
60
+
61
+ ### Steps
62
+
63
+ 1. Calculate the next version based on conventional commits:
64
+ ```bash
65
+ uvx git-cliff --bumped-version
66
+ ```
67
+ 2. Review the commits since the last tag:
68
+ ```bash
69
+ git log $(git describe --tags --abbrev=0)..HEAD --oneline
70
+ ```
71
+ 3. Push the latest commits to `main`:
72
+ ```bash
73
+ git push origin main
74
+ ```
75
+ 4. Create and push the tag:
76
+ ```bash
77
+ git tag <version>
78
+ git push origin <version>
79
+ ```
80
+
81
+ The GitHub Actions workflow will then automatically:
82
+ - Generate `CHANGELOG.md` and commit it to `main`
83
+ - Create a GitHub Release with release notes
84
+ - Publish the package to PyPI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-dependency-linter
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: A dependency linter for Python projects
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -27,6 +27,14 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  A dependency linter for Python projects. Define rules for which modules can depend on what, and catch violations.
29
29
 
30
+ ## What It Does
31
+
32
+ - Define dependency rules between modules using a simple YAML or TOML config
33
+ - Detect imports that violate your rules with a single CLI command
34
+ - Integrate into CI or pre-commit to keep your architecture consistent
35
+
36
+ For Python developers who care about module boundaries and dependency direction — whether you're applying Layered, Hexagonal, Clean Architecture, or your own conventions.
37
+
30
38
  ## Installation
31
39
 
32
40
  ```bash
@@ -80,8 +88,95 @@ contexts/boards/domain/models.py:9
80
88
  Found 2 violation(s).
81
89
  ```
82
90
 
91
+ ## Examples
92
+
93
+ ### Layered Architecture
94
+
95
+ Enforce dependency direction: `presentation → application → domain`, where `domain` has no outward dependencies.
96
+
97
+ ```yaml
98
+ rules:
99
+ - name: domain-isolation
100
+ modules: my_app.domain
101
+ allow:
102
+ standard_library: ["*"]
103
+ third_party: []
104
+ local: [my_app.domain]
105
+
106
+ - name: application-layer
107
+ modules: my_app.application
108
+ allow:
109
+ standard_library: ["*"]
110
+ third_party: [pydantic]
111
+ local:
112
+ - my_app.application
113
+ - my_app.domain
114
+
115
+ - name: presentation-layer
116
+ modules: my_app.presentation
117
+ allow:
118
+ standard_library: ["*"]
119
+ third_party: [fastapi, pydantic]
120
+ local:
121
+ - my_app.presentation
122
+ - my_app.application
123
+ - my_app.domain
124
+ ```
125
+
126
+ ### Hexagonal Architecture
127
+
128
+ Isolate domain from infrastructure. Ports (interfaces) live in domain, adapters depend on domain but not vice versa.
129
+
130
+ ```yaml
131
+ rules:
132
+ - name: domain-no-infra
133
+ modules: contexts.*.domain
134
+ allow:
135
+ standard_library: [dataclasses, typing, abc]
136
+ third_party: []
137
+ local: [contexts.*.domain]
138
+
139
+ - name: adapters-depend-on-domain
140
+ modules: contexts.*.adapters
141
+ allow:
142
+ standard_library: ["*"]
143
+ third_party: ["*"]
144
+ local:
145
+ - contexts.*.adapters
146
+ - contexts.*.domain
147
+ ```
148
+
83
149
  ## Configuration
84
150
 
151
+ ### Include / Exclude
152
+
153
+ Control which files are scanned using `include` and `exclude`:
154
+
155
+ ```yaml
156
+ include:
157
+ - src
158
+ exclude:
159
+ - src/generated/**
160
+
161
+ rules:
162
+ - name: ...
163
+ ```
164
+
165
+ - **No `include` or `exclude`** — All `.py` files under the project root are scanned
166
+ - **`include` only** — Only files matching the given paths are scanned
167
+ - **`exclude` only** — All files except those matching the given paths are scanned
168
+ - **Both** — `include` is applied first, then `exclude` filters within that result
169
+
170
+ Bare directory names (e.g., `src`) and trailing-slash forms (e.g., `src/`) are treated the same as `src/**`.
171
+
172
+ In `pyproject.toml`:
173
+
174
+ ```toml
175
+ [tool.python-dependency-linter]
176
+ include = ["src"]
177
+ exclude = ["src/generated/**"]
178
+ ```
179
+
85
180
  ### Rule Structure
86
181
 
87
182
  Each rule has:
@@ -111,13 +206,25 @@ Dependencies are classified into three categories (per PEP 8):
111
206
  - `third_party` — Installed packages (`pydantic`, `sqlalchemy`, ...)
112
207
  - `local` — Modules in your project
113
208
 
209
+ Both absolute imports (`from contexts.boards.domain import models`) and relative imports (`from ..domain import models`) are analyzed. Relative imports are resolved to absolute module names based on the file's location.
210
+
114
211
  ### Behavior
115
212
 
116
213
  - **No rule** — Everything is allowed
117
214
  - **`allow` only** — Whitelist mode. Only listed dependencies are allowed
118
215
  - **`deny` only** — Blacklist mode. Listed dependencies are denied, rest allowed
119
216
  - **`allow` + `deny`** — Allow first, then deny removes exceptions
120
- - If `allow` exists but a category is omitted, that category allows all
217
+ - If `allow` exists but a category is omitted, that category allows all. For example:
218
+
219
+ ```yaml
220
+ rules:
221
+ - name: domain-isolation
222
+ modules: contexts.*.domain
223
+ allow:
224
+ third_party: [pydantic]
225
+ local: [contexts.*.domain]
226
+ # standard_library is omitted → all standard library imports are allowed
227
+ ```
121
228
 
122
229
  Use `"*"` to allow all within a category:
123
230
 
@@ -134,6 +241,23 @@ allow:
134
241
  modules: contexts.*.domain # matches contexts.boards.domain, contexts.auth.domain, ...
135
242
  ```
136
243
 
244
+ `**` matches one or more levels in dotted module paths:
245
+
246
+ ```yaml
247
+ modules: contexts.**.domain # matches contexts.boards.domain, contexts.boards.sub.domain, ...
248
+ ```
249
+
250
+ ### Submodule Matching
251
+
252
+ When a pattern is used in `allow` or `deny`, it also matches submodules of the matched module. For example:
253
+
254
+ ```yaml
255
+ allow:
256
+ local: [contexts.*.domain]
257
+ ```
258
+
259
+ This allows imports of `contexts.boards.domain` as well as its submodules like `contexts.boards.domain.models` or `contexts.boards.domain.entities.metric`.
260
+
137
261
  ### Rule Merging
138
262
 
139
263
  When multiple rules match a module, they are merged. Specific rules override wildcard rules per field:
@@ -164,6 +288,22 @@ modules = "contexts.*.domain"
164
288
  standard_library = ["dataclasses", "typing"]
165
289
  third_party = ["pydantic"]
166
290
  local = ["contexts.*.domain"]
291
+
292
+ [[tool.python-dependency-linter.rules]]
293
+ name = "application-dependency"
294
+ modules = "contexts.*.application"
295
+
296
+ [tool.python-dependency-linter.rules.allow]
297
+ standard_library = ["*"]
298
+ third_party = ["pydantic"]
299
+ local = ["contexts.*.application", "contexts.*.domain"]
300
+
301
+ [[tool.python-dependency-linter.rules]]
302
+ name = "no-boto-in-domain"
303
+ modules = "contexts.*.domain"
304
+
305
+ [tool.python-dependency-linter.rules.deny]
306
+ third_party = ["boto3"]
167
307
  ```
168
308
 
169
309
  ## CLI
@@ -183,6 +323,13 @@ Exit codes:
183
323
 
184
324
  - `0` — No violations
185
325
  - `1` — Violations found
326
+ - `2` — Config file not found
327
+
328
+ If no `--config` is given, the tool looks for `.python-dependency-linter.yaml` in the current directory. If the config file does not exist, the tool prints an error and exits with code `2`:
329
+
330
+ ```
331
+ Error: Config file not found: .python-dependency-linter.yaml
332
+ ```
186
333
 
187
334
  ## Pre-commit
188
335
 
@@ -195,6 +342,16 @@ Add to `.pre-commit-config.yaml`:
195
342
  - id: python-dependency-linter
196
343
  ```
197
344
 
345
+ To pass custom options (e.g., a different config file or project root):
346
+
347
+ ```yaml
348
+ - repo: https://github.com/heumsi/python-dependency-linter
349
+ rev: v0.1.0
350
+ hooks:
351
+ - id: python-dependency-linter
352
+ args: [--config, custom-config.yaml, --project-root, src]
353
+ ```
354
+
198
355
  ## License
199
356
 
200
357
  MIT