diff options
| author | Bob Halley <halley@dnspython.org> | 2020-07-30 09:21:03 -0700 |
|---|---|---|
| committer | Bob Halley <halley@dnspython.org> | 2020-08-10 06:43:32 -0700 |
| commit | 54deb97c2a5331fe99a12d720f24fb481ec31576 (patch) | |
| tree | 0954caa36095efcc667aee6a3732759d4c9001c6 /dns/_immutable_attr.py | |
| parent | 26fd19690c44a01c84c27a2d4244d2e5dc7b7a19 (diff) | |
| download | dnspython-transaction.tar.gz | |
txn checkpointtransaction
Diffstat (limited to 'dns/_immutable_attr.py')
| -rw-r--r-- | dns/_immutable_attr.py | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/dns/_immutable_attr.py b/dns/_immutable_attr.py new file mode 100644 index 0000000..4221967 --- /dev/null +++ b/dns/_immutable_attr.py @@ -0,0 +1,64 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# This implementation of the immutable decorator is for python 3.6, +# which doesn't have Context Variables. This implementation is somewhat +# costly for classes with slots, as it adds a __dict__ to them. + +class _Immutable: + """Immutable mixin class""" + + # Note we MUST NOT have __slots__ as that causes + # + # TypeError: multiple bases have instance lay-out conflict + # + # when we get mixed in with another class with slots. When we + # get mixed into something with slots, it effectively adds __dict__ to + # the slots of the other class, which allows attribute setting to work, + # albeit at the cost of the dictionary. + + def __setattr__(self, name, value): + if not hasattr(self, '_immutable_init') or \ + self._immutable_init is not self: + raise TypeError("object doesn't support attribute assignment") + else: + super().__setattr__(name, value) + + def __delattr__(self, name): + if not hasattr(self, '_immutable_init') or \ + self._immutable_init is not self: + raise TypeError("object doesn't support attribute assignment") + else: + super().__delattr__(name) + + +def _immutable_init(f): + def nf(*args, **kwargs): + try: + # Are we already initializing an immutable class? + previous = args[0]._immutable_init + except AttributeError: + # We are the first! + previous = None + object.__setattr__(args[0], '_immutable_init', args[0]) + # call the actual __init__ + f(*args, **kwargs) + if not previous: + # If we started the initialzation, establish immutability + # by removing the attribute that allows mutation + object.__delattr__(args[0], '_immutable_init') + return nf + + +def immutable(cls): + if _Immutable in cls.__mro__: + # Some ancestor already has the mixin, so just make sure we keep + # following the __init__ protocol. + cls.__init__ = _immutable_init(cls.__init__) + ncls = cls + else: + # Mixin the Immutable class and follow the __init__ protocol. + class ncls(_Immutable, cls): + @_immutable_init + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + return ncls |
