Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/t184256/mutants
mutants, a Python library for objects that mutate on access
https://github.com/t184256/mutants
library limitation mutate mutate-on-access object-proxying proxy python python3
Last synced: about 2 months ago
JSON representation
mutants, a Python library for objects that mutate on access
- Host: GitHub
- URL: https://github.com/t184256/mutants
- Owner: t184256
- License: mit
- Created: 2016-09-19T09:53:11.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2016-09-26T09:58:08.000Z (over 8 years ago)
- Last Synced: 2024-10-11T23:35:52.043Z (3 months ago)
- Topics: library, limitation, mutate, mutate-on-access, object-proxying, proxy, python, python3
- Language: Python
- Size: 19.5 KB
- Stars: 2
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
mutants, a Python library for objects that mutate on access
===========================================================In short
--------
`mutants` allows to create Python objects that mutate on access.
It works by creating proxy objects
that change underlying objects on every access.Demo
----
```python
import randomimport mutants
n = mutants.OnAccessMutant(0, lambda n: n + 1)
print(n) # prints 1
print(n) # prints 2
print(n) # prints 3class Duck:
feathers = Truedef quack(self):
print('quack')class Wolf:
teeth = 'sharp'def quack(self):
print('no quack')def random_animal():
return random.choice([Duck(), Wolf()])randy = mutants.ImmutableMutant(random_animal)
randy.quack() # prints 'quack' or 'no quack'
randy.quack() # prints 'quack' or 'no quack'
print(hasattr(randy, 'feathers')) # prints 'True' or 'False'
randy.name = 'Randy'
print(hasattr(randy, 'name')) # prints 'False', see belowdef class_toggler(animal):
if isinstance(animal, Duck):
return Wolf
return Ducktracy = mutants.ClassHopperMutant(Duck(), class_toggler)
tracy.quack() # prints 'no quack' as it's a Wolf
tracy.quack() # prints 'quack' as it's a Duck
print(tracy.teeth) # prints 'sharp' as it's a Wolf
tracy.name = 'Tracy'
print(tracy.name) # prints 'Tracy'def class_extender(animal):
class SleepyAnimal(animal.__class__):
def quack(self):
super().quack()
print('zzz')
return SleepyAnimalzetta = mutants.ClassHopperMutant(Duck(), class_extender)
zetta.quack() # prints 'quack' and 'zzz'
```Details
-------
Depending on what you want, you can choose
one of two mutant kinds: ImmutableMutant and ClassyMutant
or make a custom OnAccessMutant.### OnAccessMutant
`OnAccessMutant` is the core class of the library.
It wraps an object much like `wrapt.ObjectProxy` does.
But there's a callback that is called before each access
and has the ability to modify or replace the proxied object.Usage: `OnAccessMutant(initial_object, callable_mutator)`
where: `callable_mutator(wrapped_object) -> new_wrapped_object`### ImmutableMutant
`ImmutableMutant` can impersonate different objects.
Its constructor takes a callable.
Before each access, this callable is called to provide an object
that `ImmutableMutant` will impersonate.Usage: `ImmutableMutant(callable_returning_objects_to_be_proxied)`
Modifying `ImmutableMutant` is probably a strange idea,
because it doesn't remember the objects that it impersonates
and the callable will probably return something else next time.In this pure Python implementation it's implemented as:
```python
def ImmutableMutant(mutator):
return OnAccessMutant(None, lambda _: mutator())
```
Future C extensions may implement it separately for performance benefits.### ClassHopperMutant
`ClassHopper` reevaluates the class of the wrapped object on every access.
It's like `obj.__class__ = callable_returning_a_class()`,
but magically happening before every manipulation with the object.Usage: `mutant.ClassHopper(initial_object, callable_returning_a_class)`
In this pure Python implementation it's implemented as:
```python
def ClassHopperMutant(initial_object, class_returning_callable, copy=True):from copy import copy as _copy
def class_mutator(obj):
obj.__class__ = class_returning_callable(obj)
return objif copy:
initial_object = _copy(initial_object)return OnAccessMutant(initial_object, class_mutator)
```
Future C extensions may implement it separately for performance benefits.More about mutants
------------------
`mutants` were born to serve the needs of another library, `hacks`,
that aids modifying object, function or class behaviour,
stacking such modifications
and switching `currently active modification stacks' easily on the fly.
Check it out: https://github.com/t184256/hacks`mutants` is similar to `wrapt.ObjectProxy` or `lazy-object-proxy`
but with bugs and flexibility instead of laziness, caching and performance.
CPython/Python guys, please give us something cleaner to pull off our tricks!`mutants` is currently in alpha state,
so send in pull requests if something is broken!License
-------
`mutants` is distributed under the terms of the MIT License;
see [LICENSE.txt](LICENSE.txt).