Skip to content

pwwang/attr_property

Repository files navigation

attr_property

Pypi Github PythonVers Travis building Codacy Codacy coverage

Property support for attrs

Installation

pip install attr_property

Usage

Definitaion of class with attr_propertys

import attr
from attr_property import attr_property, attr_property_class

@attr_property_class
@attr.s
class A:

  a = attr_property()
  b = attr.ib() # compatible with original attr.ib

Specification of getter, setter, and deleter

def a_getter(this, value):
    print('Property a has been accessed!')
    return value + 1
def a_setter(this, value):
    print(f'Property a has been set with value {value!r}')
def a_deleter(this):
    print('Property has been deleted!')

@attr_property_class
@attr.s
class A:
    a = attr_property(getter = a_getter, setter = a_setter, deleter = a_deleter)

a = A(a = 1)
# Property a has been set with value 1
# Property a has been accessed!
print('a.a =', a.a)
# a.a = 2
del a.a
# Property has been deleted!
a.a
# Error

Disabling deleter

@attr_property_class
@attr.s
class A:
    a = attr_property(deleter = False)

a = A(1)
del a.a
# AttributeError: can't delete attribute

Disabling setter

@attr_property_class
@attr.s
class A:
    # remember you can set init = True or
    # set any default values for it,
    # as setter will be called in __init__
    # this will cause AttributeError
    a = attr_property(init = False, setter = False, getter = lambda this, value: 2)

a = A()
a.a = 1
# AttributeError: can't set attribute

# OK to call getter
a.a == 2

Run attr.ib's converter and validator in setter

@attr_property_class
@attr.s
class A:
    a = attr_property(converter = int, validator_runtime = True, converter_runtime = True)

    @a.validator
    def lessthan20(self, attribute, value):
        if value >= 20:
            raise ValueError("d should be less than 20.")

a = A('3')
# a.a == 3
a.a = '30'
# ValueError

Order of execution of setter:

  • Delete cached value
  • Run converter
  • Run validator
  • Run specified setter
  • Save value as raw value

Caching getter results

@attr_property_class
@attr.s
class A:
    a = attr_property(getter = lambda this, value: value + 1, cache = True)

a = A(1)
# a.a == 2
# will not do value + again
# validators and converters will be skipped, as well.

Accessing raw values before getter calculation

@attr_property_class
@attr.s
class A:
    a = attr_property(getter = lambda this, value: value + 1, convert  = int, raw = True)

a = A('1')
# a.a == 2
# a._a == 1 # converted value
a._a = 9
# AttributeError, it's readonly

Using a different prefix

@attr_property_class
@attr.s
class A:
    a = attr_property(getter = lambda this, value: value + 1, convert  = int, raw = 'raw_')

a = A('1')
# a.raw_a == 1

How does it work?

  • Hack attrs' _attrs_to_init_script function to insert codes to initiate self.__attrs_property_raw__ to save raw values and __attrs_property_cached__ to save cached values.
  • Create propertys for each attribute in class decorator attr_property_class.