I'd like to establish a code pattern that allows me to do something analogous to the following:
# Foo has getter-only properties
foo: Foo = FooRepo.get(id="foo_id")
assert foo.bar == "original_bar"
# MutableFoo is a window onto the Foo, and when it __exit__s, it persists to the repo
with foo.mutable() as mutable_foo:
mutable_foo.bar = "new_bar"
# We've updated the Foo, as well as whatever persistent version the FooRepo owns
assert foo.bar == "new_bar"
I'm not wedded to the exact code pattern. What I like about it:
- We can pass the
Fooaround to lots of areas of code, and as long asmutable()isn't called, we can treat it as immutable and ignore the idea of persistence. - We have the ability to work transactionality into that
ContextManagerin a variety of ways. Outside of theContextManagerwe get to treat the object as a snapshot, which will be more common and is less hairy. - Callers can largely ignore persistence
Challenges I see:
- Need an elegant way of preventing creating the mutable version outside of a
withblock. - Ditto with whatever interface into
FooenablesMutableFooto do its mutation. (Can you tell I'm used to Java? Lacking inner classes with access to private members has me scratching my head a bit) - Need an elegant way of doing error checking. Since the persistence happens in response to exiting the context, there's potential for exceptions and those will need to be caught and handled responsibly.
Have folks built frameworks of this kind in Python? What solutions do you like?
You can create an "immutable" proxy object that disables setter and deleter methods by making them raise an exception. Implement a context manager that returns the original mutable object upon entrance to allow mutation within the context:
so that the following code passes the assertion:
while the following code would raise an
AttributeError: