Metadata-Version: 2.4
Name: privata
Version: 0.2.0
Summary: A Python module privacy checker for keeping public interfaces intentional.
Project-URL: Documentation, http://privata.nijho.lt/
Project-URL: Issues, https://github.com/basnijholt/privata/issues
Project-URL: Repository, https://github.com/basnijholt/privata
Author: Bas Nijholt
License-Expression: MIT
License-File: LICENSE
Keywords: architecture,code-quality,imports,module-boundaries,python,static-analysis
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: mypy>=1.14; extra == 'dev'
Requires-Dist: pre-commit>=4; extra == 'dev'
Requires-Dist: pytest-cov>=7.1; extra == 'dev'
Requires-Dist: pytest>=8.4; extra == 'dev'
Requires-Dist: ruff>=0.13; extra == 'dev'
Requires-Dist: ty; extra == 'dev'
Description-Content-Type: text/markdown

# Privata

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/basnijholt/privata/actions/workflows/ci.yml/badge.svg)](https://github.com/basnijholt/privata/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/privata.svg)](https://pypi.org/project/privata/)
[![Python Versions](https://img.shields.io/pypi/pyversions/privata.svg)](https://pypi.org/project/privata/)
[![Docs](https://img.shields.io/badge/docs-privata.nijho.lt-blue)](http://privata.nijho.lt/)

<img src="docs/assets/logo.svg" alt="Privata logo" align="right" width="120" />

Find Python code that looks public but is only used privately.

Privata is a static checker for keeping module boundaries intentional.
It scans your production Python modules and reports four kinds of interface drift:

- public top-level functions, classes, variables, and type aliases that are only used inside their own module
- imports of private modules such as `pkg._internal` from outside their owning package subtree
- imports of private top-level symbols such as `pkg.service._Helper` from another production module
- literal `__all__` declarations that are stale, incomplete, or exporting names that do not exist

It is designed for packages and applications where `helper()` should become `_helper()` once it is no longer part of the production interface.
Test imports do not count, so tests can still reach internals without forcing those internals to stay public.

## Example

Given:

```python
# src/example/service.py
def helper() -> int:
    return 1


def run() -> int:
    return helper()
```

Privata reports:

```text
Found 1 public symbols that could be made private:

  src/example/service.py:1: function `helper`
```

## Install

```bash
uv tool install privata
```

For local development:

```bash
uv sync --extra dev --group docs
uv run pre-commit install
```

## Usage

Run Privata from a project root:

```bash
privata .
```

Privata uses `tach.toml` `source_roots` when present.
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.

Use Privata as a pre-commit hook in another repository:

```yaml
repos:
  - repo: https://github.com/basnijholt/privata
    rev: v0.1.2
    hooks:
      - id: privata
```

For a less strict setup that only runs when requested:

```yaml
repos:
  - repo: https://github.com/basnijholt/privata
    rev: v0.1.2
    hooks:
      - id: privata-manual
```

```bash
pre-commit run --hook-stage manual privata-manual --all-files
```

Full output can include multiple issue types:

```text
Found 2 public symbols that could be made private:

  src/example/service.py:12: function `helper`
  src/example/service.py:21: class `InternalState`

Found 1 private module imports outside their package subtree:

  src/example/api.py:3: imports private module `example.worker._runtime`

Found 1 private symbol imports from production modules:

  src/example/api.py:4: imports private symbol `example.worker.runtime._Helper`

Found 1 __all__ export issues:

  src/example/__init__.py:5: public name `Service` missing from __all__
```

If the project is clean:

```text
No module privacy issues found.
```

## What Privata Checks

- Public top-level functions, classes, variables, and type aliases in production source roots.
- Whether those symbols are imported by another production module under those roots.
- Whether private modules such as `pkg._internal` are imported outside their containing package subtree.
- Whether private top-level symbols are imported from another production module.
- Whether literal `__all__` declarations exactly match public top-level bindings.
- Console entry points in `pyproject.toml`.
- Uvicorn entry points in shell scripts and Dockerfiles.
- Symbols exported through package `__init__.py` and `__all__`.
- Tach `[[interfaces]]` entries, when `tach.toml` is present.

Privata intentionally ignores imports from `tests/`.
If only tests import a symbol, Privata treats that symbol as private.

## Development

```bash
uv run pytest  # enforces 100% coverage
uv run pre-commit run --all-files
uv build
```
