test-privata 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.
- test_privata-0.1.0/.claude/settings.local.json +10 -0
- test_privata-0.1.0/.github/workflows/ci.yml +51 -0
- test_privata-0.1.0/.github/workflows/docs.yml +54 -0
- test_privata-0.1.0/.github/workflows/release.yml +24 -0
- test_privata-0.1.0/.gitignore +13 -0
- test_privata-0.1.0/.pre-commit-config.yaml +40 -0
- test_privata-0.1.0/.pre-commit-hooks.yaml +16 -0
- test_privata-0.1.0/AGENTS.md +23 -0
- test_privata-0.1.0/LICENSE +21 -0
- test_privata-0.1.0/PKG-INFO +175 -0
- test_privata-0.1.0/README.md +142 -0
- test_privata-0.1.0/docs/assets/logo.svg +9 -0
- test_privata-0.1.0/docs/contributing.md +58 -0
- test_privata-0.1.0/docs/getting-started.md +92 -0
- test_privata-0.1.0/docs/index.md +46 -0
- test_privata-0.1.0/docs/usage.md +110 -0
- test_privata-0.1.0/pyproject.toml +114 -0
- test_privata-0.1.0/src/privata/__init__.py +37 -0
- test_privata-0.1.0/src/privata/__main__.py +5 -0
- test_privata-0.1.0/src/privata/_checker.py +162 -0
- test_privata-0.1.0/src/privata/_entrypoints.py +87 -0
- test_privata-0.1.0/src/privata/_exports.py +170 -0
- test_privata-0.1.0/src/privata/_imports.py +265 -0
- test_privata-0.1.0/src/privata/_models.py +76 -0
- test_privata-0.1.0/src/privata/_modules.py +317 -0
- test_privata-0.1.0/src/privata/_source_roots.py +81 -0
- test_privata-0.1.0/src/privata/_version.py +24 -0
- test_privata-0.1.0/src/privata/cli.py +38 -0
- test_privata-0.1.0/tests/__init__.py +1 -0
- test_privata-0.1.0/tests/test_checker.py +1633 -0
- test_privata-0.1.0/uv.lock +742 -0
- test_privata-0.1.0/zensical.toml +101 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
python-version: ["3.12", "3.13"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v7
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
run: uv python install ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --all-extras --dev
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: uv run pytest
|
|
32
|
+
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
lint:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v6
|
|
40
|
+
|
|
41
|
+
- name: Install uv
|
|
42
|
+
uses: astral-sh/setup-uv@v7
|
|
43
|
+
|
|
44
|
+
- name: Set up Python
|
|
45
|
+
run: uv python install 3.12
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: uv sync --all-extras --dev
|
|
49
|
+
|
|
50
|
+
- name: Run pre-commit (via prek)
|
|
51
|
+
uses: j178/prek-action@v1
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
pages: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: "pages"
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout repository
|
|
23
|
+
uses: actions/checkout@v6
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v6
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
|
|
30
|
+
- name: Install uv
|
|
31
|
+
uses: astral-sh/setup-uv@v7
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: uv sync --group docs
|
|
35
|
+
|
|
36
|
+
- name: Build documentation
|
|
37
|
+
run: uv run zensical build
|
|
38
|
+
|
|
39
|
+
- name: Upload artifact
|
|
40
|
+
uses: actions/upload-pages-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
path: ./site
|
|
43
|
+
|
|
44
|
+
deploy:
|
|
45
|
+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
|
46
|
+
environment:
|
|
47
|
+
name: github-pages
|
|
48
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: build
|
|
51
|
+
steps:
|
|
52
|
+
- name: Deploy to GitHub Pages
|
|
53
|
+
id: deployment
|
|
54
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Upload Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment:
|
|
11
|
+
name: pypi
|
|
12
|
+
url: https://pypi.org/p/test-privata
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v7
|
|
21
|
+
- name: Build
|
|
22
|
+
run: uv build
|
|
23
|
+
- name: Publish package distributions to PyPI
|
|
24
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: debug-statements
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.14.9
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff-check
|
|
16
|
+
args: [--fix]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
|
|
19
|
+
- repo: local
|
|
20
|
+
hooks:
|
|
21
|
+
- id: privata
|
|
22
|
+
name: privata (module privacy)
|
|
23
|
+
entry: uv run privata .
|
|
24
|
+
language: system
|
|
25
|
+
pass_filenames: false
|
|
26
|
+
types_or: [python, pyi]
|
|
27
|
+
|
|
28
|
+
- id: mypy
|
|
29
|
+
name: mypy (type checker)
|
|
30
|
+
entry: uv run mypy src tests
|
|
31
|
+
language: system
|
|
32
|
+
types: [python]
|
|
33
|
+
pass_filenames: false
|
|
34
|
+
|
|
35
|
+
- id: ty
|
|
36
|
+
name: ty (type checker)
|
|
37
|
+
entry: uv run ty check
|
|
38
|
+
language: system
|
|
39
|
+
types: [python]
|
|
40
|
+
pass_filenames: false
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
- id: privata
|
|
2
|
+
name: privata
|
|
3
|
+
description: Check that Python module privacy boundaries stay intentional.
|
|
4
|
+
entry: privata .
|
|
5
|
+
language: python
|
|
6
|
+
pass_filenames: false
|
|
7
|
+
types_or: [python, pyi]
|
|
8
|
+
|
|
9
|
+
- id: privata-manual
|
|
10
|
+
name: privata
|
|
11
|
+
description: Check Python module privacy boundaries on demand.
|
|
12
|
+
entry: privata .
|
|
13
|
+
language: python
|
|
14
|
+
pass_filenames: false
|
|
15
|
+
stages: [manual]
|
|
16
|
+
types_or: [python, pyi]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
Privata is a small Python package for AST-based module privacy checks.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
- Use `uv sync --extra dev --group docs` for a full development environment.
|
|
8
|
+
- Run `uv run pytest` for tests.
|
|
9
|
+
- Run `uv run ruff check .`, `uv run ruff format --check .`, `uv run mypy src tests`, and `uv run ty check` before submitting changes.
|
|
10
|
+
- Use `uv build` to verify packaging.
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
- `src/privata/_checker.py` contains the checker implementation.
|
|
15
|
+
- `src/privata/cli.py` exposes the console script.
|
|
16
|
+
- `tests/test_checker.py` contains focused behavior tests.
|
|
17
|
+
- `docs/` contains the Zensical documentation site.
|
|
18
|
+
|
|
19
|
+
## Style
|
|
20
|
+
|
|
21
|
+
- Keep runtime dependencies minimal.
|
|
22
|
+
- Prefer standard-library parsing and typed data structures.
|
|
23
|
+
- Preserve the rule that test imports do not count when deciding whether a symbol should remain public.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bas Nijholt
|
|
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,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: test-privata
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python module privacy checker for keeping public interfaces intentional.
|
|
5
|
+
Project-URL: Repository, https://github.com/ColinKennedy/privata
|
|
6
|
+
Author: Colin Kennedy
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: architecture,code-quality,imports,module-boundaries,python,static-analysis
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy>=1.14; extra == 'dev'
|
|
27
|
+
Requires-Dist: pre-commit>=4; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov>=7.1; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.4; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.13; extra == 'dev'
|
|
31
|
+
Requires-Dist: ty; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Privata
|
|
35
|
+
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/privata/)
|
|
39
|
+
[](https://pypi.org/project/privata/)
|
|
40
|
+
[](http://privata.nijho.lt/)
|
|
41
|
+
|
|
42
|
+
<img src="docs/assets/logo.svg" alt="Privata logo" align="right" width="120" />
|
|
43
|
+
|
|
44
|
+
Find Python code that looks public but is only used privately.
|
|
45
|
+
|
|
46
|
+
Privata is a static checker for keeping module boundaries intentional.
|
|
47
|
+
It scans your production Python modules and reports four kinds of interface drift:
|
|
48
|
+
|
|
49
|
+
- public top-level functions, classes, variables, and type aliases that are only used inside their own module
|
|
50
|
+
- imports of private modules such as `pkg._internal` from outside their owning package subtree
|
|
51
|
+
- imports of private top-level symbols such as `pkg.service._Helper` from another production module
|
|
52
|
+
- literal `__all__` declarations that are stale, incomplete, or exporting names that do not exist
|
|
53
|
+
|
|
54
|
+
It is designed for packages and applications where `helper()` should become `_helper()` once it is no longer part of the production interface.
|
|
55
|
+
Test imports do not count, so tests can still reach internals without forcing those internals to stay public.
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
Given:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
# src/example/service.py
|
|
63
|
+
def helper() -> int:
|
|
64
|
+
return 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run() -> int:
|
|
68
|
+
return helper()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Privata reports:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
Found 1 public symbols that could be made private:
|
|
75
|
+
|
|
76
|
+
src/example/service.py:1: function `helper`
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv tool install privata
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For local development:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv sync --extra dev --group docs
|
|
89
|
+
uv run pre-commit install
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Usage
|
|
93
|
+
|
|
94
|
+
Run Privata from a project root:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
privata .
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Privata uses `tach.toml` `source_roots` when present.
|
|
101
|
+
Otherwise it prefers `src/` when that directory exists, and falls back to scanning the project root while ignoring tests, virtualenvs, build output, docs output, and hidden tooling directories.
|
|
102
|
+
|
|
103
|
+
Use Privata as a pre-commit hook in another repository:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
repos:
|
|
107
|
+
- repo: https://github.com/basnijholt/privata
|
|
108
|
+
rev: v0.1.2
|
|
109
|
+
hooks:
|
|
110
|
+
- id: privata
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For a less strict setup that only runs when requested:
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
repos:
|
|
117
|
+
- repo: https://github.com/basnijholt/privata
|
|
118
|
+
rev: v0.1.2
|
|
119
|
+
hooks:
|
|
120
|
+
- id: privata-manual
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pre-commit run --hook-stage manual privata-manual --all-files
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Full output can include multiple issue types:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
Found 2 public symbols that could be made private:
|
|
131
|
+
|
|
132
|
+
src/example/service.py:12: function `helper`
|
|
133
|
+
src/example/service.py:21: class `InternalState`
|
|
134
|
+
|
|
135
|
+
Found 1 private module imports outside their package subtree:
|
|
136
|
+
|
|
137
|
+
src/example/api.py:3: imports private module `example.worker._runtime`
|
|
138
|
+
|
|
139
|
+
Found 1 private symbol imports from production modules:
|
|
140
|
+
|
|
141
|
+
src/example/api.py:4: imports private symbol `example.worker.runtime._Helper`
|
|
142
|
+
|
|
143
|
+
Found 1 __all__ export issues:
|
|
144
|
+
|
|
145
|
+
src/example/__init__.py:5: public name `Service` missing from __all__
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
If the project is clean:
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
No module privacy issues found.
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## What Privata Checks
|
|
155
|
+
|
|
156
|
+
- Public top-level functions, classes, variables, and type aliases in production source roots.
|
|
157
|
+
- Whether those symbols are imported by another production module under those roots.
|
|
158
|
+
- Whether private modules such as `pkg._internal` are imported outside their containing package subtree.
|
|
159
|
+
- Whether private top-level symbols are imported from another production module.
|
|
160
|
+
- Whether literal `__all__` declarations exactly match public top-level bindings.
|
|
161
|
+
- Console entry points in `pyproject.toml`.
|
|
162
|
+
- Uvicorn entry points in shell scripts and Dockerfiles.
|
|
163
|
+
- Symbols exported through package `__init__.py` and `__all__`.
|
|
164
|
+
- Tach `[[interfaces]]` entries, when `tach.toml` is present.
|
|
165
|
+
|
|
166
|
+
Privata intentionally ignores imports from `tests/`.
|
|
167
|
+
If only tests import a symbol, Privata treats that symbol as private.
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
uv run pytest # enforces 100% coverage
|
|
173
|
+
uv run pre-commit run --all-files
|
|
174
|
+
uv build
|
|
175
|
+
```
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Privata
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
|
|
5
|
+
[](https://pypi.org/project/privata/)
|
|
6
|
+
[](https://pypi.org/project/privata/)
|
|
7
|
+
[](http://privata.nijho.lt/)
|
|
8
|
+
|
|
9
|
+
<img src="docs/assets/logo.svg" alt="Privata logo" align="right" width="120" />
|
|
10
|
+
|
|
11
|
+
Find Python code that looks public but is only used privately.
|
|
12
|
+
|
|
13
|
+
Privata is a static checker for keeping module boundaries intentional.
|
|
14
|
+
It scans your production Python modules and reports four kinds of interface drift:
|
|
15
|
+
|
|
16
|
+
- public top-level functions, classes, variables, and type aliases that are only used inside their own module
|
|
17
|
+
- imports of private modules such as `pkg._internal` from outside their owning package subtree
|
|
18
|
+
- imports of private top-level symbols such as `pkg.service._Helper` from another production module
|
|
19
|
+
- literal `__all__` declarations that are stale, incomplete, or exporting names that do not exist
|
|
20
|
+
|
|
21
|
+
It is designed for packages and applications where `helper()` should become `_helper()` once it is no longer part of the production interface.
|
|
22
|
+
Test imports do not count, so tests can still reach internals without forcing those internals to stay public.
|
|
23
|
+
|
|
24
|
+
## Example
|
|
25
|
+
|
|
26
|
+
Given:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
# src/example/service.py
|
|
30
|
+
def helper() -> int:
|
|
31
|
+
return 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run() -> int:
|
|
35
|
+
return helper()
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Privata reports:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
Found 1 public symbols that could be made private:
|
|
42
|
+
|
|
43
|
+
src/example/service.py:1: function `helper`
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv tool install privata
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For local development:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv sync --extra dev --group docs
|
|
56
|
+
uv run pre-commit install
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Run Privata from a project root:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
privata .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Privata uses `tach.toml` `source_roots` when present.
|
|
68
|
+
Otherwise it prefers `src/` when that directory exists, and falls back to scanning the project root while ignoring tests, virtualenvs, build output, docs output, and hidden tooling directories.
|
|
69
|
+
|
|
70
|
+
Use Privata as a pre-commit hook in another repository:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
repos:
|
|
74
|
+
- repo: https://github.com/basnijholt/privata
|
|
75
|
+
rev: v0.1.2
|
|
76
|
+
hooks:
|
|
77
|
+
- id: privata
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For a less strict setup that only runs when requested:
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
repos:
|
|
84
|
+
- repo: https://github.com/basnijholt/privata
|
|
85
|
+
rev: v0.1.2
|
|
86
|
+
hooks:
|
|
87
|
+
- id: privata-manual
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pre-commit run --hook-stage manual privata-manual --all-files
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Full output can include multiple issue types:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Found 2 public symbols that could be made private:
|
|
98
|
+
|
|
99
|
+
src/example/service.py:12: function `helper`
|
|
100
|
+
src/example/service.py:21: class `InternalState`
|
|
101
|
+
|
|
102
|
+
Found 1 private module imports outside their package subtree:
|
|
103
|
+
|
|
104
|
+
src/example/api.py:3: imports private module `example.worker._runtime`
|
|
105
|
+
|
|
106
|
+
Found 1 private symbol imports from production modules:
|
|
107
|
+
|
|
108
|
+
src/example/api.py:4: imports private symbol `example.worker.runtime._Helper`
|
|
109
|
+
|
|
110
|
+
Found 1 __all__ export issues:
|
|
111
|
+
|
|
112
|
+
src/example/__init__.py:5: public name `Service` missing from __all__
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If the project is clean:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
No module privacy issues found.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## What Privata Checks
|
|
122
|
+
|
|
123
|
+
- Public top-level functions, classes, variables, and type aliases in production source roots.
|
|
124
|
+
- Whether those symbols are imported by another production module under those roots.
|
|
125
|
+
- Whether private modules such as `pkg._internal` are imported outside their containing package subtree.
|
|
126
|
+
- Whether private top-level symbols are imported from another production module.
|
|
127
|
+
- Whether literal `__all__` declarations exactly match public top-level bindings.
|
|
128
|
+
- Console entry points in `pyproject.toml`.
|
|
129
|
+
- Uvicorn entry points in shell scripts and Dockerfiles.
|
|
130
|
+
- Symbols exported through package `__init__.py` and `__all__`.
|
|
131
|
+
- Tach `[[interfaces]]` entries, when `tach.toml` is present.
|
|
132
|
+
|
|
133
|
+
Privata intentionally ignores imports from `tests/`.
|
|
134
|
+
If only tests import a symbol, Privata treats that symbol as private.
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv run pytest # enforces 100% coverage
|
|
140
|
+
uv run pre-commit run --all-files
|
|
141
|
+
uv build
|
|
142
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Privata logo</title>
|
|
3
|
+
<desc id="desc">The private-module mark: bracket, dot, underscore, bracket.</desc>
|
|
4
|
+
<rect width="128" height="128" rx="28" fill="#101828"/>
|
|
5
|
+
<path d="M48 36H32v56h16" fill="none" stroke="#cbd5e1" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
6
|
+
<path d="M80 36h16v56H80" fill="none" stroke="#cbd5e1" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
+
<circle cx="54" cy="72" r="6" fill="#f8fafc"/>
|
|
8
|
+
<rect x="66" y="76" width="18" height="8" rx="4" fill="#22d3ee"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/git-pull-request
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Contributing
|
|
6
|
+
|
|
7
|
+
Contributions are welcome.
|
|
8
|
+
|
|
9
|
+
## Development Setup
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://github.com/basnijholt/privata.git
|
|
13
|
+
cd privata
|
|
14
|
+
uv sync --extra dev --group docs
|
|
15
|
+
uv run pre-commit install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Run Tests
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv run pytest # enforces 100% coverage
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Code Quality
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv run pre-commit run --all-files
|
|
28
|
+
uv build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Install the Git hook once after setup:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv run pre-commit install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Build Docs
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv run zensical build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The generated site is written to `site/`.
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
src/privata/
|
|
49
|
+
├── __init__.py Public package API
|
|
50
|
+
├── __main__.py python -m privata entry point
|
|
51
|
+
├── _checker.py AST-based privacy analysis
|
|
52
|
+
└── cli.py Console script wrapper
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Release
|
|
56
|
+
|
|
57
|
+
Releases are published from GitHub Releases through trusted publishing.
|
|
58
|
+
Create a release tag such as `v0.1.0`; the `release.yml` workflow builds and uploads to PyPI.
|