Source code for mypythontools.property
"""Module contains MyProperty class that edit normal python property to add new features.
There is default setter, it's possible to auto init values on class init and values in setter can be
validated.
It's possible to set function as a value and it's evaluated during call.
Example of how can it be used is in module config.
Examples:
=========
>>> class MyClass:
... # Init all default values of defined properties
... def __init__(self):
... for j in vars(type(self)).values():
... if type(j) is MyProperty:
... setattr(self, j.private_name, j.init_function)
...
... @MyProperty(int) # New value will be validated whether it's int
... def var() -> int: # This is for type hints in IDE.
... '''
... Type:
... int
...
... Default:
... 123
...
... This is docstrings (also visible in IDE, because not defined dynamically).'''
...
... return 123 # This is initial value that can be edited.
...
... @MyProperty() # Even if you don't need any params, use empty brackets
... def var2():
... return 111
...
>>> myobject = MyClass()
>>> myobject.var
123
>>> myobject.var = 124
>>> myobject.var
124
"""
import types as types_lib
from .misc import validate
import mylogging
[docs]class MyProperty(property):
"""Python property on steroids. Check module docstrings for more info."""
def __init__(self, types=None, options=None, fget=None, fset=None, doc=None):
if isinstance(types, types_lib.FunctionType):
raise SyntaxError(
mylogging.format_str("@MyProperty decorator has to be called (parentheses at the end).")
)
self.fget_new = fget if fget else self.default_fget
self.fset_new = fset if fset else self.default_fset
self.__doc__ = doc
self.types = types
self.options = options
def __call__(self, init_function):
self.init_function = init_function
self.__doc__ = init_function.__doc__
return self
[docs] def default_fget(self, object):
return getattr(object, self.private_name)
[docs] def default_fset(self, object, content):
setattr(object, self.private_name, content)
def __set_name__(self, _, name):
self.public_name = name
self.private_name = "_" + name
def __get__(self, object, objtype=None):
# If getting MyProperty class, not object, return MyProperty itself
if not object:
return self
# Expected value can be nominal value or function, that return that value
content = self.fget_new(object)
if callable(content):
if not len(content.__code__.co_varnames):
value = content()
else:
value = content(object)
else:
value = content
return value
def __set__(self, object, content):
# You can setup value or function, that return that value
if callable(content):
result = content(object)
else:
result = content
validate(result, self.types, self.options, self.public_name)
# Method defined can be pass as static method
try:
self.fset_new(object, content)
except TypeError:
self.fset_new(self, object, content)