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.
- privata-0.1.0/.github/workflows/ci.yml +51 -0
- privata-0.1.0/.github/workflows/docs.yml +54 -0
- privata-0.1.0/.github/workflows/release.yml +24 -0
- privata-0.1.0/.gitignore +12 -0
- privata-0.1.0/.pre-commit-config.yaml +33 -0
- privata-0.1.0/AGENTS.md +23 -0
- privata-0.1.0/LICENSE +21 -0
- privata-0.1.0/PKG-INFO +92 -0
- privata-0.1.0/README.md +76 -0
- privata-0.1.0/docs/assets/logo.svg +9 -0
- privata-0.1.0/docs/contributing.md +60 -0
- privata-0.1.0/docs/getting-started.md +67 -0
- privata-0.1.0/docs/index.md +43 -0
- privata-0.1.0/docs/usage.md +60 -0
- privata-0.1.0/pyproject.toml +81 -0
- privata-0.1.0/src/privata/__init__.py +29 -0
- privata-0.1.0/src/privata/__main__.py +5 -0
- privata-0.1.0/src/privata/_checker.py +702 -0
- privata-0.1.0/src/privata/_version.py +24 -0
- privata-0.1.0/src/privata/cli.py +14 -0
- privata-0.1.0/tests/__init__.py +1 -0
- privata-0.1.0/tests/test_checker.py +339 -0
- privata-0.1.0/uv.lock +642 -0
- 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/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
|
privata-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
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: mypy
|
|
22
|
+
name: mypy (type checker)
|
|
23
|
+
entry: uv run mypy src tests
|
|
24
|
+
language: system
|
|
25
|
+
types: [python]
|
|
26
|
+
pass_filenames: false
|
|
27
|
+
|
|
28
|
+
- id: ty
|
|
29
|
+
name: ty (type checker)
|
|
30
|
+
entry: uv run ty check
|
|
31
|
+
language: system
|
|
32
|
+
types: [python]
|
|
33
|
+
pass_filenames: false
|
privata-0.1.0/AGENTS.md
ADDED
|
@@ -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.
|
privata-0.1.0/LICENSE
ADDED
|
@@ -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.
|
privata-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: privata
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python module privacy checker for keeping public interfaces intentional.
|
|
5
|
+
Author: Bas Nijholt
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: mypy>=1.14; extra == 'dev'
|
|
11
|
+
Requires-Dist: pre-commit>=4; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest>=8.4; extra == 'dev'
|
|
13
|
+
Requires-Dist: ruff>=0.13; extra == 'dev'
|
|
14
|
+
Requires-Dist: ty; extra == 'dev'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Privata
|
|
18
|
+
|
|
19
|
+
[](LICENSE)
|
|
20
|
+
[](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
|
|
21
|
+
[](https://pypi.org/project/privata/)
|
|
22
|
+
[](https://pypi.org/project/privata/)
|
|
23
|
+
[](https://basnijholt.github.io/privata/)
|
|
24
|
+
|
|
25
|
+
Keep Python module interfaces intentional.
|
|
26
|
+
|
|
27
|
+
Privata scans a `src/` layout Python project and reports public top-level symbols that are only used inside their own module.
|
|
28
|
+
It also reports imports of private modules from outside their owning package subtree.
|
|
29
|
+
Test imports do not count, so tests can still reach internals without forcing those internals to stay public.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv tool install privata
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For local development:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv sync --extra dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
Run Privata from a project root:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
privata .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Example output:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Found 2 public symbols that could be made private:
|
|
55
|
+
|
|
56
|
+
src/example/service.py:12: function `helper`
|
|
57
|
+
src/example/service.py:21: class `InternalState`
|
|
58
|
+
|
|
59
|
+
Found 1 private module imports outside their package subtree:
|
|
60
|
+
|
|
61
|
+
src/example/api.py:3: imports private module `example.worker._runtime`
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If the project is clean:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
No module privacy issues found.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## What Privata Checks
|
|
71
|
+
|
|
72
|
+
- Public top-level functions, classes, variables, and type aliases in `src/`.
|
|
73
|
+
- Whether those symbols are imported by another production module under `src/`.
|
|
74
|
+
- Whether private modules such as `pkg._internal` are imported outside their containing package subtree.
|
|
75
|
+
- Console entry points in `pyproject.toml`.
|
|
76
|
+
- Uvicorn entry points in shell scripts and Dockerfiles.
|
|
77
|
+
- Symbols exported through package `__init__.py` and `__all__`.
|
|
78
|
+
- Tach `[[interfaces]]` entries, when `tach.toml` is present.
|
|
79
|
+
|
|
80
|
+
Privata intentionally ignores imports from `tests/`.
|
|
81
|
+
If only tests import a symbol, Privata treats that symbol as private.
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv run --extra dev pytest
|
|
87
|
+
uv run --extra dev ruff check .
|
|
88
|
+
uv run --extra dev ruff format --check .
|
|
89
|
+
uv run --extra dev mypy src tests
|
|
90
|
+
uv run --extra dev ty check
|
|
91
|
+
uv build
|
|
92
|
+
```
|
privata-0.1.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
[](https://basnijholt.github.io/privata/)
|
|
8
|
+
|
|
9
|
+
Keep Python module interfaces intentional.
|
|
10
|
+
|
|
11
|
+
Privata scans a `src/` layout Python project and reports public top-level symbols that are only used inside their own module.
|
|
12
|
+
It also reports imports of private modules from outside their owning package subtree.
|
|
13
|
+
Test imports do not count, so tests can still reach internals without forcing those internals to stay public.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install privata
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For local development:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv sync --extra dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Run Privata from a project root:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
privata .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Example output:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Found 2 public symbols that could be made private:
|
|
39
|
+
|
|
40
|
+
src/example/service.py:12: function `helper`
|
|
41
|
+
src/example/service.py:21: class `InternalState`
|
|
42
|
+
|
|
43
|
+
Found 1 private module imports outside their package subtree:
|
|
44
|
+
|
|
45
|
+
src/example/api.py:3: imports private module `example.worker._runtime`
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If the project is clean:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
No module privacy issues found.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What Privata Checks
|
|
55
|
+
|
|
56
|
+
- Public top-level functions, classes, variables, and type aliases in `src/`.
|
|
57
|
+
- Whether those symbols are imported by another production module under `src/`.
|
|
58
|
+
- Whether private modules such as `pkg._internal` are imported outside their containing package subtree.
|
|
59
|
+
- Console entry points in `pyproject.toml`.
|
|
60
|
+
- Uvicorn entry points in shell scripts and Dockerfiles.
|
|
61
|
+
- Symbols exported through package `__init__.py` and `__all__`.
|
|
62
|
+
- Tach `[[interfaces]]` entries, when `tach.toml` is present.
|
|
63
|
+
|
|
64
|
+
Privata intentionally ignores imports from `tests/`.
|
|
65
|
+
If only tests import a symbol, Privata treats that symbol as private.
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv run --extra dev pytest
|
|
71
|
+
uv run --extra dev ruff check .
|
|
72
|
+
uv run --extra dev ruff format --check .
|
|
73
|
+
uv run --extra dev mypy src tests
|
|
74
|
+
uv run --extra dev ty check
|
|
75
|
+
uv build
|
|
76
|
+
```
|
|
@@ -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">A shield containing a private module underscore.</desc>
|
|
4
|
+
<rect width="128" height="128" rx="28" fill="#312e81"/>
|
|
5
|
+
<path d="M64 18l36 14v28c0 24.5-14.2 42.8-36 52-21.8-9.2-36-27.5-36-52V32l36-14z" fill="#eef2ff"/>
|
|
6
|
+
<path d="M64 28l25 9.7v21.1c0 18-9.5 31.8-25 39.7-15.5-7.9-25-21.7-25-39.7V37.7L64 28z" fill="#4f46e5"/>
|
|
7
|
+
<path d="M39 82h50v10H39z" fill="#c7d2fe"/>
|
|
8
|
+
<path d="M51 69h26v8H51z" fill="#ffffff"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
```
|
|
16
|
+
|
|
17
|
+
## Run Tests
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run pytest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Code Quality
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv run ruff check .
|
|
27
|
+
uv run ruff format --check .
|
|
28
|
+
uv run mypy src tests
|
|
29
|
+
uv run ty check
|
|
30
|
+
uv build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The repository also uses pre-commit:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv run pre-commit run --all-files
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Build Docs
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv run zensical build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The generated site is written to `site/`.
|
|
46
|
+
|
|
47
|
+
## Project Structure
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
src/privata/
|
|
51
|
+
├── __init__.py Public package API
|
|
52
|
+
├── __main__.py python -m privata entry point
|
|
53
|
+
├── _checker.py AST-based privacy analysis
|
|
54
|
+
└── cli.py Console script wrapper
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Release
|
|
58
|
+
|
|
59
|
+
Releases are published from GitHub Releases through trusted publishing.
|
|
60
|
+
Create a release tag such as `v0.1.0`; the `release.yml` workflow builds and uploads to PyPI.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/rocket
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Getting Started
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
You need:
|
|
10
|
+
|
|
11
|
+
- Python 3.12+
|
|
12
|
+
- a Python project with source code under `src/`
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
=== "uv tool"
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv tool install privata
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
=== "pipx"
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pipx install privata
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
=== "pip"
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install privata
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
=== "From source"
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/basnijholt/privata.git
|
|
38
|
+
cd privata
|
|
39
|
+
uv sync --extra dev
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Run
|
|
43
|
+
|
|
44
|
+
From a project root:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
privata .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Privata exits with status `0` when no privacy issues are found.
|
|
51
|
+
It exits with status `1` when it finds public symbols that can be made private or private module imports that cross package boundaries.
|
|
52
|
+
|
|
53
|
+
## Expected Layout
|
|
54
|
+
|
|
55
|
+
Privata expects a `src/` directory:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
project/
|
|
59
|
+
├── pyproject.toml
|
|
60
|
+
└── src/
|
|
61
|
+
└── package/
|
|
62
|
+
├── __init__.py
|
|
63
|
+
└── module.py
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Tests can live anywhere.
|
|
67
|
+
Imports from tests do not count when deciding whether a symbol should stay public.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/shield-check
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Privata
|
|
6
|
+
|
|
7
|
+
**Keep Python module interfaces intentional**
|
|
8
|
+
|
|
9
|
+
<div style="text-align: center; margin: 1.5rem 0;">
|
|
10
|
+
<img src="assets/logo.svg" alt="Privata logo" width="140" />
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
Privata scans Python projects that use a `src/` layout and reports public symbols that are only used inside their own module.
|
|
14
|
+
It also reports private module imports that cross package boundaries.
|
|
15
|
+
|
|
16
|
+
[PyPI package](https://pypi.org/project/privata/) · [GitHub repository](https://github.com/basnijholt/privata)
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv tool install privata
|
|
22
|
+
privata .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Continue with [Getting Started](getting-started.md), or see the [usage guide](usage.md) for the full checker behavior.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Finds public module-level functions, classes, variables, and type aliases that can be made private.
|
|
30
|
+
- Ignores test imports when deciding whether a symbol is public.
|
|
31
|
+
- Detects private modules imported from outside their owning package subtree.
|
|
32
|
+
- Honors package `__init__.py` re-exports and literal `__all__` declarations.
|
|
33
|
+
- Honors `pyproject.toml` console entry points, Uvicorn shell entry points, and Tach interfaces.
|
|
34
|
+
- Uses only the Python standard library at runtime.
|
|
35
|
+
|
|
36
|
+
## Example
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Found 2 public symbols that could be made private:
|
|
40
|
+
|
|
41
|
+
src/example/service.py:12: function `helper`
|
|
42
|
+
src/example/service.py:21: class `InternalState`
|
|
43
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/terminal
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Usage
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
privata <project-root>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The command scans Python files under `<project-root>/src`.
|
|
14
|
+
|
|
15
|
+
## Public Symbols
|
|
16
|
+
|
|
17
|
+
Privata reports top-level public symbols that are not imported from another production module:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
def helper() -> int:
|
|
21
|
+
return 1
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If `helper` is only used inside its own module, Privata reports it as a candidate for `_helper`.
|
|
25
|
+
|
|
26
|
+
## Private Module Imports
|
|
27
|
+
|
|
28
|
+
Private modules are modules whose dotted path contains a private segment:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
package._internal
|
|
32
|
+
package.feature._runtime
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Those modules can be imported from inside their owning package subtree.
|
|
36
|
+
Imports from outside that subtree are reported.
|
|
37
|
+
|
|
38
|
+
## What Counts As Public Use
|
|
39
|
+
|
|
40
|
+
The following keep a symbol public:
|
|
41
|
+
|
|
42
|
+
- another module under `src/` imports the symbol
|
|
43
|
+
- a package `__init__.py` re-exports the symbol
|
|
44
|
+
- a literal `__all__` includes the symbol
|
|
45
|
+
- `pyproject.toml` lists the symbol as a console or GUI script entry point
|
|
46
|
+
- a shell script or Dockerfile launches the symbol as a Uvicorn app
|
|
47
|
+
- `tach.toml` exposes the symbol through a `[[interfaces]]` entry
|
|
48
|
+
|
|
49
|
+
Imports from tests do not count.
|
|
50
|
+
|
|
51
|
+
## Framework Exceptions
|
|
52
|
+
|
|
53
|
+
Privata skips common framework-owned names:
|
|
54
|
+
|
|
55
|
+
- FastAPI route handlers and related request/response models
|
|
56
|
+
- Typer command callbacks
|
|
57
|
+
- framework app/router objects created with `FastAPI`, `APIRouter`, or `Typer`
|
|
58
|
+
- module-level `logger`
|
|
59
|
+
|
|
60
|
+
These names are often public by framework convention even when they are not imported from another production module.
|