Advanced descriptors¶
This package includes helpers for special cases:
SeparateClassMethod - allow to have classmethod and normal method both with the same name.
AdvancedProperty - property with possibility to set class wide getter.
LogOnAccess - property with logging on successful get/set/delete or failure.
SeparateClassMethod¶
This descriptor can be set using standard decorator syntax. Create instance with arguments:
def imeth(instance):
return instance.value
def cmeth(owner):
return owner.value
class Target(object):
value = 1
def __init__(self):
self.value = 2
getval = advanced_descriptors.SeparateClassMethod(
imeth, cmeth
)
Create instance wrapping as decorator:
class Target(object):
value = 1
def __init__(self):
self.value = 2
@advanced_descriptors.SeparateClassMethod
def getval(self):
return self.value
@getval.class_method
def getval(cls):
return cls.value
Cases with method only and classmethod only is useless: method as-is and @classmethod should be used in corresponding cases.
Note
classmethod receives class as argument. IDE’s don’t know about custom descriptors and substitutes self by default.
AdvancedProperty¶
This descriptor should be used in cases, when in addition to normal property API, class getter is required. If class-wide setter and deleter also required - you should use standard propery in metaclass.
Usage examples:
In addition to normal property API:
class Target(object): _value = 777 def __init__(self): self._value = 42 @advanced_descriptors.AdvancedProperty def val(self): return self._value @val.setter def val(self, value): self._value = value @val.deleter def val(self): self._value = 0 @val.cgetter def val(cls): return cls._value
Use class-wide getter for instance too:
class Target(object): _value = 1 val = advanced_descriptors.AdvancedProperty() @val.cgetter def val(cls): return cls._value
Note
class-wide getter receives class as argument. IDE’s don’t know about custom descriptors and substitutes self by default.
LogOnAccess¶
This special case of property is useful in cases, where a lot of properties should be logged by similar way without writing a lot of code.
Basic API is conform with property, but in addition it is possible to customize logger, log levels and log conditions.
Usage examples:
Simple usage. All by default. logger is re-used from instance if available with names logger or log else used internal advanced_descriptors.log_on_access logger:
import logging class Target(object): def init(self, val='ok') self.val = val self.logger = logging.get_logger(self.__class__.__name__) # Single for class, follow subclassing def __repr__(self): return "{cls}(val={self.val})".format(cls=self.__class__.__name__, self=self) @advanced_descriptors.LogOnAccess def ok(self): return self.val @ok.setter def ok(self, val): self.val = val @ok.deleter def ok(self): self.val = ""
Use with global logger for class:
class Target(object): def init(self, val='ok') self.val = val def __repr__(self): return "{cls}(val={self.val})".format(cls=self.__class__.__name__, self=self) @advanced_descriptors.LogOnAccess def ok(self): return self.val @ok.setter def ok(self, val): self.val = val @ok.deleter def ok(self): self.val = "" ok.logger = 'test_logger' ok.log_level = logging.INFO ok.exc_level = logging.ERROR ok.log_object_repr = True # As by default ok.log_success = True # As by default ok.log_failure = True # As by default ok.log_traceback = True # As by default ok.override_name = None # As by default: use original name
Testing¶
The main test mechanism for the package advanced-descriptors is using tox. Available environments can be collected via tox -l
CI systems¶
For CI/CD GitHub actions is used:
GitHub actions: is used for checking: PEP8, pylint, bandit, installation possibility and unit tests.
Contents: