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.