# Limen
Limen is an access control system that provides fine-grained security and encapsulation for Python classes. It implements true C++ semantics including public, protected, and private access levels, friend relationships, and inheritance-based access control with automatic detection of access levels based on method naming conventions.
*Limen (Latin: "threshold") - The boundary between spaces, representing the controlled passage between public and private domains.*
### Key Features
- **C++ Style Access Control**: Complete implementation of `@private`, `@protected`, `@public` decorators
- **Implicit Access Control**: Automatic access level detection based on naming conventions (_, __, normal names)
- **Name Mangling Bypass Prevention**: Blocks circumvention of access control via `_ClassName__method` patterns
- **Friend Relationships**: Support for `@friend` classes, methods, functions, and staticmethods/classmethods
- **Advanced Inheritance**: True C++ style inheritance with public, protected, and private inheritance patterns
- **Dual-Layer Security**: Access modifiers on friend methods for fine-grained permission control
- **Descriptor Support**: Full compatibility with `@staticmethod`, `@classmethod`, `@property` decorators
- **Multiple Inheritance**: Support for complex inheritance hierarchies with proper access control
- **Runtime Management**: Dynamic enable/disable enforcement, metrics, and debugging capabilities
- **Enhanced Error Handling**: Contextual exception types with detailed error messages, code suggestions, and intelligent formatting
- **Zero Dependencies**: Pure Python implementation with no external requirements
<details>
<summary><strong>Installation</strong></summary>
## Installation
### From PyPI (Recommended)
```bash
pip install pylimen
```
### From Source
```bash
git clone https://github.com/Ma1achy/Limen.git
cd Limen
pip install -e .
```
</details>
<details>
<summary><strong>Access Control & Inheritance</strong></summary>
## Access Control & Inheritance
Limen provides comprehensive access control through explicit decorators and C++ style inheritance semantics with public, protected, and private inheritance types.
### Basic Access Control Decorators
#### @private - Same Class Only
Private methods are only accessible within the same class where they're defined.
```python
from limen import private, protected, public, friend
class Base:
@private
def _private_method(self):
return "private"
@public
def public_method(self):
# Works - same class access
return self._private_method()
obj = Base()
obj.public_method() # Works - public access
# obj._private_method() # PermissionDeniedError - private access
```
#### @protected - Inheritance Hierarchy
Protected methods are accessible within the same class and its subclasses.
```python
class Base:
@protected
def _protected_method(self):
return "protected"
class Derived(Base):
def foo(self):
# Works - derived class can access protected members
return self._protected_method()
obj = Derived()
obj.foo() # Works - calls protected method internally
# obj._protected_method() # PermissionDeniedError - external access blocked
```
#### @public - Universal Access
Public methods are accessible from anywhere (default Python behavior, useful for explicit documentation).
```python
class Base:
@public
def get_data(self):
return "data"
@public
def check_status(self):
return "ok"
obj = Base()
obj.get_data() # Works from anywhere
obj.check_status() # Works from anywhere
```
### C++ Style Inheritance Control
Apply inheritance decorators to modify access levels of inherited members according to C++ semantics.
#### Public Inheritance (Default)
Standard Python inheritance behavior where access levels are preserved:
```python
class Base:
def public_method(self): # Implicit @public
return "public"
def _protected_method(self): # Implicit @protected
return "protected"
def __private_method(self): # Implicit @private
return "private"
class Derived(Base):
def test_access(self):
# Can access public and protected members from base
public_data = self.public_method() # Inherited as public
protected_data = self._protected_method() # Inherited as protected
# Cannot access private members from base
# private_data = self.__private_method() # PermissionDeniedError
return f"{public_data}, {protected_data}"
obj = Derived()
result = obj.test_access() # Works internally
external_public = obj.public_method() # Works externally - public access
# external_protected = obj._protected_method() # PermissionDeniedError - protected
```
#### Protected Inheritance
Protected inheritance converts public members to protected, following C++ semantics:
```python
class Base:
def public_method(self): # Implicit @public
return "public"
def _protected_method(self): # Implicit @protected
return "protected"
@private
def _private_method(self): # Explicit @private
return "private"
@protected(Base) # Protected inheritance - applies implicit control to Base
class Derived(Base):
def operation(self):
# Can access all inherited members internally
public_data = self.public_method() # Now protected due to inheritance
protected_data = self._protected_method() # Remains protected
# Cannot access private members
# secret = self._private_method() # PermissionDeniedError
return f"{public_data}, {protected_data}"
obj = Derived()
result = obj.operation() # Works internally
# External access - all methods are now protected due to inheritance
# obj.public_method() # PermissionDeniedError - public became protected
# obj._protected_method() # PermissionDeniedError - protected remains protected
```
#### Private Inheritance
Private inheritance makes all inherited members private to the derived class:
```python
class Base:
def public_method(self): # Implicit @public
return "public"
def _protected_method(self): # Implicit @protected
return "protected"
@private(Base) # Private inheritance
class Derived(Base):
def operation(self):
# Can access inherited members internally
public_data = self.public_method() # Now private due to inheritance
protected_data = self._protected_method() # Now private due to inheritance
return f"{public_data}, {protected_data}"
def public_interface(self):
# Expose functionality through controlled interface
return self.operation()
obj = Derived()
result = obj.public_interface() # Works - controlled access
# External access blocked - all inherited methods are now private
# obj.public_method() # PermissionDeniedError - public became private
# obj._protected_method() # PermissionDeniedError - protected became private
```
#### Inheritance Summary
| Inheritance Type | Public Members | Protected Members | Private Members |
|------------------|----------------|-------------------|-----------------|
| **Public** (default) | Remain public | Remain protected | Remain private (inaccessible) |
| **Protected** `@protected(Base)` | Become protected | Remain protected | Remain private (inaccessible) |
| **Private** `@private(Base)` | Become private | Become private | Remain private (inaccessible) |
### Multiple Inheritance with Access Control
Apply inheritance decorators to multiple base classes for complex access patterns:
```python
class BaseA:
def method_a(self): # Implicit @public
return "a"
def _helper_a(self): # Implicit @protected
return "helper a"
class BaseB:
def method_b(self): # Implicit @public
return "b"
def _helper_b(self): # Implicit @protected
return "helper b"
# Multiple inheritance with different access patterns
@protected(BaseA) # Only BaseA gets protected inheritance
@private(BaseB) # Only BaseB gets private inheritance
class Derived(BaseA, BaseB):
def operation(self):
# BaseA methods - protected due to inheritance
a_method = self.method_a() # Now protected
a_helper = self._helper_a() # Still protected
# BaseB methods - private due to inheritance
b_method = self.method_b() # Now private
b_helper = self._helper_b() # Now private
return f"{a_method}, {a_helper}, {b_method}, {b_helper}"
def public_interface(self):
return self.operation()
obj = Derived()
result = obj.public_interface() # Works - controlled access
# External access follows inheritance rules
# obj.method_a() # PermissionDeniedError - protected inheritance
# obj.method_b() # PermissionDeniedError - private inheritance
```
### Friend Relationships with Inheritance
Friend relationships are preserved across inheritance patterns:
```python
class Target:
def _protected_method(self): # Implicit @protected
return "protected"
@friend(Target)
class Helper:
def access_target(self, target):
# Friend can access protected members
return target._protected_method()
# Protected inheritance preserves friend relationships
@protected(Target)
class Derived(Target):
def internal_operation(self):
return self._protected_method() # Works internally
helper = Helper()
derived_obj = Derived()
# Friend access works even with inheritance
result = helper.access_target(derived_obj) # Works - friend relationship preserved
# Regular external access blocked
# derived_obj._protected_method() # PermissionDeniedError - protected access
```
\n</details><details>\n<summary><strong>Implicit Access Control</strong></summary>## Implicit Access Control
Limen provides automatic access level detection based on Python naming conventions. When inheritance decorators are applied, methods are automatically wrapped with appropriate access control based on their names.
### Naming Convention Rules
- **Normal names** (e.g., `method_name`) → `@public`
- **Single underscore prefix** (e.g., `_method_name`) → `@protected`
- **Double underscore prefix** (e.g., `__method_name`) → `@private`
### Automatic Application with Inheritance Decorators
When you use inheritance decorators like `@protected(BaseClass)`, implicit access control is automatically applied to both the base class and derived class:
```python
class Base:
def public_method(self): # Automatically treated as @public
return "public"
def _protected_method(self): # Automatically treated as @protected
return "protected"
def __private_method(self): # Automatically treated as @private
return "private"
@public # Explicit decorator overrides implicit
def _explicitly_public(self):
return "explicit public"
# Inheritance decorator applies implicit access control to Base
@protected(Base)
class Derived(Base):
def test_access(self):
# Can access all inherited methods internally
public_data = self.public_method() # Inherited public method
protected_data = self._protected_method() # Inherited protected method
explicit_data = self._explicitly_public() # Explicit override
return f"{public_data}, {protected_data}, {explicit_data}"
obj = Derived()
# Internal access works
result = obj.test_access() # Works - internal access
# External access controlled by inheritance rules
# Protected inheritance converts public methods to protected
obj.public_method() # PermissionDeniedError - public became protected
obj._protected_method() # PermissionDeniedError - protected method
obj._explicitly_public() # PermissionDeniedError - explicit public became protected
```
### Manual Application
You can also manually apply implicit access control without inheritance:
```python
from limen.utils.implicit import apply_implicit_access_control
class Base:
def public_method(self):
return "public"
def _protected_method(self):
return "protected"
def __private_method(self):
return "private"
# Manually apply implicit access control
apply_implicit_access_control(Base)
obj = Base()
obj.public_method() # Works - public access
# obj._protected_method() # PermissionDeniedError - protected access
# obj.__private_method() # PermissionDeniedError - private access (name mangled)
```
### Explicit Override of Implicit Rules
Explicit decorators always take precedence over implicit naming conventions:
```python
class Base:
@private # Explicit private
def normal_name_but_private(self): # Normal name, but explicitly private
return "private despite normal name"
@public # Explicit public
def _underscore_but_public(self): # Underscore name, but explicitly public
return "public despite underscore"
@protected(Base) # Apply implicit control
class Derived(Base):
pass
obj = Derived()
# Explicit decorators override naming conventions
# obj.normal_name_but_private() # PermissionDeniedError - explicitly private
obj._underscore_but_public() # PermissionDeniedError - but protected inheritance affects it
```
\n</details><details>\n<summary><strong>Friend Relationships</strong></summary>## Friend Relationships
Friend classes and functions can access private and protected members of target classes, providing controlled access across class boundaries.
### Friend Classes
Friend classes can access private and protected members of the target class.
```python
class Target:
@private
def _private_method(self):
return "private"
@protected
def _protected_method(self):
return "protected"
@friend(Target)
class FriendA:
def access_target(self, target):
# Friend can access private methods
private_data = target._private_method()
protected_data = target._protected_method()
return f"{private_data}, {protected_data}"
@friend(Target)
class FriendB:
def inspect_target(self, target):
# Multiple classes can be friends
return target._protected_method()
# Usage
target = Target()
friend_a = FriendA()
friend_b = FriendB()
friend_a.access_target(target) # Friend access works
friend_b.inspect_target(target) # Multiple friends work
# Regular class cannot access private members
class Regular:
def try_access(self, target):
# PermissionDeniedError - not a friend
return target._protected_method()
```
### Friend Functions
Friend functions are standalone functions that can access private and protected members.
```python
class Target:
@private
def _private_method(self):
return "private"
@protected
def _protected_method(self):
return "protected"
@friend(Target)
def friend_function_a(target):
"""Friend function for processing"""
private_data = target._private_method()
return f"Processed: {private_data}"
@friend(Target)
def friend_function_b(target):
"""Friend function for analysis"""
protected_data = target._protected_method()
return f"Analyzed: {protected_data}"
def regular_function(target):
"""Regular function - no friend access"""
# PermissionDeniedError - cannot access private methods
return target._private_method()
# Usage
target = Target()
friend_function_a(target) # Friend function works
friend_function_b(target) # Another friend function works
# regular_function(target) # PermissionDeniedError
```
### Friend Descriptors
Friend relationships work with all Python descriptor types: staticmethod, classmethod, and property.
```python
class Target:
def __init__(self, value):
self._value = value
@private
def _private_method(self):
return "private"
@private
@property
def private_property(self):
return self._value
class Helper:
# Friend staticmethod
@friend(Target)
@staticmethod
def static_helper(target):
return target._private_method()
# Friend classmethod
@friend(Target)
@classmethod
def class_helper(cls, target):
return target._private_method()
# Friend instance method accessing property
@friend(Target)
def access_property(self, target):
return target.private_property
target = Target("secret")
result1 = Helper.static_helper(target) # Works
result2 = Helper.class_helper(target) # Works
helper = Helper()
result3 = helper.access_property(target) # Works
```
\n</details><details>\n<summary><strong>Dual-Layer Security: Access Modifiers on Friend Methods</strong></summary>## Dual-Layer Security: Access Modifiers on Friend Methods
**Advanced Feature**: Apply access modifiers to friend methods themselves for fine-grained control.
```python
class Target:
@private
def _private_method(self):
return "private"
class Helper:
# Public friend method - anyone can call it
@public
@friend(Target)
def public_access(self, target):
return target._private_method()
# Protected friend method - only inheritance can use it
@protected
@friend(Target)
def protected_access(self, target):
return target._private_method()
# Private friend method - only internal use
@private
@friend(Target)
def private_access(self, target):
return target._private_method()
def internal_operation(self, target):
# Can use private friend method internally
return self.private_access(target)
class DerivedHelper(Helper):
def inherited_operation(self, target):
# Can use protected friend method via inheritance
return self.protected_access(target)
# Usage
target = Target()
helper = Helper()
derived = DerivedHelper()
# Public friend method works for everyone
helper.public_access(target)
# Protected friend method works via inheritance
derived.inherited_operation(target)
# Private friend method works via internal access
helper.internal_operation(target)
# Direct access to protected/private friend methods blocked
# helper.protected_access(target) # PermissionDeniedError
# helper.private_access(target) # PermissionDeniedError
```
### Staticmethod and Classmethod with Access Modifiers
```python
class Target:
@private
def _private_method(self):
return "private"
class Helper:
# Protected friend staticmethod
@protected
@friend(Target)
@staticmethod
def protected_static_helper(target):
return target._private_method()
# Private friend classmethod
@private
@friend(Target)
@classmethod
def private_class_helper(cls, target):
return target._private_method()
@classmethod
def internal_class_operation(cls, target):
# Can use private classmethod internally
return cls.private_class_helper(target)
class DerivedHelper(Helper):
@classmethod
def use_protected_static(cls, target):
# Can use protected staticmethod via inheritance
return cls.protected_static_helper(target)
target = Target()
helper = Helper()
derived = DerivedHelper()
# Protected staticmethod works via inheritance
derived.use_protected_static(target)
# Private classmethod works via internal access
helper.internal_class_operation(target)
# Direct access blocked
# Helper.protected_static_helper(target) # PermissionDeniedError
# Helper.private_class_helper(target) # PermissionDeniedError
```
\n</details><details>\n<summary><strong>Security Features</strong></summary>## Security Features
### Name Mangling Bypass Prevention
**Critical Security Feature**: Limen prevents bypassing access control through Python's name mangling mechanism using multiple protection layers.
Python automatically converts private methods like `__private_method` to `_ClassName__private_method`. Without protection, external code could bypass access control by directly accessing the mangled name:
#### Protection for Implicit Private Methods
```python
class SecureClass:
def __private_method(self):
return "secret data"
def public_access(self):
return self.__private_method() # Legitimate internal access
# Apply implicit access control (detects __ methods as private)
from limen.utils.implicit import apply_implicit_access_control
apply_implicit_access_control(SecureClass)
obj = SecureClass()
# Internal access works
result = obj.public_access() # "secret data"
# Direct access blocked (AttributeError)
# obj.__private_method() # AttributeError: no attribute '__private_method'
# Name mangling bypass blocked (PermissionDeniedError)
# obj._SecureClass__private_method() # PermissionDeniedError: Access denied to private method
```
#### Protection for Explicit @private Decorators
Explicit `@private` decorators also prevent name mangling bypasses through descriptor-level access control:
```python
from limen import private
class SecureClass:
@private
def __private_method(self):
return "secret data"
@private
def regular_private(self):
return "also secret"
def public_access(self):
return f"{self.__private_method()}, {self.regular_private()}"
obj = SecureClass()
# Internal access works
result = obj.public_access() # "secret data, also secret"
# Direct access blocked (PermissionDeniedError)
# obj.regular_private() # PermissionDeniedError: Access denied to private method
# Name mangling bypass blocked (PermissionDeniedError)
# obj._SecureClass__private_method() # PermissionDeniedError: Access denied to private method
# Manual mangling attempts fail (AttributeError)
# obj._SecureClass__regular_private() # AttributeError: no such attribute
```
**How It Works:**
- **Explicit decorators**: Descriptor-level access control validates every method call regardless of access path
- **Implicit detection**: Custom `__getattribute__` protection intercepts mangled name access for `__` methods
- **Dual protection**: Methods can be protected by both mechanisms simultaneously
- **Friend preservation**: Authorized friends can still access via any legitimate method
**Friend Access Still Works:**
```python
class DataStore:
def __private_data(self):
return "sensitive"
@private
def __explicit_private(self):
return "explicit sensitive"
@friend(DataStore)
class AuthorizedProcessor:
def process(self, store):
# Friend can access via mangled name when authorized (both types)
implicit = store._DataStore__private_data()
explicit = store._DataStore__explicit_private()
return f"{implicit}, {explicit}"
apply_implicit_access_control(DataStore)
store = DataStore()
processor = AuthorizedProcessor()
result = processor.process(store) # Works - friend access allowed
# Unauthorized access still blocked for both
class UnauthorizedClass:
def hack(self, store):
return store._DataStore__private_data() # PermissionDeniedError
unauthorized = UnauthorizedClass()
# unauthorized.hack(store) # PermissionDeniedError: Access denied
```
This security feature ensures that Limen's access control cannot be circumvented through Python's name mangling, providing true encapsulation and security for your private methods.
\n</details><details>\n<summary><strong>Property Access Control</strong></summary>## Property Access Control
Control getter and setter access independently with sophisticated property decorators.
### Basic Property Control
```python
class Base:
def __init__(self, name, value):
self._name = name
self._value = value
@protected
@property
def value(self):
"""Protected getter - accessible in inheritance"""
return self._value
@value.setter
@private
def value(self, new_value):
"""Private setter - only same class"""
if new_value > 0:
self._value = new_value
def update_value(self, amount):
# Private setter works within same class
self.value = self._value + amount
@public
@property
def name(self):
"""Public getter"""
return self._name
class Derived(Base):
def check_value(self):
# Can read value (protected getter)
return f"Value: {self.value}"
def try_modify_value(self, new_value):
# Cannot use private setter
# self.value = new_value # PermissionDeniedError
pass
obj1 = Base("item1", 100)
obj2 = Derived("item2", 200)
# Public property access
print(obj1.name)
# Protected property access via inheritance
print(obj2.check_value())
# Internal value modification
obj1.update_value(50)
# External access to protected property
# print(obj1.value) # PermissionDeniedError
```
### Friend Access to Properties
```python
class Target:
def __init__(self, value):
self._value = value
@private
@property
def private_property(self):
return self._value
@friend(Target)
class Friend:
def access_property(self, target):
# Friend can access private property
value = target.private_property
return f"Accessed: {value}"
target = Target("secret")
friend = Friend()
result = friend.access_property(target) # Works
```
\n</details><details>\n<summary><strong>System Management</strong></summary>## System Management
### Runtime Control
```python
from limen import (
enable_enforcement,
disable_enforcement,
is_enforcement_enabled,
get_access_control_system
)
class Base:
@private
def _private_method(self):
return "private"
obj = Base()
# Normal enforcement
try:
obj._private_method() # PermissionDeniedError
except PermissionDeniedError:
print("Access blocked")
# Disable enforcement (useful for testing)
disable_enforcement()
result = obj._private_method() # Now works
print(f"Access allowed: {result}")
# Re-enable enforcement
enable_enforcement()
# obj.secret_method() # PermissionDeniedError again
# Check enforcement status
print(f"Enforcement enabled: {is_enforcement_enabled()}")
```
### System Metrics and Debugging
```python
from limen.system import get_access_control_system
# Get system instance for advanced operations
access_control = get_access_control_system()
# Check enforcement status
print(f"Enforcement enabled: {access_control.enforcement_enabled}")
# Get friendship relationships count
friendship_manager = access_control._friendship_manager
print(f"Total friend relationships: {friendship_manager.get_friends_count()}")
print(f"Classes with friends: {friendship_manager.get_relationships_count()}")
# Reset system state (useful for testing)
from limen import reset_system
reset_system()
```
\n</details><details>\n<summary><strong>Error Handling</strong></summary>## Error Handling
Limen provides comprehensive, contextual exception types with enhanced error messages that include actual code suggestions and detailed explanations.
### Exception Types
```python
from limen.exceptions import (
LimenError, # Base exception for all Limen errors
PermissionDeniedError, # Access denied to private/protected members
DecoratorConflictError, # Conflicting access level decorators
DecoratorUsageError, # Incorrect decorator usage
)
```
#### LimenError
Base exception class for all Limen access control errors. All other Limen exceptions inherit from this.
#### PermissionDeniedError
Raised when attempting to access private or protected members from unauthorized contexts.
```python
class SecureClass:
@private
def secret_method(self):
return "secret"
try:
obj = SecureClass()
obj.secret_method() # Unauthorized access
except PermissionDeniedError as e:
print(f"Access denied: {e}")
# Output: Access denied to private method secret_method
```
#### DecoratorConflictError
Raised when conflicting access level decorators are applied to the same method. Provides enhanced error messages with actual code suggestions and function body extraction.
```python
# This will raise an error during class creation
try:
class ConflictClass:
@private
@protected # Conflicting access levels
def conflicted_method(self, data: str) -> str:
return f"processing {data}"
except DecoratorConflictError as e:
print(f"Decorator conflict: {e}")
# Enhanced output shows:
# Conflicting access level decorators on conflicted_method():
# already has @private, cannot apply @protected.
# Did you mean:
# @protected
# def conflicted_method(self, data: str) -> str:
# return f"processing {data}"
# ?
```
#### DecoratorUsageError
Raised when decorators are used incorrectly (wrong context, invalid syntax, etc.). Provides contextual suggestions based on the specific misuse.
```python
# Invalid decorator usage examples:
# 1. Module-level function (not allowed)
try:
@private # Cannot use on module-level function
def module_function():
pass
except DecoratorUsageError as e:
print(f"Invalid usage: {e}")
# Output: @private cannot be applied to module-level functions.
# Access control decorators can only be used on class methods.
# Did you mean to put this function inside a class?
# 2. Bare class decoration (missing inheritance target)
try:
@private # Missing class argument
class MyClass:
pass
except DecoratorUsageError as e:
print(f"Invalid usage: {e}")
# Output: @private cannot be applied to a class without specifying a class to inherit from.
# Did you mean: @private(BaseClass) ?
# 3. Duplicate decorator application
try:
class DuplicateClass:
@private
@private # Applied twice
def duplicate_method(self):
return "data"
except DecoratorConflictError as e:
print(f"Duplicate decorator: {e}")
# Output: @private was applied to duplicate_method() more than once!
# Did you mean:
# @private
# def duplicate_method(self):
# return "data"
# ?
```
### Enhanced Error Messages
Limen's error system provides contextual, helpful error messages that include:
- **Actual function signatures** with type annotations
- **Real function body content** (not just "pass")
- **Specific suggestions** for fixing the error
- **Contextual help** based on the type of mistake
```python
# Example with complex method signature
class ExampleClass:
@property
@private
@protected # Conflict error
def complex_property(self) -> Dict[str, int]:
return {"count": 42, "status": 1}
# Error message will show:
# Conflicting access level decorators on complex_property:
# already has @private, cannot apply @protected.
# Did you mean:
# @property
# @protected
# def complex_property(self) -> Dict[str, int]:
# return {"count": 42, "status": 1}
# ?
```
### Property vs Method Formatting
Error messages intelligently format target names based on the member type:
- **Methods**: Show with parentheses `method_name()`
- **Properties**: Show without parentheses `property_name`
```python
# Property error (no parentheses)
class MyClass:
@property
@private
@private # Duplicate
def my_prop(self):
return "value"
# Error: @private was applied to my_prop more than once!
# Method error (with parentheses)
class MyClass:
@private
@private # Duplicate
def my_method(self):
return "value"
# Error: @private was applied to my_method() more than once!
```
### Error System Architecture
Limen's error system is built with a modular, maintainable architecture:
- **`method_utils.py`**: Method introspection utilities
- `MethodInspector`: Extracts method types, arguments, and decorators with type hints
- `FunctionBodyExtractor`: Extracts actual function implementation code
- `TargetFormatter`: Formats method names appropriately (with/without parentheses)
- **`message_generators.py`**: Contextual message generation
- `MessageGenerator`: Creates detailed, helpful error messages with code suggestions
- Handles different error scenarios with specific, actionable advice
- **`limen_errors.py`**: Clean, focused exception classes
- Each exception focuses on its core responsibility
- Uses composition for shared functionality
- Easy to extend and maintain
This modular design ensures that error messages are consistent, helpful, and maintainable as the system grows.
\n</details><details>\n<summary><strong>Testing and Development</strong></summary>## Testing and Development
### Testing with Enforcement Control
```python
import unittest
from limen import disable_enforcement, enable_enforcement
class TestSecureClass(unittest.TestCase):
def setUp(self):
# Disable enforcement for easier testing
disable_enforcement()
def tearDown(self):
# Re-enable enforcement
enable_enforcement()
def test_private_method_access(self):
class TestClass:
@private
def _secret(self):
return "secret"
obj = TestClass()
# This works because enforcement is disabled
result = obj._secret()
self.assertEqual(result, "secret")
# Or use context manager approach
from limen.system import get_access_control_system
def test_with_disabled_enforcement():
access_control = get_access_control_system()
original_state = access_control.enforcement_enabled
try:
access_control.enforcement_enabled = False
# Test code here with enforcement disabled
pass
finally:
access_control.enforcement_enabled = original_state
```
### Debugging Friend Relationships
```python
from limen.system import get_access_control_system
class TargetClass:
@private
def secret(self):
return "secret"
@friend(TargetClass)
class FriendClass:
pass
# Debug friendship relationships
access_control = get_access_control_system()
friendship_manager = access_control._friendship_manager
print(f"Total friends: {friendship_manager.get_friends_count()}")
print(f"Classes with friends: {friendship_manager.get_relationships_count()}")
# Check if specific friendship exists
is_friend = friendship_manager.is_friend(TargetClass, FriendClass)
print(f"FriendClass is friend of TargetClass: {is_friend}")
```
\n</details><details>\n<summary><strong>Requirements</strong></summary>## Requirements
- **Python 3.12+**
- **No external dependencies** for core functionality
- **Optional development dependencies** for testing and development
\n</details><details>\n<summary><strong>Development Setup</strong></summary>## Development Setup
### Clone and Setup
```bash
git clone https://github.com/Ma1achy/Limen.git
cd Limen
pip install -e .[dev]
```
### Run Tests
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=limen
# Run specific test categories
pytest -m access_control # Access control tests
pytest -m friend_methods # Friend relationship tests
pytest -m inheritance # Inheritance tests
pytest -m edge_cases # Edge cases and boundary tests
```
### Development Commands
```bash
# Format code
black limen/ tests/
# Type checking
mypy limen/
# Lint code
flake8 limen/ tests/
```
\n</details><details>\n<summary><strong>Contributing</strong></summary>## Contributing
Contributions are welcome! Please feel free to submit pull requests, report bugs, or suggest features.
### Development Guidelines
1. **Add tests** for new features
2. **Update documentation** for API changes
3. **Follow code style** (Black formatting, type hints)
4. **Ensure compatibility** with Python 3.8+
\n</details><details>\n<summary><strong>License</strong></summary>## LicenseMIT License - see [LICENSE](LICENSE) file for details.</details>
Raw data
{
"_id": null,
"home_page": null,
"name": "pylimen",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "access-control, private, protected, public, friend, decorators, oop, security, inheritance, encapsulation, cpp-style",
"author": null,
"author_email": "Ma1achy <malachydoherty16@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/02/e8/273f96ac3abab40299194dc203262640cc89a95b3414de82c293ddcc45c6/pylimen-1.1.0.tar.gz",
"platform": null,
"description": "# Limen\r\n\r\nLimen is an access control system that provides fine-grained security and encapsulation for Python classes. It implements true C++ semantics including public, protected, and private access levels, friend relationships, and inheritance-based access control with automatic detection of access levels based on method naming conventions.\r\n\r\n*Limen (Latin: \"threshold\") - The boundary between spaces, representing the controlled passage between public and private domains.*\r\n\r\n### Key Features\r\n\r\n- **C++ Style Access Control**: Complete implementation of `@private`, `@protected`, `@public` decorators\r\n- **Implicit Access Control**: Automatic access level detection based on naming conventions (_, __, normal names)\r\n- **Name Mangling Bypass Prevention**: Blocks circumvention of access control via `_ClassName__method` patterns\r\n- **Friend Relationships**: Support for `@friend` classes, methods, functions, and staticmethods/classmethods\r\n- **Advanced Inheritance**: True C++ style inheritance with public, protected, and private inheritance patterns\r\n- **Dual-Layer Security**: Access modifiers on friend methods for fine-grained permission control\r\n- **Descriptor Support**: Full compatibility with `@staticmethod`, `@classmethod`, `@property` decorators\r\n- **Multiple Inheritance**: Support for complex inheritance hierarchies with proper access control\r\n- **Runtime Management**: Dynamic enable/disable enforcement, metrics, and debugging capabilities\r\n- **Enhanced Error Handling**: Contextual exception types with detailed error messages, code suggestions, and intelligent formatting\r\n- **Zero Dependencies**: Pure Python implementation with no external requirements\r\n\r\n<details>\r\n<summary><strong>Installation</strong></summary>\r\n\r\n## Installation\r\n\r\n### From PyPI (Recommended)\r\n```bash\r\npip install pylimen\r\n```\r\n\r\n### From Source\r\n```bash\r\ngit clone https://github.com/Ma1achy/Limen.git\r\ncd Limen\r\npip install -e .\r\n```\r\n\r\n</details>\r\n\r\n<details>\r\n<summary><strong>Access Control & Inheritance</strong></summary>\r\n\r\n## Access Control & Inheritance\r\n\r\nLimen provides comprehensive access control through explicit decorators and C++ style inheritance semantics with public, protected, and private inheritance types.\r\n\r\n### Basic Access Control Decorators\r\n\r\n#### @private - Same Class Only\r\n\r\nPrivate methods are only accessible within the same class where they're defined.\r\n\r\n```python\r\nfrom limen import private, protected, public, friend\r\n\r\nclass Base:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n \r\n @public\r\n def public_method(self):\r\n # Works - same class access\r\n return self._private_method()\r\n\r\nobj = Base()\r\nobj.public_method() # Works - public access\r\n# obj._private_method() # PermissionDeniedError - private access\r\n```\r\n\r\n#### @protected - Inheritance Hierarchy\r\n\r\nProtected methods are accessible within the same class and its subclasses.\r\n\r\n```python\r\nclass Base:\r\n @protected\r\n def _protected_method(self):\r\n return \"protected\"\r\n\r\nclass Derived(Base):\r\n def foo(self):\r\n # Works - derived class can access protected members\r\n return self._protected_method()\r\n\r\nobj = Derived()\r\nobj.foo() # Works - calls protected method internally\r\n# obj._protected_method() # PermissionDeniedError - external access blocked\r\n```\r\n\r\n#### @public - Universal Access\r\n\r\nPublic methods are accessible from anywhere (default Python behavior, useful for explicit documentation).\r\n\r\n```python\r\nclass Base:\r\n @public\r\n def get_data(self):\r\n return \"data\"\r\n \r\n @public\r\n def check_status(self):\r\n return \"ok\"\r\n\r\nobj = Base()\r\nobj.get_data() # Works from anywhere\r\nobj.check_status() # Works from anywhere\r\n```\r\n\r\n### C++ Style Inheritance Control\r\n\r\nApply inheritance decorators to modify access levels of inherited members according to C++ semantics.\r\n\r\n#### Public Inheritance (Default)\r\n\r\nStandard Python inheritance behavior where access levels are preserved:\r\n\r\n```python\r\nclass Base:\r\n def public_method(self): # Implicit @public\r\n return \"public\"\r\n \r\n def _protected_method(self): # Implicit @protected\r\n return \"protected\"\r\n \r\n def __private_method(self): # Implicit @private\r\n return \"private\"\r\n\r\nclass Derived(Base):\r\n def test_access(self):\r\n # Can access public and protected members from base\r\n public_data = self.public_method() # Inherited as public\r\n protected_data = self._protected_method() # Inherited as protected\r\n \r\n # Cannot access private members from base\r\n # private_data = self.__private_method() # PermissionDeniedError\r\n \r\n return f\"{public_data}, {protected_data}\"\r\n\r\nobj = Derived()\r\nresult = obj.test_access() # Works internally\r\nexternal_public = obj.public_method() # Works externally - public access\r\n# external_protected = obj._protected_method() # PermissionDeniedError - protected\r\n```\r\n\r\n#### Protected Inheritance\r\n\r\nProtected inheritance converts public members to protected, following C++ semantics:\r\n\r\n```python\r\nclass Base:\r\n def public_method(self): # Implicit @public\r\n return \"public\"\r\n \r\n def _protected_method(self): # Implicit @protected\r\n return \"protected\"\r\n \r\n @private\r\n def _private_method(self): # Explicit @private\r\n return \"private\"\r\n\r\n@protected(Base) # Protected inheritance - applies implicit control to Base\r\nclass Derived(Base):\r\n def operation(self):\r\n # Can access all inherited members internally\r\n public_data = self.public_method() # Now protected due to inheritance\r\n protected_data = self._protected_method() # Remains protected\r\n # Cannot access private members\r\n # secret = self._private_method() # PermissionDeniedError\r\n return f\"{public_data}, {protected_data}\"\r\n\r\nobj = Derived()\r\nresult = obj.operation() # Works internally\r\n\r\n# External access - all methods are now protected due to inheritance\r\n# obj.public_method() # PermissionDeniedError - public became protected\r\n# obj._protected_method() # PermissionDeniedError - protected remains protected\r\n```\r\n\r\n#### Private Inheritance\r\n\r\nPrivate inheritance makes all inherited members private to the derived class:\r\n\r\n```python\r\nclass Base:\r\n def public_method(self): # Implicit @public\r\n return \"public\"\r\n \r\n def _protected_method(self): # Implicit @protected\r\n return \"protected\"\r\n\r\n@private(Base) # Private inheritance\r\nclass Derived(Base):\r\n def operation(self):\r\n # Can access inherited members internally\r\n public_data = self.public_method() # Now private due to inheritance\r\n protected_data = self._protected_method() # Now private due to inheritance\r\n return f\"{public_data}, {protected_data}\"\r\n \r\n def public_interface(self):\r\n # Expose functionality through controlled interface\r\n return self.operation()\r\n\r\nobj = Derived()\r\nresult = obj.public_interface() # Works - controlled access\r\n\r\n# External access blocked - all inherited methods are now private\r\n# obj.public_method() # PermissionDeniedError - public became private\r\n# obj._protected_method() # PermissionDeniedError - protected became private\r\n```\r\n\r\n#### Inheritance Summary\r\n\r\n| Inheritance Type | Public Members | Protected Members | Private Members |\r\n|------------------|----------------|-------------------|-----------------|\r\n| **Public** (default) | Remain public | Remain protected | Remain private (inaccessible) |\r\n| **Protected** `@protected(Base)` | Become protected | Remain protected | Remain private (inaccessible) |\r\n| **Private** `@private(Base)` | Become private | Become private | Remain private (inaccessible) |\r\n\r\n### Multiple Inheritance with Access Control\r\n\r\nApply inheritance decorators to multiple base classes for complex access patterns:\r\n\r\n```python\r\nclass BaseA:\r\n def method_a(self): # Implicit @public\r\n return \"a\"\r\n \r\n def _helper_a(self): # Implicit @protected\r\n return \"helper a\"\r\n\r\nclass BaseB:\r\n def method_b(self): # Implicit @public\r\n return \"b\"\r\n \r\n def _helper_b(self): # Implicit @protected\r\n return \"helper b\"\r\n\r\n# Multiple inheritance with different access patterns\r\n@protected(BaseA) # Only BaseA gets protected inheritance\r\n@private(BaseB) # Only BaseB gets private inheritance\r\nclass Derived(BaseA, BaseB):\r\n def operation(self):\r\n # BaseA methods - protected due to inheritance\r\n a_method = self.method_a() # Now protected\r\n a_helper = self._helper_a() # Still protected\r\n \r\n # BaseB methods - private due to inheritance\r\n b_method = self.method_b() # Now private\r\n b_helper = self._helper_b() # Now private\r\n \r\n return f\"{a_method}, {a_helper}, {b_method}, {b_helper}\"\r\n \r\n def public_interface(self):\r\n return self.operation()\r\n\r\nobj = Derived()\r\nresult = obj.public_interface() # Works - controlled access\r\n\r\n# External access follows inheritance rules\r\n# obj.method_a() # PermissionDeniedError - protected inheritance\r\n# obj.method_b() # PermissionDeniedError - private inheritance\r\n```\r\n\r\n### Friend Relationships with Inheritance\r\n\r\nFriend relationships are preserved across inheritance patterns:\r\n\r\n```python\r\nclass Target:\r\n def _protected_method(self): # Implicit @protected\r\n return \"protected\"\r\n\r\n@friend(Target)\r\nclass Helper:\r\n def access_target(self, target):\r\n # Friend can access protected members\r\n return target._protected_method()\r\n\r\n# Protected inheritance preserves friend relationships\r\n@protected(Target)\r\nclass Derived(Target):\r\n def internal_operation(self):\r\n return self._protected_method() # Works internally\r\n\r\nhelper = Helper()\r\nderived_obj = Derived()\r\n\r\n# Friend access works even with inheritance\r\nresult = helper.access_target(derived_obj) # Works - friend relationship preserved\r\n\r\n# Regular external access blocked\r\n# derived_obj._protected_method() # PermissionDeniedError - protected access\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Implicit Access Control</strong></summary>## Implicit Access Control\r\n\r\nLimen provides automatic access level detection based on Python naming conventions. When inheritance decorators are applied, methods are automatically wrapped with appropriate access control based on their names.\r\n\r\n### Naming Convention Rules\r\n\r\n- **Normal names** (e.g., `method_name`) \u2192 `@public`\r\n- **Single underscore prefix** (e.g., `_method_name`) \u2192 `@protected`\r\n- **Double underscore prefix** (e.g., `__method_name`) \u2192 `@private`\r\n\r\n### Automatic Application with Inheritance Decorators\r\n\r\nWhen you use inheritance decorators like `@protected(BaseClass)`, implicit access control is automatically applied to both the base class and derived class:\r\n\r\n```python\r\nclass Base:\r\n def public_method(self): # Automatically treated as @public\r\n return \"public\"\r\n \r\n def _protected_method(self): # Automatically treated as @protected\r\n return \"protected\"\r\n \r\n def __private_method(self): # Automatically treated as @private\r\n return \"private\"\r\n \r\n @public # Explicit decorator overrides implicit\r\n def _explicitly_public(self):\r\n return \"explicit public\"\r\n\r\n# Inheritance decorator applies implicit access control to Base\r\n@protected(Base)\r\nclass Derived(Base):\r\n def test_access(self):\r\n # Can access all inherited methods internally\r\n public_data = self.public_method() # Inherited public method\r\n protected_data = self._protected_method() # Inherited protected method\r\n explicit_data = self._explicitly_public() # Explicit override\r\n return f\"{public_data}, {protected_data}, {explicit_data}\"\r\n\r\nobj = Derived()\r\n\r\n# Internal access works\r\nresult = obj.test_access() # Works - internal access\r\n\r\n# External access controlled by inheritance rules\r\n# Protected inheritance converts public methods to protected\r\nobj.public_method() # PermissionDeniedError - public became protected\r\nobj._protected_method() # PermissionDeniedError - protected method\r\nobj._explicitly_public() # PermissionDeniedError - explicit public became protected\r\n```\r\n\r\n### Manual Application\r\n\r\nYou can also manually apply implicit access control without inheritance:\r\n\r\n```python\r\nfrom limen.utils.implicit import apply_implicit_access_control\r\n\r\nclass Base:\r\n def public_method(self):\r\n return \"public\"\r\n \r\n def _protected_method(self):\r\n return \"protected\"\r\n \r\n def __private_method(self):\r\n return \"private\"\r\n\r\n# Manually apply implicit access control\r\napply_implicit_access_control(Base)\r\n\r\nobj = Base()\r\nobj.public_method() # Works - public access\r\n# obj._protected_method() # PermissionDeniedError - protected access\r\n# obj.__private_method() # PermissionDeniedError - private access (name mangled)\r\n```\r\n\r\n### Explicit Override of Implicit Rules\r\n\r\nExplicit decorators always take precedence over implicit naming conventions:\r\n\r\n```python\r\nclass Base:\r\n @private # Explicit private\r\n def normal_name_but_private(self): # Normal name, but explicitly private\r\n return \"private despite normal name\"\r\n \r\n @public # Explicit public\r\n def _underscore_but_public(self): # Underscore name, but explicitly public\r\n return \"public despite underscore\"\r\n\r\n@protected(Base) # Apply implicit control\r\nclass Derived(Base):\r\n pass\r\n\r\nobj = Derived()\r\n\r\n# Explicit decorators override naming conventions\r\n# obj.normal_name_but_private() # PermissionDeniedError - explicitly private\r\nobj._underscore_but_public() # PermissionDeniedError - but protected inheritance affects it\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Friend Relationships</strong></summary>## Friend Relationships\r\n\r\nFriend classes and functions can access private and protected members of target classes, providing controlled access across class boundaries.\r\n\r\n### Friend Classes\r\n\r\nFriend classes can access private and protected members of the target class.\r\n\r\n```python\r\nclass Target:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n \r\n @protected\r\n def _protected_method(self):\r\n return \"protected\"\r\n\r\n@friend(Target)\r\nclass FriendA:\r\n def access_target(self, target):\r\n # Friend can access private methods\r\n private_data = target._private_method()\r\n protected_data = target._protected_method()\r\n return f\"{private_data}, {protected_data}\"\r\n\r\n@friend(Target)\r\nclass FriendB:\r\n def inspect_target(self, target):\r\n # Multiple classes can be friends\r\n return target._protected_method()\r\n\r\n# Usage\r\ntarget = Target()\r\nfriend_a = FriendA()\r\nfriend_b = FriendB()\r\n\r\nfriend_a.access_target(target) # Friend access works\r\nfriend_b.inspect_target(target) # Multiple friends work\r\n\r\n# Regular class cannot access private members\r\nclass Regular:\r\n def try_access(self, target):\r\n # PermissionDeniedError - not a friend\r\n return target._protected_method()\r\n```\r\n\r\n### Friend Functions\r\n\r\nFriend functions are standalone functions that can access private and protected members.\r\n\r\n```python\r\nclass Target:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n \r\n @protected\r\n def _protected_method(self):\r\n return \"protected\"\r\n\r\n@friend(Target)\r\ndef friend_function_a(target):\r\n \"\"\"Friend function for processing\"\"\"\r\n private_data = target._private_method()\r\n return f\"Processed: {private_data}\"\r\n\r\n@friend(Target)\r\ndef friend_function_b(target):\r\n \"\"\"Friend function for analysis\"\"\"\r\n protected_data = target._protected_method()\r\n return f\"Analyzed: {protected_data}\"\r\n\r\ndef regular_function(target):\r\n \"\"\"Regular function - no friend access\"\"\"\r\n # PermissionDeniedError - cannot access private methods\r\n return target._private_method()\r\n\r\n# Usage\r\ntarget = Target()\r\n\r\nfriend_function_a(target) # Friend function works\r\nfriend_function_b(target) # Another friend function works\r\n# regular_function(target) # PermissionDeniedError\r\n```\r\n\r\n### Friend Descriptors\r\n\r\nFriend relationships work with all Python descriptor types: staticmethod, classmethod, and property.\r\n\r\n```python\r\nclass Target:\r\n def __init__(self, value):\r\n self._value = value\r\n \r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n \r\n @private\r\n @property\r\n def private_property(self):\r\n return self._value\r\n\r\nclass Helper:\r\n # Friend staticmethod\r\n @friend(Target)\r\n @staticmethod\r\n def static_helper(target):\r\n return target._private_method()\r\n \r\n # Friend classmethod\r\n @friend(Target)\r\n @classmethod\r\n def class_helper(cls, target):\r\n return target._private_method()\r\n \r\n # Friend instance method accessing property\r\n @friend(Target)\r\n def access_property(self, target):\r\n return target.private_property\r\n\r\ntarget = Target(\"secret\")\r\nresult1 = Helper.static_helper(target) # Works\r\nresult2 = Helper.class_helper(target) # Works\r\nhelper = Helper()\r\nresult3 = helper.access_property(target) # Works\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Dual-Layer Security: Access Modifiers on Friend Methods</strong></summary>## Dual-Layer Security: Access Modifiers on Friend Methods\r\n\r\n**Advanced Feature**: Apply access modifiers to friend methods themselves for fine-grained control.\r\n\r\n```python\r\nclass Target:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n\r\nclass Helper:\r\n # Public friend method - anyone can call it\r\n @public\r\n @friend(Target)\r\n def public_access(self, target):\r\n return target._private_method()\r\n \r\n # Protected friend method - only inheritance can use it\r\n @protected\r\n @friend(Target)\r\n def protected_access(self, target):\r\n return target._private_method()\r\n \r\n # Private friend method - only internal use\r\n @private\r\n @friend(Target)\r\n def private_access(self, target):\r\n return target._private_method()\r\n \r\n def internal_operation(self, target):\r\n # Can use private friend method internally\r\n return self.private_access(target)\r\n\r\nclass DerivedHelper(Helper):\r\n def inherited_operation(self, target):\r\n # Can use protected friend method via inheritance\r\n return self.protected_access(target)\r\n\r\n# Usage\r\ntarget = Target()\r\nhelper = Helper()\r\nderived = DerivedHelper()\r\n\r\n# Public friend method works for everyone\r\nhelper.public_access(target)\r\n\r\n# Protected friend method works via inheritance\r\nderived.inherited_operation(target)\r\n\r\n# Private friend method works via internal access\r\nhelper.internal_operation(target)\r\n\r\n# Direct access to protected/private friend methods blocked\r\n# helper.protected_access(target) # PermissionDeniedError\r\n# helper.private_access(target) # PermissionDeniedError\r\n```\r\n\r\n### Staticmethod and Classmethod with Access Modifiers\r\n\r\n```python\r\nclass Target:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n\r\nclass Helper:\r\n # Protected friend staticmethod\r\n @protected\r\n @friend(Target)\r\n @staticmethod\r\n def protected_static_helper(target):\r\n return target._private_method()\r\n \r\n # Private friend classmethod\r\n @private\r\n @friend(Target)\r\n @classmethod\r\n def private_class_helper(cls, target):\r\n return target._private_method()\r\n \r\n @classmethod\r\n def internal_class_operation(cls, target):\r\n # Can use private classmethod internally\r\n return cls.private_class_helper(target)\r\n\r\nclass DerivedHelper(Helper):\r\n @classmethod\r\n def use_protected_static(cls, target):\r\n # Can use protected staticmethod via inheritance\r\n return cls.protected_static_helper(target)\r\n\r\ntarget = Target()\r\nhelper = Helper()\r\nderived = DerivedHelper()\r\n\r\n# Protected staticmethod works via inheritance\r\nderived.use_protected_static(target)\r\n\r\n# Private classmethod works via internal access\r\nhelper.internal_class_operation(target)\r\n\r\n# Direct access blocked\r\n# Helper.protected_static_helper(target) # PermissionDeniedError\r\n# Helper.private_class_helper(target) # PermissionDeniedError\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Security Features</strong></summary>## Security Features\r\n\r\n### Name Mangling Bypass Prevention\r\n\r\n**Critical Security Feature**: Limen prevents bypassing access control through Python's name mangling mechanism using multiple protection layers.\r\n\r\nPython automatically converts private methods like `__private_method` to `_ClassName__private_method`. Without protection, external code could bypass access control by directly accessing the mangled name:\r\n\r\n#### Protection for Implicit Private Methods\r\n\r\n```python\r\nclass SecureClass:\r\n def __private_method(self):\r\n return \"secret data\"\r\n \r\n def public_access(self):\r\n return self.__private_method() # Legitimate internal access\r\n\r\n# Apply implicit access control (detects __ methods as private)\r\nfrom limen.utils.implicit import apply_implicit_access_control\r\napply_implicit_access_control(SecureClass)\r\n\r\nobj = SecureClass()\r\n\r\n# Internal access works\r\nresult = obj.public_access() # \"secret data\"\r\n\r\n# Direct access blocked (AttributeError)\r\n# obj.__private_method() # AttributeError: no attribute '__private_method'\r\n\r\n# Name mangling bypass blocked (PermissionDeniedError) \r\n# obj._SecureClass__private_method() # PermissionDeniedError: Access denied to private method\r\n```\r\n\r\n#### Protection for Explicit @private Decorators\r\n\r\nExplicit `@private` decorators also prevent name mangling bypasses through descriptor-level access control:\r\n\r\n```python\r\nfrom limen import private\r\n\r\nclass SecureClass:\r\n @private\r\n def __private_method(self):\r\n return \"secret data\"\r\n \r\n @private \r\n def regular_private(self):\r\n return \"also secret\"\r\n \r\n def public_access(self):\r\n return f\"{self.__private_method()}, {self.regular_private()}\"\r\n\r\nobj = SecureClass()\r\n\r\n# Internal access works\r\nresult = obj.public_access() # \"secret data, also secret\"\r\n\r\n# Direct access blocked (PermissionDeniedError)\r\n# obj.regular_private() # PermissionDeniedError: Access denied to private method\r\n\r\n# Name mangling bypass blocked (PermissionDeniedError) \r\n# obj._SecureClass__private_method() # PermissionDeniedError: Access denied to private method\r\n\r\n# Manual mangling attempts fail (AttributeError)\r\n# obj._SecureClass__regular_private() # AttributeError: no such attribute\r\n```\r\n\r\n**How It Works:**\r\n- **Explicit decorators**: Descriptor-level access control validates every method call regardless of access path\r\n- **Implicit detection**: Custom `__getattribute__` protection intercepts mangled name access for `__` methods \r\n- **Dual protection**: Methods can be protected by both mechanisms simultaneously\r\n- **Friend preservation**: Authorized friends can still access via any legitimate method\r\n\r\n**Friend Access Still Works:**\r\n```python\r\nclass DataStore:\r\n def __private_data(self):\r\n return \"sensitive\"\r\n \r\n @private\r\n def __explicit_private(self):\r\n return \"explicit sensitive\"\r\n\r\n@friend(DataStore)\r\nclass AuthorizedProcessor:\r\n def process(self, store):\r\n # Friend can access via mangled name when authorized (both types)\r\n implicit = store._DataStore__private_data()\r\n explicit = store._DataStore__explicit_private()\r\n return f\"{implicit}, {explicit}\"\r\n\r\napply_implicit_access_control(DataStore)\r\n\r\nstore = DataStore()\r\nprocessor = AuthorizedProcessor()\r\nresult = processor.process(store) # Works - friend access allowed\r\n\r\n# Unauthorized access still blocked for both\r\nclass UnauthorizedClass:\r\n def hack(self, store):\r\n return store._DataStore__private_data() # PermissionDeniedError\r\n\r\nunauthorized = UnauthorizedClass()\r\n# unauthorized.hack(store) # PermissionDeniedError: Access denied\r\n```\r\n\r\nThis security feature ensures that Limen's access control cannot be circumvented through Python's name mangling, providing true encapsulation and security for your private methods.\r\n\r\n\\n</details><details>\\n<summary><strong>Property Access Control</strong></summary>## Property Access Control\r\n\r\nControl getter and setter access independently with sophisticated property decorators.\r\n\r\n### Basic Property Control\r\n\r\n```python\r\nclass Base:\r\n def __init__(self, name, value):\r\n self._name = name\r\n self._value = value\r\n \r\n @protected\r\n @property\r\n def value(self):\r\n \"\"\"Protected getter - accessible in inheritance\"\"\"\r\n return self._value\r\n \r\n @value.setter\r\n @private\r\n def value(self, new_value):\r\n \"\"\"Private setter - only same class\"\"\"\r\n if new_value > 0:\r\n self._value = new_value\r\n \r\n def update_value(self, amount):\r\n # Private setter works within same class\r\n self.value = self._value + amount\r\n \r\n @public\r\n @property\r\n def name(self):\r\n \"\"\"Public getter\"\"\"\r\n return self._name\r\n\r\nclass Derived(Base):\r\n def check_value(self):\r\n # Can read value (protected getter)\r\n return f\"Value: {self.value}\"\r\n \r\n def try_modify_value(self, new_value):\r\n # Cannot use private setter\r\n # self.value = new_value # PermissionDeniedError\r\n pass\r\n\r\nobj1 = Base(\"item1\", 100)\r\nobj2 = Derived(\"item2\", 200)\r\n\r\n# Public property access\r\nprint(obj1.name)\r\n\r\n# Protected property access via inheritance\r\nprint(obj2.check_value())\r\n\r\n# Internal value modification\r\nobj1.update_value(50)\r\n\r\n# External access to protected property\r\n# print(obj1.value) # PermissionDeniedError\r\n```\r\n\r\n### Friend Access to Properties\r\n\r\n```python\r\nclass Target:\r\n def __init__(self, value):\r\n self._value = value\r\n \r\n @private\r\n @property\r\n def private_property(self):\r\n return self._value\r\n\r\n@friend(Target)\r\nclass Friend:\r\n def access_property(self, target):\r\n # Friend can access private property\r\n value = target.private_property\r\n return f\"Accessed: {value}\"\r\n\r\ntarget = Target(\"secret\")\r\nfriend = Friend()\r\nresult = friend.access_property(target) # Works\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>System Management</strong></summary>## System Management\r\n\r\n### Runtime Control\r\n\r\n```python\r\nfrom limen import (\r\n enable_enforcement, \r\n disable_enforcement, \r\n is_enforcement_enabled,\r\n get_access_control_system\r\n)\r\n\r\nclass Base:\r\n @private\r\n def _private_method(self):\r\n return \"private\"\r\n\r\nobj = Base()\r\n\r\n# Normal enforcement\r\ntry:\r\n obj._private_method() # PermissionDeniedError\r\nexcept PermissionDeniedError:\r\n print(\"Access blocked\")\r\n\r\n# Disable enforcement (useful for testing)\r\ndisable_enforcement()\r\nresult = obj._private_method() # Now works\r\nprint(f\"Access allowed: {result}\")\r\n\r\n# Re-enable enforcement\r\nenable_enforcement()\r\n# obj.secret_method() # PermissionDeniedError again\r\n\r\n# Check enforcement status\r\nprint(f\"Enforcement enabled: {is_enforcement_enabled()}\")\r\n```\r\n\r\n### System Metrics and Debugging\r\n\r\n```python\r\nfrom limen.system import get_access_control_system\r\n\r\n# Get system instance for advanced operations\r\naccess_control = get_access_control_system()\r\n\r\n# Check enforcement status\r\nprint(f\"Enforcement enabled: {access_control.enforcement_enabled}\")\r\n\r\n# Get friendship relationships count\r\nfriendship_manager = access_control._friendship_manager\r\nprint(f\"Total friend relationships: {friendship_manager.get_friends_count()}\")\r\nprint(f\"Classes with friends: {friendship_manager.get_relationships_count()}\")\r\n\r\n# Reset system state (useful for testing)\r\nfrom limen import reset_system\r\nreset_system()\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Error Handling</strong></summary>## Error Handling\r\n\r\nLimen provides comprehensive, contextual exception types with enhanced error messages that include actual code suggestions and detailed explanations.\r\n\r\n### Exception Types\r\n\r\n```python\r\nfrom limen.exceptions import (\r\n LimenError, # Base exception for all Limen errors\r\n PermissionDeniedError, # Access denied to private/protected members\r\n DecoratorConflictError, # Conflicting access level decorators\r\n DecoratorUsageError, # Incorrect decorator usage\r\n)\r\n```\r\n\r\n#### LimenError\r\nBase exception class for all Limen access control errors. All other Limen exceptions inherit from this.\r\n\r\n#### PermissionDeniedError\r\nRaised when attempting to access private or protected members from unauthorized contexts.\r\n\r\n```python\r\nclass SecureClass:\r\n @private\r\n def secret_method(self):\r\n return \"secret\"\r\n\r\ntry:\r\n obj = SecureClass()\r\n obj.secret_method() # Unauthorized access\r\nexcept PermissionDeniedError as e:\r\n print(f\"Access denied: {e}\")\r\n # Output: Access denied to private method secret_method\r\n```\r\n\r\n#### DecoratorConflictError\r\nRaised when conflicting access level decorators are applied to the same method. Provides enhanced error messages with actual code suggestions and function body extraction.\r\n\r\n```python\r\n# This will raise an error during class creation\r\ntry:\r\n class ConflictClass:\r\n @private\r\n @protected # Conflicting access levels\r\n def conflicted_method(self, data: str) -> str:\r\n return f\"processing {data}\"\r\nexcept DecoratorConflictError as e:\r\n print(f\"Decorator conflict: {e}\")\r\n # Enhanced output shows:\r\n # Conflicting access level decorators on conflicted_method(): \r\n # already has @private, cannot apply @protected.\r\n # Did you mean:\r\n # @protected\r\n # def conflicted_method(self, data: str) -> str:\r\n # return f\"processing {data}\"\r\n # ?\r\n```\r\n\r\n#### DecoratorUsageError \r\nRaised when decorators are used incorrectly (wrong context, invalid syntax, etc.). Provides contextual suggestions based on the specific misuse.\r\n\r\n```python\r\n# Invalid decorator usage examples:\r\n\r\n# 1. Module-level function (not allowed)\r\ntry:\r\n @private # Cannot use on module-level function\r\n def module_function():\r\n pass\r\nexcept DecoratorUsageError as e:\r\n print(f\"Invalid usage: {e}\")\r\n # Output: @private cannot be applied to module-level functions. \r\n # Access control decorators can only be used on class methods.\r\n # Did you mean to put this function inside a class?\r\n\r\n# 2. Bare class decoration (missing inheritance target)\r\ntry:\r\n @private # Missing class argument\r\n class MyClass:\r\n pass\r\nexcept DecoratorUsageError as e:\r\n print(f\"Invalid usage: {e}\")\r\n # Output: @private cannot be applied to a class without specifying a class to inherit from.\r\n # Did you mean: @private(BaseClass) ?\r\n\r\n# 3. Duplicate decorator application\r\ntry:\r\n class DuplicateClass:\r\n @private\r\n @private # Applied twice\r\n def duplicate_method(self):\r\n return \"data\"\r\nexcept DecoratorConflictError as e:\r\n print(f\"Duplicate decorator: {e}\")\r\n # Output: @private was applied to duplicate_method() more than once!\r\n # Did you mean:\r\n # @private\r\n # def duplicate_method(self):\r\n # return \"data\"\r\n # ?\r\n```\r\n\r\n### Enhanced Error Messages\r\n\r\nLimen's error system provides contextual, helpful error messages that include:\r\n\r\n- **Actual function signatures** with type annotations\r\n- **Real function body content** (not just \"pass\")\r\n- **Specific suggestions** for fixing the error\r\n- **Contextual help** based on the type of mistake\r\n\r\n```python\r\n# Example with complex method signature\r\nclass ExampleClass:\r\n @property\r\n @private\r\n @protected # Conflict error\r\n def complex_property(self) -> Dict[str, int]:\r\n return {\"count\": 42, \"status\": 1}\r\n\r\n# Error message will show:\r\n# Conflicting access level decorators on complex_property: \r\n# already has @private, cannot apply @protected.\r\n# Did you mean:\r\n# @property\r\n# @protected \r\n# def complex_property(self) -> Dict[str, int]:\r\n# return {\"count\": 42, \"status\": 1}\r\n# ?\r\n```\r\n\r\n### Property vs Method Formatting\r\n\r\nError messages intelligently format target names based on the member type:\r\n- **Methods**: Show with parentheses `method_name()`\r\n- **Properties**: Show without parentheses `property_name`\r\n\r\n```python\r\n# Property error (no parentheses)\r\nclass MyClass:\r\n @property\r\n @private\r\n @private # Duplicate\r\n def my_prop(self):\r\n return \"value\"\r\n# Error: @private was applied to my_prop more than once!\r\n\r\n# Method error (with parentheses) \r\nclass MyClass:\r\n @private\r\n @private # Duplicate\r\n def my_method(self):\r\n return \"value\"\r\n# Error: @private was applied to my_method() more than once!\r\n```\r\n\r\n### Error System Architecture\r\n\r\nLimen's error system is built with a modular, maintainable architecture:\r\n\r\n- **`method_utils.py`**: Method introspection utilities\r\n - `MethodInspector`: Extracts method types, arguments, and decorators with type hints\r\n - `FunctionBodyExtractor`: Extracts actual function implementation code \r\n - `TargetFormatter`: Formats method names appropriately (with/without parentheses)\r\n\r\n- **`message_generators.py`**: Contextual message generation\r\n - `MessageGenerator`: Creates detailed, helpful error messages with code suggestions\r\n - Handles different error scenarios with specific, actionable advice\r\n\r\n- **`limen_errors.py`**: Clean, focused exception classes\r\n - Each exception focuses on its core responsibility\r\n - Uses composition for shared functionality\r\n - Easy to extend and maintain\r\n\r\nThis modular design ensures that error messages are consistent, helpful, and maintainable as the system grows.\r\n\r\n\\n</details><details>\\n<summary><strong>Testing and Development</strong></summary>## Testing and Development\r\n\r\n### Testing with Enforcement Control\r\n\r\n```python\r\nimport unittest\r\nfrom limen import disable_enforcement, enable_enforcement\r\n\r\nclass TestSecureClass(unittest.TestCase):\r\n def setUp(self):\r\n # Disable enforcement for easier testing\r\n disable_enforcement()\r\n \r\n def tearDown(self):\r\n # Re-enable enforcement\r\n enable_enforcement()\r\n \r\n def test_private_method_access(self):\r\n class TestClass:\r\n @private\r\n def _secret(self):\r\n return \"secret\"\r\n \r\n obj = TestClass()\r\n # This works because enforcement is disabled\r\n result = obj._secret()\r\n self.assertEqual(result, \"secret\")\r\n\r\n# Or use context manager approach\r\nfrom limen.system import get_access_control_system\r\n\r\ndef test_with_disabled_enforcement():\r\n access_control = get_access_control_system()\r\n original_state = access_control.enforcement_enabled\r\n \r\n try:\r\n access_control.enforcement_enabled = False\r\n # Test code here with enforcement disabled\r\n pass\r\n finally:\r\n access_control.enforcement_enabled = original_state\r\n```\r\n\r\n### Debugging Friend Relationships\r\n\r\n```python\r\nfrom limen.system import get_access_control_system\r\n\r\nclass TargetClass:\r\n @private\r\n def secret(self):\r\n return \"secret\"\r\n\r\n@friend(TargetClass)\r\nclass FriendClass:\r\n pass\r\n\r\n# Debug friendship relationships\r\naccess_control = get_access_control_system()\r\nfriendship_manager = access_control._friendship_manager\r\n\r\nprint(f\"Total friends: {friendship_manager.get_friends_count()}\")\r\nprint(f\"Classes with friends: {friendship_manager.get_relationships_count()}\")\r\n\r\n# Check if specific friendship exists\r\nis_friend = friendship_manager.is_friend(TargetClass, FriendClass)\r\nprint(f\"FriendClass is friend of TargetClass: {is_friend}\")\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Requirements</strong></summary>## Requirements\r\n\r\n- **Python 3.12+**\r\n- **No external dependencies** for core functionality\r\n- **Optional development dependencies** for testing and development\r\n\r\n\\n</details><details>\\n<summary><strong>Development Setup</strong></summary>## Development Setup\r\n\r\n### Clone and Setup\r\n\r\n```bash\r\ngit clone https://github.com/Ma1achy/Limen.git\r\ncd Limen\r\npip install -e .[dev]\r\n```\r\n\r\n### Run Tests\r\n\r\n```bash\r\n# Run all tests\r\npytest\r\n\r\n# Run with coverage\r\npytest --cov=limen\r\n\r\n# Run specific test categories\r\npytest -m access_control # Access control tests\r\npytest -m friend_methods # Friend relationship tests\r\npytest -m inheritance # Inheritance tests\r\npytest -m edge_cases # Edge cases and boundary tests\r\n```\r\n\r\n### Development Commands\r\n\r\n```bash\r\n# Format code\r\nblack limen/ tests/\r\n\r\n# Type checking\r\nmypy limen/\r\n\r\n# Lint code\r\nflake8 limen/ tests/\r\n```\r\n\r\n\\n</details><details>\\n<summary><strong>Contributing</strong></summary>## Contributing\r\n\r\nContributions are welcome! Please feel free to submit pull requests, report bugs, or suggest features.\r\n\r\n### Development Guidelines\r\n\r\n1. **Add tests** for new features\r\n2. **Update documentation** for API changes\r\n3. **Follow code style** (Black formatting, type hints)\r\n4. **Ensure compatibility** with Python 3.8+\r\n\r\n\\n</details><details>\\n<summary><strong>License</strong></summary>## LicenseMIT License - see [LICENSE](LICENSE) file for details.</details>\r\n",
"bugtrack_url": null,
"license": null,
"summary": "C++ style access control for Python classes with friend relationships and inheritance control",
"version": "1.1.0",
"project_urls": {
"Bug Reports": "https://github.com/Ma1achy/Limen/issues",
"Documentation": "https://github.com/Ma1achy/Limen#readme",
"Homepage": "https://github.com/Ma1achy/Limen",
"Source": "https://github.com/Ma1achy/Limen"
},
"split_keywords": [
"access-control",
" private",
" protected",
" public",
" friend",
" decorators",
" oop",
" security",
" inheritance",
" encapsulation",
" cpp-style"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f6b24538f793652b2a2ad2b572a83ab0b7a16b60614f3009d5fc4cd983bef0f7",
"md5": "363ab72a41794b312329f2a3096b8ec5",
"sha256": "cda1bffb3814b06f7f02595c6805552e87b917b934ef1b5795dc086849bea06b"
},
"downloads": -1,
"filename": "pylimen-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "363ab72a41794b312329f2a3096b8ec5",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 51946,
"upload_time": "2025-08-13T18:32:06",
"upload_time_iso_8601": "2025-08-13T18:32:06.290076Z",
"url": "https://files.pythonhosted.org/packages/f6/b2/4538f793652b2a2ad2b572a83ab0b7a16b60614f3009d5fc4cd983bef0f7/pylimen-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "02e8273f96ac3abab40299194dc203262640cc89a95b3414de82c293ddcc45c6",
"md5": "da27a902f799fbb7c888bee08210d95d",
"sha256": "d10df23b0bfbc504e3f5872050f778f9495174ddfca4fcf18335a46229693a39"
},
"downloads": -1,
"filename": "pylimen-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "da27a902f799fbb7c888bee08210d95d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 85839,
"upload_time": "2025-08-13T18:32:07",
"upload_time_iso_8601": "2025-08-13T18:32:07.341414Z",
"url": "https://files.pythonhosted.org/packages/02/e8/273f96ac3abab40299194dc203262640cc89a95b3414de82c293ddcc45c6/pylimen-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-13 18:32:07",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Ma1achy",
"github_project": "Limen",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pylimen"
}