Skip to content

Instantly share code, notes, and snippets.

@naufraghi
Last active May 23, 2019 20:09
Show Gist options
  • Save naufraghi/7df4cf49284b9ad605bdbdbc4dfc6afe to your computer and use it in GitHub Desktop.
Save naufraghi/7df4cf49284b9ad605bdbdbc4dfc6afe to your computer and use it in GitHub Desktop.
class Checked(object):
"""
This class offers a `checked()` method that returns the same namedtuple after an user defined check.
The returned class is not exacly the same, but is in fact a copy with an added marker base class.
Given the `namedtuple` immutability, the marker base class is used to skip the checks after the first run.
"""
class Done: # Using `Done(object)` let the code work as expected in Python 2 too
__slots__ = ()
__slots__ = ()
def checked(self):
if isinstance(self, Checked.Done):
return self
self._check()
_Checked = type(self.__class__.__name__, self.__class__.__bases__ + (Checked.Done, ),
{"__slots__": ()})
return _Checked(*self)
def _check(self):
"Override in subclasses"
pass
if __name__ == "__main__":
import sys
from collections import namedtuple
class Foo(namedtuple("Foo", "a b"), Checked):
__slots__ = ()
def _check(self):
print("doing check for {name}".format(name=type(self).__name__))
foo = Foo(1, 2)
fooc = foo.checked()
a = {}
print("pass 1")
# Sadly the instances are not preserved, even if the check passes
assert foo is not fooc
# The size of the object is preserved
try:
assert sys.getsizeof(foo) == sys.getsizeof(fooc)
except AssertionError:
print("Python 2 Warning: sizeof(foo)={0} != sizeof(fooc)={1}".format(sys.getsizeof(foo), sys.getsizeof(fooc)))
# Slots in type() declaration are not managed correctly in Python 2
assert sys.version_info.major < 3
# An the equality too
assert foo == fooc
a[foo] = True
print("pass 2")
# Should not print "doing check..."
assert foo == fooc.checked()
# Used as dictionary key, the checked and unckecked are equal
assert a[fooc]
# We can verify if an instance is checked or not
assert not isinstance(foo, Checked.Done)
assert isinstance(fooc, Checked.Done)
# Slots are preserved
try:
foo.bar = 12
except AttributeError:
print("Unable to set foo.bar = 12")
else:
assert False
try:
fooc.bar = 12
except AttributeError:
print("Unable to set fooc.bar = 12")
else:
print("Python 2 Warning: Able to set fooc.bar = 12")
# Slots in type() declaration are not managed correctly in Python 2
assert sys.version_info.major < 3
@naufraghi
Copy link
Author

Python 2 run:

$ python2 src/sandbox/checked_namedtuple.py
doing check for Foo
pass 1
Python 2 Warning: sizeof(foo)=72 != sizeof(fooc)=80
pass 2
Unable to set foo.bar = 12
Python 2 Warning: Able to set fooc.bar = 12

Python 3 run:

$ python3 src/sandbox/checked_namedtuple.py
doing check for Foo
pass 1
pass 2
Unable to set foo.bar = 12
Unable to set fooc.bar = 12

@naufraghi
Copy link
Author

Problem solved, class Done: should be class Done(object): to use the new style classes in Python 2 too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment