Module

Accretive modules have an interface nearly equivalent to types.ModuleType, i.e., standard Python modules. However, once an attribute has been assigned, it cannot be reassigned or deleted. This protects accretive modules from accidental (or unsophisticated malicious) tampering.

>>> from accretive import Module

Initialization

While modules are typically initialized during import of their sources, they may also be created dynamically. As with standard Python modules, a name is required when dynamically creating a module.

>>> m = Module( 'foo' )
>>> m
<module 'foo'>

Immutability

Existing attributes cannot be reassigned.

>>> m.__name__
'foo'
>>> m.__name__ = 'bar'
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute '__name__'.

Or deleted.

>>> del m.__name__
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute '__name__'.

Attribute Assignment

However, new attributes can be assigned.

>>> m.__version__ = '1.0a3'
>>> vars( m )
{'__name__': 'foo', '__doc__': None, '__package__': None, '__loader__': None, '__spec__': None, '__version__': '1.0a3'}

Reclassification

Existing non-accretive modules can be reclassified as accretive modules. For application developers, this can provide an extra layer of safety around some modules. (However, library developers should never reclassify any modules except those in the packages that they distribute. Reclassifying other modules that are used by applications could break contracts.)

Consider the following scenario:

>>> import getpass
>>> true_getpass = getpass.getpass
>>> import functools
>>> @functools.wraps( true_getpass )
... def pwned_getpass( prompt = 'Password: ', stream = None ):
...     password = true_getpass( prompt = prompt, stream = stream )
...     # Send intercepted password, stack trace, and host details to malicious collector.
...     return password
...
>>> getpass.getpass = pwned_getpass

In the above scenario, a “trusted” standard library module has been monkey-patched to provide a compromised function. We can prevent unsophisticated monkey-patching by reclassifying the module.

>>> getpass.getpass = true_getpass
>>> getpass.__class__ = Module
>>> getpass.getpass = pwned_getpass
Traceback (most recent call last):
...
accretive.exceptions.AttributeImmutabilityError: Cannot reassign or delete existing attribute 'getpass'.

Warning

We are unable to prevent tampering of a module’s underlying attributes dictionary, __dict__. So, accretive modules do not provide any true resistance against determined tampering. They do provide security against accidental alterations of attributes though.

Mass Reclassification

For cases where multiple modules should be reclassified, a convenience function is provided. This function looks for all modules in a dictionary, such as the attributes dictionary for another module, and reclassifies the modules to accretive modules.

from accretive import reclassify_modules
reclassify_modules( globals( ) )