attrs

Attrs, allow you to define a set of attributes that will be applied later to a class or object.

>>> from flask.ext.dry import attrs

Basics

Attrs are created with keyword arguments:

>>> a = attrs(foo=1, bar=2)

They may be copied into a class:

>>> class c: pass
>>> a.copy_into(c)
>>> c.foo
1
>>> c.bar
2

Or an object:

>>> class d:
...     # for lookup modifier, later
...     def get_expanded_attr(self, attr): return getattr(self, attr)
>>> obj = d()
>>> a.copy_into(obj)
>>> obj.foo
1
>>> obj.bar
2

In the latter case, the class isn’t changed:

>>> d.foo
Traceback (most recent call last):
    ...
AttributeError: type object 'd' has no attribute 'foo'

They may also be changed, like any other object:

>>> a.foo = 100
>>> obj2 = d()
>>> a.copy_into(obj2)
>>> obj2.foo
100
>>> obj2.bar
2

Finally, attrs make a deepcopy of all values assigned to them, as well as the values copied into the receiving class or object so that one object can not corrupt the value of another.

>>> l = [1, 2, 3]
>>> a = attrs(foo=l)    # foo is a deepcopy of l
>>> obj4 = d()
>>> a.copy_into(obj4)   # obj4.foo is a deepcopy of a.foo
>>> a.foo.append(4)
>>> obj5 = d()
>>> a.copy_into(obj5)   # obj5.foo is a second deepcopy of a.foo
>>> obj4.foo.append(5)
>>> obj5.foo.append(6)
>>> obj4.foo
[1, 2, 3, 5]
>>> obj5.foo
[1, 2, 3, 4, 6]
>>> a.foo
[1, 2, 3, 4]
>>> l
[1, 2, 3]

Modifiers

Normally, the attrs attributes replace any attributes of the same name already in the class or object.

>>> a = attrs(foo=1, bar=2)
>>> obj6 = d()
>>> obj6.bar = 400
>>> obj6.baz = 500
>>> a.copy_into(obj6)
>>> obj6.foo        # set by a.copy_into
1
>>> obj6.bar        # overridden by a.copy_into
2
>>> obj6.baz        # untouched by a.copy_into
500

But attrs may also have modifiers as attribute values. When these are copied into a class or object, they modify any pre-existing value.

Two of these operate on tuples: extend and remove.

>>> from flask.ext.dry import extend, remove
>>> a = attrs(foo=extend(4, 5), bar=remove(4, 6))
>>> obj5 = d()
>>> obj5.foo = (1, 2, 3)
>>> obj5.bar = (4, 5, 6)
>>> a.copy_into(obj5)
>>> obj5.foo
(1, 2, 3, 4, 5)
>>> obj5.bar
(5,)

Finally, lookup can be used to lookup a value at the time that the copy_into is done:

>>> from flask.ext.dry import lookup
>>> a = attrs(foo=lookup('x'), bar=lookup('y.z'))
>>> obj5 = d()
>>> obj5.x = 'first x value'
>>> obj5.y = d()
>>> obj5.y.z = 'first x.z value'
>>> a.copy_into(obj5)
>>> obj5.foo
'first x value'
>>> obj5.bar
'first x.z value'
>>> obj5.x = 'second x value'
>>> obj5.y.z = 'second x.z value'
>>> a.copy_into(obj5)
>>> obj5.foo
'second x value'
>>> obj5.bar
'second x.z value'

You may create your own modifiers by deriving from modifier. Read the source code to see how to do this.