--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_utils/property.py Wed Dec 05 12:45:56 2018 +0100
@@ -0,0 +1,118 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+
+# import packages
+
+
+class cached(object):
+ """Custom property decorator to define a property or function which is calculated only once
+
+ When applied on a function, caching is based on input arguments
+ """
+
+ def __init__(self, function):
+ self._function = function
+ self._cache = {}
+
+ def __call__(self, *args):
+ try:
+ return self._cache[args]
+ except KeyError:
+ self._cache[args] = self._function(*args)
+ return self._cache[args]
+
+ def expire(self, *args):
+ del self._cache[args]
+
+
+class cached_property(object):
+ """A read-only property decorator that is only evaluated once.
+
+ The value is cached on the object itself rather than the function or class; this should prevent
+ memory leakage.
+ """
+ def __init__(self, fget, doc=None):
+ self.fget = fget
+ self.__doc__ = doc or fget.__doc__
+ self.__name__ = fget.__name__
+ self.__module__ = fget.__module__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ obj.__dict__[self.__name__] = result = self.fget(obj)
+ return result
+
+
+class classproperty:
+ """Same decorator as property(), but passes obj.__class__ instead of obj to fget/fset/fdel.
+
+ Original code for property emulation:
+ https://docs.python.org/3.5/howto/descriptor.html#properties
+ """
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+ self.fget = fget
+ self.fset = fset
+ self.fdel = fdel
+ if doc is None and fget is not None:
+ doc = fget.__doc__
+ self.__doc__ = doc
+
+ def __get__(self, obj, objtype=None):
+ if obj is None:
+ return self
+ if self.fget is None:
+ raise AttributeError("Unreadable attribute")
+ return self.fget(obj.__class__)
+
+ def __set__(self, obj, value):
+ if self.fset is None:
+ raise AttributeError("Can't set attribute")
+ self.fset(obj.__class__, value)
+
+ def __delete__(self, obj):
+ if self.fdel is None:
+ raise AttributeError("Can't delete attribute")
+ self.fdel(obj.__class__)
+
+ def getter(self, fget):
+ return type(self)(fget, self.fset, self.fdel, self.__doc__)
+
+ def setter(self, fset):
+ return type(self)(self.fget, fset, self.fdel, self.__doc__)
+
+ def deleter(self, fdel):
+ return type(self)(self.fget, self.fset, fdel, self.__doc__)
+
+
+def classproperty_support(cls):
+ """Class decorator to add metaclass to a class.
+
+ Metaclass uses to add descriptors to class attributes
+ """
+ class Meta(type):
+ pass
+
+ for name, obj in vars(cls).items():
+ if isinstance(obj, classproperty):
+ setattr(Meta, name, property(obj.fget, obj.fset, obj.fdel))
+
+ class Wrapper(cls, metaclass=Meta):
+ pass
+ return Wrapper