Objects¶
Accretive objects allow for the creation of instances where attributes can be added but not modified or deleted after assignment. This behavior is valuable for configuration objects, immutable data containers, and scenarios where attribute stability is required.
>>> from accretive import Object, accretive
Object Class¶
The Object
base class provides accretive behavior when instantiated
directly:
>>> obj = Object( )
>>> obj.name = 'example'
>>> obj.name
'example'
or through inheritance:
>>> class CustomConfig( Object ):
... ''' A custom configuration with accretive attributes. '''
... pass
...
>>> custom = CustomConfig( )
>>> custom.setting = 'enabled'
Once attributes are set, they cannot be modified or deleted:
>>> obj.name = 'modified'
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'name'.
>>> del custom.setting
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'setting'.
New attributes can be added at any time:
>>> obj.version = '1.0'
>>> obj.version
'1.0'
Accretive Decorator¶
The accretive
decorator can apply accretive attribute behavior to any
class:
>>> @accretive
... class Config:
... def __init__( self, debug = False ):
... self.debug = debug
...
>>> config = Config( debug = True )
>>> config.debug
True
>>> config.verbose = True # Add new attribute
>>> config.verbose
True
As with the Object
class, attributes cannot be modified or deleted once
set:
>>> config.debug = False
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'debug'.
Accretive Decorator with Parameters¶
The accretive
decorator accepts optional parameters to customize behavior:
>>> # With mutable attributes
>>> @accretive( mutables = ( 'version', ) )
... class VersionedConfig:
... def __init__( self, name, version ):
... self.name = name
... self.version = version
...
>>> config = VersionedConfig( 'MyApp', '1.0.0' )
>>> config.name
'MyApp'
>>> config.version
'1.0.0'
Attributes specified in the mutables
parameter can be modified even after
initial assignment:
>>> # Modify mutable attribute
>>> config.version = '1.0.1' # This works fine
>>> config.version
'1.0.1'
>>>
>>> # Attempt to modify immutable attribute
>>> config.name = 'YourApp'
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'name'.
Mutable attributes can also be deleted:
>>> del config.version # This works with mutable attributes
>>> hasattr( config, 'version' )
False
Custom Docstrings¶
The accretive
decorator can set or override the docstring of the
decorated class:
>>> @accretive( docstring = 'A configuration class with custom documentation.' )
... class DocumentedConfig:
... '''Original docstring that will be replaced.'''
... def __init__( self, name ):
... self.name = name
...
>>> print( DocumentedConfig.__doc__ )
A configuration class with custom documentation.
Combining Parameters¶
Multiple parameters can be combined for more flexibility:
>>> @accretive(
... docstring = 'Advanced configuration with mutable settings.',
... mutables = ( 'debug', 'log_level' )
... )
... class AdvancedConfig:
... def __init__( self, name, debug = False, log_level = 'INFO' ):
... self.name = name
... self.debug = debug
... self.log_level = log_level
...
>>> advanced = AdvancedConfig( 'ServiceApp' )
>>>
>>> # Modify mutable attributes
>>> advanced.debug = True
>>> advanced.log_level = 'DEBUG'
>>>
>>> # Add new attribute
>>> advanced.timeout = 30
>>>
>>> # All attributes are accessible
>>> advanced.name, advanced.debug, advanced.log_level, advanced.timeout
('ServiceApp', True, 'DEBUG', 30)
>>>
>>> # Only immutable attributes raise errors on modification
>>> advanced.name = 'NewName'
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'name'.
Working with Data Classes¶
The accretive
decorator works well with Python’s data classes:
>>> from dataclasses import dataclass
>>>
>>> @accretive
... @dataclass
... class ServerConfig:
... host: str
... port: int = 8080
...
>>> server = ServerConfig( host = 'localhost' )
>>> server.host
'localhost'
>>> server.port
8080
>>> server.secure = True # Add new attribute
>>> server.host = '127.0.0.1' # Attempt to modify
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'host'.
Working with Slotted Classes¶
The accretive
decorator also works with classes that use __slots__
:
>>> @accretive
... class SlottedConfig:
... __slots__ = ( 'debug', '_behaviors_' )
...
... def __init__( self, debug = False ):
... self.debug = debug
...
>>> config = SlottedConfig( debug = True )
>>> config.debug
True
>>> config.debug = False
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'debug'.
Exception Classes¶
The accretive
decorator is particularly useful for creating exception
hierarchies with accretive properties:
>>> @accretive
... class CustomException( Exception ):
... ''' Base exception with accretive attributes. '''
... pass
...
>>> try:
... raise CustomException( 'Operation failed' )
... except CustomException as e:
... e.error_code = 500
... print( f"Error {e.error_code}: {e}" )
... try:
... e.error_code = 404 # Try to modify
... except Exception as modify_error:
... print( f"Modification failed: {type(modify_error).__name__}" )
Error 500: Operation failed
Modification failed: AttributeImmutabilityError
Multiple Inheritance Considerations¶
When using the Object
class with multiple inheritance, be aware of
potential layout conflicts with built-in types that have their own memory
layout:
>>> # This would raise a TypeError due to memory layout conflict
>>> # class InvalidCombination( BaseException, Object ):
>>> # pass
>>> # Instead, use the accretive decorator directly
>>> @accretive
... class ValidException( BaseException ):
... ''' An exception with accretive behavior. '''
... pass
...
>>> ex = ValidException( 'Something went wrong' )
>>> ex.context = 'Additional information'
>>> ex.context
'Additional information'
>>> # Cannot modify after setting
>>> ex.context = 'Changed information'
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'context'.
Warning
When working with built-in types (especially exception types) in multiple
inheritance hierarchies, avoid using the Object
base class which
uses __slots__
. Instead, apply the accretive
decorator directly to
your class.