Saving users from debug-hell by conveniently keeping track of mutations in objects and logging them
Project description
Project description
Stay out of debug hell by letting the mutationtracker do the dirty work for you! It will ease up the process
of identifying where an object took a wrong turn.
Just don’t tell your employer, so they won’t fire half your colleagues due to the time saved on debugging.
Features
- Hold all mutations in the
MutationLedgerin theMUTATIONSattribute of any child class ofMutationTrackedObjector class decorated bytrack_mutations(includes full trace stack) - Log creation and mutations of object with log callable of your choice (e.g.
print,logger.debug,logger.info) - If a
MutationTrackedObjectis set as attribute value to anotherMutationTrackedObject, the caller function of the 'filler' is saved in thefilled_byattribute of theMutationLedger
Table of contents
- Project description
- How to install & use
- Example 1: MutationTrackedObject base class
- Example 2: track_mutations decorator
- Limitations
- Release notes
- Useful links
- License
How to install & use
Install this package easily using pip:
pip install mutationtracker
This project aims to be very easy to use. Import either the MutationTrackedObject or track_mutations decorator in
your Python module. Next, define your custom class of which instances should be tracked, and optionally add a log
callable to it:
from mutationtracker import MutationTrackedObject
class YourClass(MutationTrackedObject):
_mutations_log_function = print # optional attribute; might be other callable; default is no logging
def __init__(self):
...
or
from mutationtracker import track_mutations
@track_mutations(log_function=print) # optional keyword argument; might be other callable; default is no logging
class YourClass:
def __init__(self):
...
Your class will automatically contain the MUTATIONS attribute holding the full mutation history of the instance.
For an extensive enjoyable example, try code below.
Example 1: MutationTrackedObject base class
Code
This example is best shown using two files in a single directory: alice_and_bob.py and run.py. The latter is not
mandatory, but will improve the result because it not ran as script. Let's create alice_and_bob.py first with the
following contents:
from datetime import datetime, date
import time
from mutationtracker import MutationTrackedObject
class Person(MutationTrackedObject):
_mutations_log_function = print # optional attribute, remove for no console output
def __init__(
self,
social_security_number,
name,
gender,
date_of_birth,
children,
date_of_death=None,
):
self.social_security_number = social_security_number
self.name = name
self.gender = gender
self.date_of_birth = date_of_birth
self.date_of_death = date_of_death
self.children = children
def have_child(
self, social_security_number, name, gender, date_of_birth=datetime.now().date()
):
baby = Person(social_security_number, name, gender, date_of_birth, [])
self.children = self.children + [baby]
def adopt_child(
self, social_security_number, name, gender, date_of_birth=datetime.now().date()
):
child = Person(social_security_number, name, gender, date_of_birth, [])
self.children = self.children + [child]
def __repr__(self):
return f"{self.social_security_number} - {self.name}"
def main():
# ========== Example: init of Alice ==========
print("========== Example: init of Alice ==========")
example_person = Person(12345678901, "Alice", "Female", date(1990, 3, 7), [])
# ========== Example: children ==========
print("\n========== Example: children ==========")
# might one day have children herself
example_person.have_child(12312312301, "Charles", "Male")
example_person.have_child(12312312302, "Davey", "Male")
# might also adopt one more
example_person.adopt_child(
99889988771, "Fatima", "Female", date_of_birth=date(2024, 2, 2)
)
# might find out Charles is actually a different baby because of a hospital mix-up
# so put him up for adoption as he is not as enjoyable
def get_rid_of_child(person, name):
person.children = [c for c in person.children if c.name != name]
get_rid_of_child(example_person, "Charles")
# ========== Example: gender ==========
print("\n========== Example: gender ==========")
# is her gender right? might change
def change_gender(person, new_name, new_gender):
"""Might one day find out something is off but that can be changed"""
time.sleep(0.1) # takes some time
person.name = new_name
person.gender = new_gender
# actually, she feels like he and continues as Bob
change_gender(example_person, "Bob", "Male")
# Might regret that later, and actually convert back
change_gender(example_person, "Alice", "Female")
# ========== Example: safari trip gone wrong ==========
print("\n========== Example: safari trip gone wrong ==========")
# live is an adventure, so go on a nice safari trip
def go_on_safari(person):
"""Go on a nice and safe safari trip, so better be careful, right?"""
pet_lion(person)
def pet_lion(person):
"""Do not try this at home, or at the zoo, or while on safari, or anywhere ever, it might end badly"""
person.date_of_death = datetime.now().date()
go_on_safari(example_person)
# ========== Example: log full history of Alice ==========
print("\n========== Example: log full history of Alice ==========")
example_person.log_all_mutations()
return example_person
if __name__ == "__main__":
main()
Next up, you might want to create run.py to prevent it from running as script:
from alice_and_bob import main
"""
This example shows the Person class with baseclass MutationTrackedObject, and tells the life story of instance Alice
Make sure to put an breakpoint on the line with the `print` function and take a look at
`example_person.MUTATIONS` for the full experience provided by the MutationTrackedObject baseclass
"""
example_person = main()
# place a breakpoint on the print() line below
# run it in debug mode
# and make sure to check out example_person.MUTATIONS for the full experience
print("Thank you for trying the example with the baseclass MutationTrackedObject!")
Output
========== Example: init of Alice ==========
A new Person instance is being created by examples.baseclass.alice_and_bob.main:46: args=(12345678901, 'Alice', 'Female', datetime.date(1990, 3, 7), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12345678901 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:20)
Change in Person instance (repr unavailable) attribute name: to Alice from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:21)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:22)
Change in Person instance (12345678901 - Alice) attribute date_of_birth: to 1990-03-07 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to None from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:24)
Change in Person instance (12345678901 - Alice) attribute children: to [] from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:25)
========== Example: children ==========
A new Person instance is being created by examples.baseclass.alice_and_bob.Person.have_child:30: args=(12312312301, 'Charles', 'Male', datetime.date(2025, 3, 16), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12312312301 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:20)
Change in Person instance (repr unavailable) attribute name: to Charles from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:21)
Change in Person instance (12312312301 - Charles) attribute gender: to Male from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:22)
Change in Person instance (12312312301 - Charles) attribute date_of_birth: to 2025-03-16 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:23)
Change in Person instance (12312312301 - Charles) attribute date_of_death: to None from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:24)
Change in Person instance (12312312301 - Charles) attribute children: to [] from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:25)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles] from [] (by examples.baseclass.alice_and_bob.Person.have_child:31)
A new Person instance is being created by examples.baseclass.alice_and_bob.Person.have_child:30: args=(12312312302, 'Davey', 'Male', datetime.date(2025, 3, 16), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12312312302 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:20)
Change in Person instance (repr unavailable) attribute name: to Davey from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:21)
Change in Person instance (12312312302 - Davey) attribute gender: to Male from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:22)
Change in Person instance (12312312302 - Davey) attribute date_of_birth: to 2025-03-16 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:23)
Change in Person instance (12312312302 - Davey) attribute date_of_death: to None from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:24)
Change in Person instance (12312312302 - Davey) attribute children: to [] from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:25)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey] from [12312312301 - Charles] (by examples.baseclass.alice_and_bob.Person.have_child:31)
A new Person instance is being created by examples.baseclass.alice_and_bob.Person.adopt_child:36: args=(99889988771, 'Fatima', 'Female', datetime.date(2024, 2, 2), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 99889988771 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:20)
Change in Person instance (repr unavailable) attribute name: to Fatima from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:21)
Change in Person instance (99889988771 - Fatima) attribute gender: to Female from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:22)
Change in Person instance (99889988771 - Fatima) attribute date_of_birth: to 2024-02-02 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:23)
Change in Person instance (99889988771 - Fatima) attribute date_of_death: to None from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:24)
Change in Person instance (99889988771 - Fatima) attribute children: to [] from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:25)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey] (by examples.baseclass.alice_and_bob.Person.adopt_child:37)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] (by examples.baseclass.alice_and_bob.get_rid_of_child:61)
========== Example: gender ==========
Change in Person instance (12345678901 - Alice) attribute name: to Bob from Alice (by examples.baseclass.alice_and_bob.change_gender:72)
Change in Person instance (12345678901 - Bob) attribute gender: to Male from Female (by examples.baseclass.alice_and_bob.change_gender:73)
Change in Person instance (12345678901 - Bob) attribute name: to Alice from Bob (by examples.baseclass.alice_and_bob.change_gender:72)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from Male (by examples.baseclass.alice_and_bob.change_gender:73)
========== Example: safari trip gone wrong ==========
Change in Person instance (12345678901 - Alice) attribute date_of_death: to 2025-03-16 from None (by examples.baseclass.alice_and_bob.pet_lion:90)
========== Example: log full history of Alice ==========
===== All mutations for Person instance 12345678901 - Alice =====
Created by: examples.baseclass.alice_and_bob.main:46
Last mutation by: examples.baseclass.alice_and_bob.pet_lion:90
Filled by: None
Number of attribute mutations: 15
Change in Person instance (12345678901 - Alice) attribute social_security_number: to 12345678901 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:20)
Change in Person instance (12345678901 - Alice) attribute name: to Alice from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:21)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:22)
Change in Person instance (12345678901 - Alice) attribute date_of_birth: to 1990-03-07 from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to None from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:24)
Change in Person instance (12345678901 - Alice) attribute children: to [] from AttributeDoesNotExist (by examples.baseclass.alice_and_bob.Person.__init__:25)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles] from [] (by examples.baseclass.alice_and_bob.Person.have_child:31)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey] from [12312312301 - Charles] (by examples.baseclass.alice_and_bob.Person.have_child:31)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey] (by examples.baseclass.alice_and_bob.Person.adopt_child:37)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] (by examples.baseclass.alice_and_bob.get_rid_of_child:61)
Change in Person instance (12345678901 - Alice) attribute name: to Bob from Alice (by examples.baseclass.alice_and_bob.change_gender:72)
Change in Person instance (12345678901 - Alice) attribute gender: to Male from Female (by examples.baseclass.alice_and_bob.change_gender:73)
Change in Person instance (12345678901 - Alice) attribute name: to Alice from Bob (by examples.baseclass.alice_and_bob.change_gender:72)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from Male (by examples.baseclass.alice_and_bob.change_gender:73)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to 2025-03-16 from None (by examples.baseclass.alice_and_bob.pet_lion:90)
Thank you for trying the example with the baseclass MutationTrackedObject!
Debug mode
Feel free to experience it yourself in debug mode to explore the data in example_person.MUTATIONS near the end of the
example script. There's more data than printed in the log, such as the full trace stack.
Example 2: track_mutations decorator
Code
This example is best shown using two files in a single directory: alice_and_bob.py and run.py. The latter is not
mandatory, but will improve the result because it not ran as script. Let's create alice_and_bob.py first with the
following contents:
from datetime import datetime, date
import time
from mutationtracker import track_mutations
@track_mutations(log_function=print) # optional keyword argument, default: no output
class Person:
def __init__(
self,
social_security_number,
name,
gender,
date_of_birth,
children,
date_of_death=None,
):
self.social_security_number = social_security_number
self.name = name
self.gender = gender
self.date_of_birth = date_of_birth
self.date_of_death = date_of_death
self.children = children
def have_child(
self, social_security_number, name, gender, date_of_birth=datetime.now().date()
):
baby = Person(social_security_number, name, gender, date_of_birth, [])
self.children = self.children + [baby]
def adopt_child(
self, social_security_number, name, gender, date_of_birth=datetime.now().date()
):
child = Person(social_security_number, name, gender, date_of_birth, [])
self.children = self.children + [child]
def __repr__(self):
return f"{self.social_security_number} - {self.name}"
def main():
# ========== Example: init of Alice ==========
print("========== Example: init of Alice ==========")
example_person = Person(12345678901, "Alice", "Female", date(1990, 3, 7), [])
# ========== Example: children ==========
print("\n========== Example: children ==========")
# might one day have children herself
example_person.have_child(12312312301, "Charles", "Male")
example_person.have_child(12312312302, "Davey", "Male")
# might also adopt one more
example_person.adopt_child(
99889988771, "Fatima", "Female", date_of_birth=date(2024, 2, 2)
)
# might find out Charles is actually a different baby because of a hospital mix-up
# so put him up for adoption as he is not as enjoyable
def get_rid_of_child(person, name):
person.children = [c for c in person.children if c.name != name]
get_rid_of_child(example_person, "Charles")
# ========== Example: gender ==========
print("\n========== Example: gender ==========")
# is her gender right? might change
def change_gender(person, new_name, new_gender):
"""Might one day find out something is off but that can be changed"""
time.sleep(0.1) # takes some time
person.name = new_name
person.gender = new_gender
# actually, she feels like he and continues as Bob
change_gender(example_person, "Bob", "Male")
# Might regret that later, and actually convert back
change_gender(example_person, "Alice", "Female")
# ========== Example: safari trip gone wrong ==========
print("\n========== Example: safari trip gone wrong ==========")
# live is an adventure, so go on a nice safari trip
def go_on_safari(person):
"""Go on a nice and safe safari trip, so better be careful, right?"""
pet_lion(person)
def pet_lion(person):
"""Do not try this at home, or at the zoo, or while on safari, or anywhere ever, it might end badly"""
person.date_of_death = datetime.now().date()
go_on_safari(example_person)
# ========== Example: log full history of Alice ==========
print("\n========== Example: log full history of Alice ==========")
example_person.log_all_mutations()
return example_person
if __name__ == "__main__":
main()
Next up, you might want to create run.py to prevent it from running as script:
from alice_and_bob import main
"""
This example shows the Person class with decorator track_mutations, and tells the life story of instance Alice
Make sure to put an breakpoint on the line with the `print` function and take a look at
`example_person.MUTATIONS` for the full experience provided by the track_mutations decorator
"""
example_person = main()
# place a breakpoint on the print() line below
# run it in debug mode
# and make sure to check out example_person.MUTATIONS for the full experience
print("Thank you for trying the example with the decorator track_mutations!")
Output
========== Example: init of Alice ==========
A new Person instance is being created by examples.decorator.alice_and_bob.main:44: args=(12345678901, 'Alice', 'Female', datetime.date(1990, 3, 7), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12345678901 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:18)
Change in Person instance (repr unavailable) attribute name: to Alice from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:19)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:20)
Change in Person instance (12345678901 - Alice) attribute date_of_birth: to 1990-03-07 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:21)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to None from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:22)
Change in Person instance (12345678901 - Alice) attribute children: to [] from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:23)
========== Example: children ==========
A new Person instance is being created by examples.decorator.alice_and_bob.Person.have_child:28: args=(12312312301, 'Charles', 'Male', datetime.date(2025, 3, 16), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12312312301 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:18)
Change in Person instance (repr unavailable) attribute name: to Charles from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:19)
Change in Person instance (12312312301 - Charles) attribute gender: to Male from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:20)
Change in Person instance (12312312301 - Charles) attribute date_of_birth: to 2025-03-16 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:21)
Change in Person instance (12312312301 - Charles) attribute date_of_death: to None from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:22)
Change in Person instance (12312312301 - Charles) attribute children: to [] from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles] from [] (by examples.decorator.alice_and_bob.Person.have_child:29)
A new Person instance is being created by examples.decorator.alice_and_bob.Person.have_child:28: args=(12312312302, 'Davey', 'Male', datetime.date(2025, 3, 16), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 12312312302 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:18)
Change in Person instance (repr unavailable) attribute name: to Davey from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:19)
Change in Person instance (12312312302 - Davey) attribute gender: to Male from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:20)
Change in Person instance (12312312302 - Davey) attribute date_of_birth: to 2025-03-16 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:21)
Change in Person instance (12312312302 - Davey) attribute date_of_death: to None from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:22)
Change in Person instance (12312312302 - Davey) attribute children: to [] from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey] from [12312312301 - Charles] (by examples.decorator.alice_and_bob.Person.have_child:29)
A new Person instance is being created by examples.decorator.alice_and_bob.Person.adopt_child:34: args=(99889988771, 'Fatima', 'Female', datetime.date(2024, 2, 2), []), kwargs={}
Change in Person instance (repr unavailable) attribute social_security_number: to 99889988771 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:18)
Change in Person instance (repr unavailable) attribute name: to Fatima from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:19)
Change in Person instance (99889988771 - Fatima) attribute gender: to Female from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:20)
Change in Person instance (99889988771 - Fatima) attribute date_of_birth: to 2024-02-02 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:21)
Change in Person instance (99889988771 - Fatima) attribute date_of_death: to None from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:22)
Change in Person instance (99889988771 - Fatima) attribute children: to [] from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey] (by examples.decorator.alice_and_bob.Person.adopt_child:35)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] (by examples.decorator.alice_and_bob.get_rid_of_child:59)
========== Example: gender ==========
Change in Person instance (12345678901 - Alice) attribute name: to Bob from Alice (by examples.decorator.alice_and_bob.change_gender:70)
Change in Person instance (12345678901 - Bob) attribute gender: to Male from Female (by examples.decorator.alice_and_bob.change_gender:71)
Change in Person instance (12345678901 - Bob) attribute name: to Alice from Bob (by examples.decorator.alice_and_bob.change_gender:70)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from Male (by examples.decorator.alice_and_bob.change_gender:71)
========== Example: safari trip gone wrong ==========
Change in Person instance (12345678901 - Alice) attribute date_of_death: to 2025-03-16 from None (by examples.decorator.alice_and_bob.pet_lion:88)
========== Example: log full history of Alice ==========
===== All mutations for Person instance 12345678901 - Alice =====
Created by: examples.decorator.alice_and_bob.main:44
Last mutation by: examples.decorator.alice_and_bob.pet_lion:88
Filled by: None
Number of attribute mutations: 15
Change in Person instance (12345678901 - Alice) attribute social_security_number: to 12345678901 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:18)
Change in Person instance (12345678901 - Alice) attribute name: to Alice from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:19)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:20)
Change in Person instance (12345678901 - Alice) attribute date_of_birth: to 1990-03-07 from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:21)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to None from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:22)
Change in Person instance (12345678901 - Alice) attribute children: to [] from AttributeDoesNotExist (by examples.decorator.alice_and_bob.Person.__init__:23)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles] from [] (by examples.decorator.alice_and_bob.Person.have_child:29)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey] from [12312312301 - Charles] (by examples.decorator.alice_and_bob.Person.have_child:29)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey] (by examples.decorator.alice_and_bob.Person.adopt_child:35)
Change in Person instance (12345678901 - Alice) attribute children: to [12312312302 - Davey, 99889988771 - Fatima] from [12312312301 - Charles, 12312312302 - Davey, 99889988771 - Fatima] (by examples.decorator.alice_and_bob.get_rid_of_child:59)
Change in Person instance (12345678901 - Alice) attribute name: to Bob from Alice (by examples.decorator.alice_and_bob.change_gender:70)
Change in Person instance (12345678901 - Alice) attribute gender: to Male from Female (by examples.decorator.alice_and_bob.change_gender:71)
Change in Person instance (12345678901 - Alice) attribute name: to Alice from Bob (by examples.decorator.alice_and_bob.change_gender:70)
Change in Person instance (12345678901 - Alice) attribute gender: to Female from Male (by examples.decorator.alice_and_bob.change_gender:71)
Change in Person instance (12345678901 - Alice) attribute date_of_death: to 2025-03-16 from None (by examples.decorator.alice_and_bob.pet_lion:88)
Thank you for trying the example with the decorator track_mutations!
Debug mode
Feel free to experience it yourself in debug mode to explore the data in example_person.MUTATIONS near the end of the
example script. There's more data than printed in the log, such as the full trace stack.
Limitations
- The mutation will keep the
new_valueas it was at the time of mutation (usingdeepcopy). If it is a mutable type, it might be further mutated bypassing the observer functionality because__setattr__is not called. - The user is prohibited from redefining
__setattr__and__new__while usingMutationTrackedObjectbecause it will break the observer functionality. If this is blocking for your use, please use thetrack_mutationsdecorator instead. - The
MutationTrackedObjectfunctionality will inherit to all child classes of the class actually aimed to be tracked. Often this is desirable, but it might be undesirable in some cases. If that's the case, please use thetrack_mutationsdecorator instead.
Release notes
No changes since initial release v1.0.1
Useful links
License
Copyright (c) 2025 Jaime Derrez
All rights reserved.
This code is available under the BSD-license. Read the LICENSE file in the root directory for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mutationtracker-1.0.1.tar.gz.
File metadata
- Download URL: mutationtracker-1.0.1.tar.gz
- Upload date:
- Size: 17.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cfd5cefe9b5e5d5af2cf9003d8ad6a0dbfcf118e83aa5c2fd84237a8f2b5567
|
|
| MD5 |
562116b7ee073678693095c085f1d639
|
|
| BLAKE2b-256 |
6251132460eff060143f3cf19beafb025cd7ed41666e8e7a1b234d424ea65975
|
Provenance
The following attestation bundles were made for mutationtracker-1.0.1.tar.gz:
Publisher:
python-publish.yml on RedmanLabs/mutationtracker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mutationtracker-1.0.1.tar.gz -
Subject digest:
6cfd5cefe9b5e5d5af2cf9003d8ad6a0dbfcf118e83aa5c2fd84237a8f2b5567 - Sigstore transparency entry: 183038274
- Sigstore integration time:
-
Permalink:
RedmanLabs/mutationtracker@59651b387d878c5cce4943e5a416387c3f1b38c2 -
Branch / Tag:
refs/tags/1.0.1 - Owner: https://github.com/RedmanLabs
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@59651b387d878c5cce4943e5a416387c3f1b38c2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mutationtracker-1.0.1-py3-none-any.whl.
File metadata
- Download URL: mutationtracker-1.0.1-py3-none-any.whl
- Upload date:
- Size: 14.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80e2fa29ca083c203e5cfcc4c312d543bf18baaf2f037ededa144de7fccd17c3
|
|
| MD5 |
249dedead6b1315f1259e62698bab7b6
|
|
| BLAKE2b-256 |
31c885e565c7ecd7d2549236115bce057a8b32ef513877137aa9ba015702ef6f
|
Provenance
The following attestation bundles were made for mutationtracker-1.0.1-py3-none-any.whl:
Publisher:
python-publish.yml on RedmanLabs/mutationtracker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mutationtracker-1.0.1-py3-none-any.whl -
Subject digest:
80e2fa29ca083c203e5cfcc4c312d543bf18baaf2f037ededa144de7fccd17c3 - Sigstore transparency entry: 183038275
- Sigstore integration time:
-
Permalink:
RedmanLabs/mutationtracker@59651b387d878c5cce4943e5a416387c3f1b38c2 -
Branch / Tag:
refs/tags/1.0.1 - Owner: https://github.com/RedmanLabs
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@59651b387d878c5cce4943e5a416387c3f1b38c2 -
Trigger Event:
release
-
Statement type: