Skip to main content

C++ style access control for Python classes with friend relationships and inheritance control

Project description

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

Installation

From PyPI (Recommended)

pip install pylimen

From Source

git clone https://github.com/Ma1achy/Limen.git
cd Limen
pip install -e .

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.

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()  # PermissionError - private access

@protected - Inheritance Hierarchy

Protected methods are accessible within the same class and its subclasses.

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()  # PermissionError - external access blocked

@public - Universal Access

Public methods are accessible from anywhere (default Python behavior, useful for explicit documentation).

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:

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()  # PermissionError
        
        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()  # PermissionError - protected

Protected Inheritance

Protected inheritance converts public members to protected, following C++ semantics:

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()        # PermissionError
        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()          # PermissionError - public became protected
# obj._protected_method()      # PermissionError - protected remains protected

Private Inheritance

Private inheritance makes all inherited members private to the derived class:

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()                 # PermissionError - public became private
# obj._protected_method()             # PermissionError - 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:

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()                      # PermissionError - protected inheritance
# obj.method_b()                      # PermissionError - private inheritance

Friend Relationships with Inheritance

Friend relationships are preserved across inheritance patterns:

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()           # PermissionError - protected access

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:

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()         # PermissionError - public became protected
obj._protected_method()     # PermissionError - protected method
obj._explicitly_public()   # PermissionError - explicit public became protected

Manual Application

You can also manually apply implicit access control without inheritance:

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()  # PermissionError - protected access
# obj.__private_method()   # PermissionError - private access (name mangled)

Explicit Override of Implicit Rules

Explicit decorators always take precedence over implicit naming conventions:

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()    # PermissionError - explicitly private
obj._underscore_but_public()       # PermissionError - but protected inheritance affects it

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.

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):
        # PermissionError - not a friend
        return target._protected_method()

Friend Functions

Friend functions are standalone functions that can access private and protected members.

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"""
    # PermissionError - 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)  # PermissionError

Friend Descriptors

Friend relationships work with all Python descriptor types: staticmethod, classmethod, and property.

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

Dual-Layer Security: Access Modifiers on Friend Methods

Advanced Feature: Apply access modifiers to friend methods themselves for fine-grained control.

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)  # PermissionError
# helper.private_access(target)    # PermissionError

Staticmethod and Classmethod with Access Modifiers

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)  # PermissionError
# Helper.private_class_helper(target)     # PermissionError

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

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 (PermissionError)  
# obj._SecureClass__private_method()  # PermissionError: Access denied to private method

Protection for Explicit @private Decorators

Explicit @private decorators also prevent name mangling bypasses through descriptor-level access control:

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 (PermissionError)
# obj.regular_private()  # PermissionError: Access denied to private method

# Name mangling bypass blocked (PermissionError) 
# obj._SecureClass__private_method()  # PermissionError: 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:

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()  # PermissionError

unauthorized = UnauthorizedClass()
# unauthorized.hack(store)  # PermissionError: 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.

Property Access Control

Control getter and setter access independently with sophisticated property decorators.

Basic Property Control

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  # PermissionError
        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)  # PermissionError

Friend Access to Properties

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

System Management

Runtime Control

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()  # PermissionError
except PermissionError:
    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()  # PermissionError again

# Check enforcement status
print(f"Enforcement enabled: {is_enforcement_enabled()}")

System Metrics and Debugging

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()

Error Handling

Limen provides comprehensive, contextual exception types with enhanced error messages that include actual code suggestions and detailed explanations.

Exception Types

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.

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.

# 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.

# 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
# 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
# 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.

Testing and Development

Testing with Enforcement Control

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

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}")

Requirements

  • Python 3.12+
  • No external dependencies for core functionality
  • Optional development dependencies for testing and development

Development Setup

Clone and Setup

git clone https://github.com/Ma1achy/Limen.git
cd Limen
pip install -e .[dev]

Run Tests

# 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

# Format code
black limen/ tests/

# Type checking
mypy limen/

# Lint code
flake8 limen/ tests/

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+

License

MIT License - see LICENSE file for details.

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

pylimen-1.0.2.tar.gz (85.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pylimen-1.0.2-py3-none-any.whl (47.7 kB view details)

Uploaded Python 3

File details

Details for the file pylimen-1.0.2.tar.gz.

File metadata

  • Download URL: pylimen-1.0.2.tar.gz
  • Upload date:
  • Size: 85.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pylimen-1.0.2.tar.gz
Algorithm Hash digest
SHA256 eededc304a9dd5a09e350f8b744d9c04588b237943bbc1978dc0d5a4f0a25c70
MD5 8458d445a172807144d11a3cf5971239
BLAKE2b-256 fc4db0beaf5f8e2df7e1890493ad9b0811b9e28cf650e375e55d7d3cd4d26208

See more details on using hashes here.

File details

Details for the file pylimen-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: pylimen-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 47.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pylimen-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ce2d52a98a1164cd0419246adcd2e6afbefcc69ed05c8ee1208f9c5d388810ef
MD5 585b5074c6134e218859f1d78f29bbf6
BLAKE2b-256 4d0624751266d6be2d0975b8891cfa4d4f5103b6c65cf7710e9ea13010ebf00d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page