Metadata-Version: 2.4
Name: strictaccess
Version: 1.0.0
Summary: Strict access control for Python classes (private/protected/public).
Author-email: Jhoel Peralta <jhoelperaltap@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/jhoelperaltap/strictaccess
Project-URL: Issues, https://github.com/jhoelperaltap/strictaccess/issues
Project-URL: Changelog, https://github.com/jhoelperaltap/strictaccess/blob/main/CHANGELOG.md
Keywords: access control,encapsulation,private,protected,oop
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6; extra == "docs"
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
Dynamic: license-file

# strictaccess

[![CI](https://github.com/jhoelperaltap/strictaccess/actions/workflows/ci.yml/badge.svg)](https://github.com/jhoelperaltap/strictaccess/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/strictaccess.svg)](https://pypi.org/project/strictaccess/)
[![Python versions](https://img.shields.io/pypi/pyversions/strictaccess.svg)](https://pypi.org/project/strictaccess/)
[![Coverage](https://img.shields.io/codecov/c/github/jhoelperaltap/strictaccess)](https://codecov.io/gh/jhoelperaltap/strictaccess)
[![License: MIT](https://img.shields.io/pypi/l/strictaccess.svg)](https://github.com/jhoelperaltap/strictaccess/blob/main/LICENSE)
[![Typed: PEP 561](https://img.shields.io/badge/typed-PEP%20561-blue.svg)](https://peps.python.org/pep-0561/)

**strictaccess** enforces Java/C++-style access modifiers on Python classes
through the `@private`, `@protected`, and `@public` decorators. Violations
raise typed exceptions; an optional debug mode downgrades them to logged
warnings.

> This is a **discipline tool**, not a security boundary. See
> [Limitations](#limitations) below.

## Installation

```bash
pip install strictaccess
```

Requires Python 3.11+ (the engine relies on `frame.f_code.co_qualname`,
introduced in CPython 3.11).

## Quick example

```python
from strictaccess import strict_access_control, private, protected, PrivateAccessError

@strict_access_control()
class Account:
    def __init__(self, balance: int) -> None:
        self._balance = balance        # underscore -> protected by convention

    @private
    def _adjust(self, delta: int) -> None:
        self._balance += delta

    @protected
    def _audit(self) -> str:
        return f"balance={self._balance}"

    def deposit(self, amount: int) -> None:
        self._adjust(amount)            # OK: internal call

acct = Account(100)
acct.deposit(50)                        # OK
acct._adjust(1000)                      # raises PrivateAccessError
acct._balance                           # raises ProtectedAccessError
```

## What each decorator does

| Decorator     | Callable from                              | If called from outside     |
|---------------|--------------------------------------------|----------------------------|
| `@public`     | Anywhere (overrides underscore convention) | Allowed                    |
| `@protected`  | Defining class + any subclass              | `ProtectedAccessError`     |
| `@private`    | Defining class only                        | `PrivateAccessError`       |

Plus, the underscore convention is enforced automatically:

| Attribute name pattern    | Treated as            |
|---------------------------|-----------------------|
| `name` (no underscore)    | public                |
| `_name`                   | protected             |
| `__name` (name-mangled)   | private               |
| `__name__` (dunder)       | unrestricted          |

## Debug mode

Replace exceptions with `WARNING` log records via the `strictaccess` logger:

```python
import logging
logging.basicConfig(level=logging.WARNING)

@strict_access_control(debug=True)
class Audit:
    @private
    def _secret(self): return 42

Audit()._secret()  # logs a warning, returns 42
```

Useful for adopting strictaccess on a legacy codebase without stopping
production behaviour while you fix call sites.

## Limitations

strictaccess enforces discipline at attribute-read time. It is **not** a
security boundary. Specifically:

- `object.__getattribute__(obj, name)` bypasses the check entirely.
- Pickling a decorated instance can fail because the wrapper class is created
  dynamically by `type(...)`. Use `copy.deepcopy` instead, or unwrap the
  instance before pickling.

The full list is documented in [`docs/limitations.md`](docs/limitations.md).

## Documentation

Full guide and API reference: <https://jhoelperaltap.github.io/strictaccess>
(generated from `docs/`).

## Contributing

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for development setup, test
commands, and the contribution flow. Open a discussion before large changes.

## License

MIT — see [`LICENSE`](LICENSE).
