A method decorator: provides a check for presence of specified attributes.
>>> class XX(object):
... a = 1
...
... @attr_required('a')
... def meth_a(self):
... print 'OK'
...
... @attr_required('a', 'b')
... def meth_ab(self):
... print 'Excellent'
...
... @attr_required('z', dummy_placeholder=NotImplemented)
... def meth_z(self):
... print 'Nice'
...
>>> x = XX()
>>> x.meth_a()
OK
>>> x.meth_ab()
Traceback (most recent call last):
...
NotImplementedError: ...
>>> x.b = 42
>>> x.meth_ab()
Excellent
>>> del XX.a
>>> x.meth_ab()
Traceback (most recent call last):
...
NotImplementedError: ...
>>> XX.a = None
>>> x.meth_ab()
Traceback (most recent call last):
...
NotImplementedError: ...
>>> x.meth_z()
Traceback (most recent call last):
...
NotImplementedError: ...
>>> x.z = None
>>> x.meth_z() # OK as here `dummy_placeholder` is not None
Nice
>>> x.z = NotImplemented
>>> x.meth_z()
Traceback (most recent call last):
...
NotImplementedError: ...
Traceback (most recent call last):
...
NotImplementedError: ...
A class decorator ensuring that the class can be instantiated only once.
Trying to instantiate the decorated class more than once causes RuntimeError – unless, during provious instantiations, __init__() of the decorated class did not succeed (caused an exception).
Subclasses are also bound by this restriction (i.e. the decorated class and its subclasses are “counted” as one entity) – unless their __init__() is overridden in such a way that the __init__() of the decorated class is not called.
The check is thread-safe (protected with a lock).
>>> @singleton
... class X(object):
... pass
...
>>> o = X()
>>> o = X()
Traceback (most recent call last):
...
RuntimeError: ...
Traceback (most recent call last):
...
RuntimeError: ...
>>> @singleton
... class X2(object):
... def __init__(self, exc=None):
... if exc is not None:
... raise exc
...
>>> o = X2(ValueError('foo'))
Traceback (most recent call last):
...
ValueError: foo
>>> o = X2()
>>> o = X2()
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = X2(ValueError('foo'))
Traceback (most recent call last):
...
RuntimeError: ...
Traceback (most recent call last):
...
RuntimeError: ...
>>> @singleton
... class Y(object):
... def __init__(self, a, b, c=42):
... print a, b, c
...
>>> class Z(Y):
... pass
...
>>> class ZZZ(Y):
... def __init__(self, a, b):
... # will *not* call Y.__init__
... print 'zzz', a, b
...
>>> o = Y('spam', b='ham')
spam ham 42
>>> o = Y('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = Z('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = ZZZ('spam', b='ham')
zzz spam ham
Traceback (most recent call last):
...
RuntimeError: ...
>>> @singleton
... class Y2(object):
... def __init__(self, a, b, c=42):
... print a, b, c
...
>>> class Z2(Y2):
... pass
...
>>> class ZZZZZ(Y):
... def __init__(self, a, b):
... # *will* call Y.__init__
... super(ZZZZZ, self).__init__(a, b=b)
...
>>> o = Z2('spam', b='ham')
spam ham 42
>>> o = Z2('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = Y2('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = ZZZZZ('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
Traceback (most recent call last):
...
RuntimeError: ...
>>> class A(object):
... def __init__(self, a, b, c=42):
... print a, b, c
...
>>> @singleton
... class B(A):
... pass
...
>>> o = A('spam', b='ham')
spam ham 42
>>> o = B('spam', b='ham')
spam ham 42
>>> o = B('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
>>> o = A('spam', b='ham')
spam ham 42
>>> o = B('spam', b='ham')
Traceback (most recent call last):
...
RuntimeError: ...
Traceback (most recent call last):
...
RuntimeError: ...