Runtime final guard for methods/properties across projects
Project description
Access Guard
A super lightweight runtime guard that provides a @final decorator, final_class, and an Access metaclass. It prevents methods/properties (optionally magic methods) marked as final from being overridden in subclasses, and blocks dynamic overriding after a class is created.
Features
- Supports regular methods,
@staticmethod,@classmethod, and@property(including getter / setter / deleter). - Optionally lock magic methods:
@final_class(include_magic=True). - No third-party dependencies; standard library only, zero runtime external deps.
- Validates at class creation time; violations raise
RuntimeError(fail fast). - Prevents monkey patching: reassigning a final member after class creation is blocked.
- PEP 561: ships
py.typed, friendly to type checkers. - O(member count) lightweight scan; no added overhead on runtime calls.
Installation
Supports Python 3.10+.
Using uv:
uv add access-guard
Or using pip:
pip install access-guard
Quick Examples
final
from access_guard import Access, final
class Base(metaclass=Access):
@final
def stable(self):
return "v1"
@property
@final
def value(self):
return 42
class Child(Base):
pass
Child().stable() # OK
# Any of the following will raise RuntimeError:
# class Bad(Base):
# def stable(self): # overriding a final method
# return "v2"
# Bad.stable = lambda self: "hack" # dynamic override after class creation
final_class
Mark all currently defined non-magic members in the class as final in one go:
from access_guard import Access, final_class
@final_class
class Service(metaclass=Access):
def create(self):
return "created"
def delete(self):
return "deleted"
class Child(Service):
pass # OK
# The following will fail:
# class Bad(Service):
# def create(self): # RuntimeError
# return "x"
Note: Members added dynamically after the class is created will not be final automatically; it’s recommended to use @final on those members individually when you define them.
Parameters
from access_guard import Access, final_class
# Exclude some methods from being locked
@final_class(exclude={"debug", "open"})
class Service(metaclass=Access):
def create(self): ... # final
def debug(self): ... # not final
def open(self): ... # not final
# Lock including magic methods
@final_class(include_magic=True)
class Model(metaclass=Access):
def __repr__(self):
return "Model()" # final
def run(self):
return 1 # final
# Use both together
@final_class(exclude=["__repr__"], include_magic=True)
class Partial(metaclass=Access):
def __repr__(self): # not final
return "P()"
def calc(self): # final
return 42
Parameter details:
exclude: iterable[str]. Names that should NOT be marked final; can be list / set / tuple.include_magic: when True, magic methods like__repr__are also marked (builtin low-level__dict__and__weakref__are always ignored).
Behavior Details
@finalmarks a special attribute on the actual function/property/descriptor object; for properties it also marks their accessors.final_classcollects all members in the class at decoration time and marks them;excludeandinclude_magiccontrol the scope.Accessmetaclass in__new__:- Collects all final names from base classes (including magic);
- Checks whether the subclass overrides them;
- Merges and records them to the subclass.
- Reassigning a final name after class creation is rejected in
__setattr__. - If a base class uses
final_class(include_magic=True), its magic methods are locked and propagated to all subclasses.
Limitations / Notes
- Protection only applies at class definition and dynamic assignment levels; instance attributes are not handled.
- Alias references are not tracked (you can still copy a function object and assign it under another name).
- With multiple inheritance: if different base classes define different member names, everything works as usual; if there’s a name conflict where one base marks it final, the subclass cannot override it.
- Does not attempt to intercept complex descriptor dynamics beyond
types.FunctionType(e.g., some dynamic__getattr__patterns). - Does not attempt to block low-level metaclass-level direct
__dict__manipulation (deliberate attack scenarios).
Tests
After installing dev dependencies:
uv sync --extra dev
uv run pytest -q
Or using pip:
pip install -e .[dev]
pytest -q
Versioning
Semantic Versioning. In the early phase (<1.0.0), breaking changes may occur.
Current version: 0.1.3.
Roadmap (potential)
- Add an opt-in mode for locking instance-level resources.
- Integrate with type checkers (mypy/pyright) via plugin (static early warnings).
- Provide a CLI quick scan (list which names are locked).
- Track override intent: allow explicit opt-out via
@allow_override(in design).
License
MIT License. See LICENSE.
Feedback / Contribution
Issues and PRs are welcome:
- Describe your use case and expected behavior
- Provide a minimal reproducible example
- Specify Python version and package version
For performance topics, please include a simple benchmark (timeit or pyperf).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file access_guard-0.1.6.tar.gz.
File metadata
- Download URL: access_guard-0.1.6.tar.gz
- Upload date:
- Size: 7.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46824d9a07a9fa4f75bd745bca4c007e277a4d4f9e8f7c3f285ce2840715908a
|
|
| MD5 |
06872b3015c05d61c462a940870853ea
|
|
| BLAKE2b-256 |
10b39fdcbf2d7cc041680119c5a83d2233f60973aa9141d8b74ebcea15426e07
|
File details
Details for the file access_guard-0.1.6-py3-none-any.whl.
File metadata
- Download URL: access_guard-0.1.6-py3-none-any.whl
- Upload date:
- Size: 7.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ecbe3e40d6e560b8dee66d42af27f7301d4104eaff5ff8f1e55c43961176fb9
|
|
| MD5 |
cc8d85441d8ddb7908746b5fabac2ac2
|
|
| BLAKE2b-256 |
7f848fe88e95312a2722e9ffe63cea23da4df937ce9f94b09fcc4d84f951343f
|