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.
@@ -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
@@ -0,0 +1,12 @@
1
+ .DS_Store
2
+ .venv/
3
+ .ruff_cache/
4
+ .pytest_cache/
5
+ __pycache__/
6
+ *.py[cod]
7
+ dist/
8
+ build/
9
+ site/
10
+ *.egg-info/
11
+ .privata/
12
+ src/privata/_version.py
@@ -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
@@ -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](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
20
+ [![CI](https://github.com/basnijholt/privata/actions/workflows/ci.yml/badge.svg)](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
21
+ [![PyPI](https://img.shields.io/pypi/v/privata.svg)](https://pypi.org/project/privata/)
22
+ [![Python Versions](https://img.shields.io/pypi/pyversions/privata.svg)](https://pypi.org/project/privata/)
23
+ [![Docs](https://img.shields.io/badge/docs-basnijholt.github.io%2Fprivata-blue)](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
+ ```
@@ -0,0 +1,76 @@
1
+ # Privata
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+ [![CI](https://github.com/basnijholt/privata/actions/workflows/ci.yml/badge.svg)](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
5
+ [![PyPI](https://img.shields.io/pypi/v/privata.svg)](https://pypi.org/project/privata/)
6
+ [![Python Versions](https://img.shields.io/pypi/pyversions/privata.svg)](https://pypi.org/project/privata/)
7
+ [![Docs](https://img.shields.io/badge/docs-basnijholt.github.io%2Fprivata-blue)](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.