Commit 590ae47e authored by Tres Seaver's avatar Tres Seaver

Convert attribut hook doctests to unittests + Sphinx API docs.

parent cbf4700b
......@@ -5,4 +5,5 @@
:maxdepth: 2
api/interfaces
api/attributes
api/pickling
Customizing Attribute Access
============================
Hooking :meth:`__getattr__`
---------------------------
The __getattr__ method works pretty much the same for persistent
classes as it does for other classes. No special handling is
needed. If an object is a ghost, then it will be activated before
__getattr__ is called.
In this example, our objects returns a tuple with the attribute
name, converted to upper case and the value of _p_changed, for any
attribute that isn't handled by the default machinery.
.. doctest::
>>> from persistent.tests.test_overriding_attrs import OverridesGetattr
>>> o = OverridesGetattr()
>>> o._p_changed
False
>>> o._p_oid
>>> o._p_jar
>>> o.spam
('SPAM', False)
>>> o.spam = 1
>>> o.spam
1
We'll save the object, so it can be deactivated:
.. doctest::
>>> from persistent.tests.test_overriding_attrs import _resettingJar
>>> jar = _resettingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
And now, if we ask for an attribute it doesn't have,
.. doctest::
>>> o.eggs
('EGGS', False)
And we see that the object was activated before calling the
:meth:`__getattr__` method.
Hooking All Access
------------------
In this example, we'll provide an example that shows how to
override the :meth:`__getattribute__`, :meth:`__setattr__`, and
:meth:`__delattr__` methods. We'll create a class that stores it's
attributes in a secret dictionary within the instance dictionary.
The class will have the policy that variables with names starting
with ``tmp_`` will be volatile.
Our sample class takes initial values as keyword arguments to the constructor:
.. doctest::
>>> from persistent.tests.test_overriding_attrs import VeryPrivate
>>> o = VeryPrivate(x=1)
Hooking :meth:`__getattribute__``
#################################
The :meth:`__getattribute__` method is called for all attribute
accesses. It overrides the attribute access support inherited
from Persistent.
.. doctest::
>>> o._p_changed
False
>>> o._p_oid
>>> o._p_jar
>>> o.x
1
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
Next, we'll save the object in a database so that we can deactivate it:
.. doctest::
>>> from persistent.tests.test_overriding_attrs import _rememberingJar
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
And we'll get some data:
.. doctest::
>>> o.x
1
which activates the object:
.. doctest::
>>> o._p_changed
False
It works for missing attribes too:
.. doctest::
>>> o._p_deactivate()
>>> o._p_changed
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
>>> o._p_changed
False
Hooking :meth:`__setattr__``
############################
The :meth:`__setattr__` method is called for all attribute
assignments. It overrides the attribute assignment support
inherited from Persistent.
Implementors of :meth:`__setattr__` methods:
1. Must call Persistent._p_setattr first to allow it
to handle some attributes and to make sure that the object
is activated if necessary, and
2. Must set _p_changed to mark objects as changed.
.. doctest::
>>> o = VeryPrivate()
>>> o._p_changed
False
>>> o._p_oid
>>> o._p_jar
>>> o.x
Traceback (most recent call last):
...
AttributeError: x
>>> o.x = 1
>>> o.x
1
Because the implementation doesn't store attributes directly
in the instance dictionary, we don't have a key for the attribute:
.. doctest::
>>> 'x' in o.__dict__
False
Next, we'll give the object a "remembering" jar so we can
deactivate it:
.. doctest::
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
We'll modify an attribute
.. doctest::
>>> o.y = 2
>>> o.y
2
which reactivates it, and markes it as modified, because our
implementation marked it as modified:
.. doctest::
>>> o._p_changed
True
Now, if fake a commit:
.. doctest::
>>> jar.fake_commit()
>>> o._p_changed
False
And deactivate the object:
.. doctest::
>>> o._p_deactivate()
>>> o._p_changed
and then set a variable with a name starting with ``tmp_``,
The object will be activated, but not marked as modified,
because our :meth:`__setattr__` implementation doesn't mark the
object as changed if the name starts with ``tmp_``:
.. doctest::
>>> o.tmp_foo = 3
>>> o._p_changed
False
>>> o.tmp_foo
3
Hooking :meth:`__delattr__``
############################
The __delattr__ method is called for all attribute
deletions. It overrides the attribute deletion support
inherited from Persistent.
Implementors of :meth:`__delattr__` methods:
1. Must call Persistent._p_delattr first to allow it
to handle some attributes and to make sure that the object
is activated if necessary, and
2. Must set _p_changed to mark objects as changed.
.. doctest::
>>> o = VeryPrivate(x=1, y=2, tmp_z=3)
>>> o._p_changed
False
>>> o._p_oid
>>> o._p_jar
>>> o.x
1
>>> del o.x
>>> o.x
Traceback (most recent call last):
...
AttributeError: x
Next, we'll save the object in a jar so that we can
deactivate it:
.. doctest::
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
If we delete an attribute:
.. doctest::
>>> del o.y
The object is activated. It is also marked as changed because
our implementation marked it as changed.
.. doctest::
>>> o._p_changed
True
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
>>> o.tmp_z
3
Now, if fake a commit:
.. doctest::
>>> jar.fake_commit()
>>> o._p_changed
False
And deactivate the object:
.. doctest::
>>> o._p_deactivate()
>>> o._p_changed
and then delete a variable with a name starting with ``tmp_``,
The object will be activated, but not marked as modified,
because our :meth:`__delattr__` implementation doesn't mark the
object as changed if the name starts with ``tmp_``:
.. doctest::
>>> del o.tmp_z
>>> o._p_changed
False
>>> o.tmp_z
Traceback (most recent call last):
...
AttributeError: tmp_z
......@@ -13,11 +13,10 @@
##############################################################################
"""Overriding attr methods
This module tests and documents, through example, overriding attribute
access methods.
Examples for overriding attribute access methods.
"""
from persistent import Persistent # ouch!
from persistent import Persistent
def _resettingJar():
from persistent.tests.utils import ResettingJar
......@@ -27,47 +26,12 @@ def _rememberingJar():
from persistent.tests.utils import RememberingJar
return RememberingJar()
class SampleOverridingGetattr(Persistent):
class OverridesGetattr(Persistent):
"""Example of overriding __getattr__
"""
def __getattr__(self, name):
"""Get attributes that can't be gotten the usual way
The __getattr__ method works pretty much the same for persistent
classes as it does for other classes. No special handling is
needed. If an object is a ghost, then it will be activated before
__getattr__ is called.
In this example, our objects returns a tuple with the attribute
name, converted to upper case and the value of _p_changed, for any
attribute that isn't handled by the default machinery.
>>> o = SampleOverridingGetattr()
>>> o._p_changed
False
>>> o._p_oid
>>> o._p_jar
>>> o.spam
('SPAM', False)
>>> o.spam = 1
>>> o.spam
1
We'll save the object, so it can be deactivated:
>>> jar = _resettingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
And now, if we ask for an attribute it doesn't have,
>>> o.eggs
('EGGS', False)
And we see that the object was activated before calling the
__getattr__ method.
"""
# Don't pretend we have any special attributes.
if name.startswith("__") and name.endswrith("__"):
......@@ -75,78 +39,18 @@ class SampleOverridingGetattr(Persistent):
else:
return name.upper(), self._p_changed
class SampleOverridingGetattributeSetattrAndDelattr(Persistent):
"""Example of overriding __getattribute__, __setattr__, and __delattr__
In this example, we'll provide an example that shows how to
override the __getattribute__, __setattr__, and __delattr__
methods. We'll create a class that stores it's attributes in a
secret dictionary within it's instance dictionary.
The class will have the policy that variables with names starting
with 'tmp_' will be volatile.
class VeryPrivate(Persistent):
"""Example of overriding __getattribute__, __setattr__, and __delattr__
"""
def __init__(self, **kw):
self.__dict__['__secret__'] = kw.copy()
def __getattribute__(self, name):
"""Get an attribute value
The __getattribute__ method is called for all attribute
accesses. It overrides the attribute access support inherited
from Persistent.
Our sample class let's us provide initial values as keyword
arguments to the constructor:
>>> o = SampleOverridingGetattributeSetattrAndDelattr(x=1)
>>> o._p_changed
0
>>> o._p_oid
>>> o._p_jar
>>> o.x
1
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
Next, we'll save the object in a database so that we can
deactivate it:
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
And we'll get some data:
>>> o.x
1
which activates the object:
>>> o._p_changed
0
It works for missing attribes too:
>>> o._p_deactivate()
>>> o._p_changed
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
>>> o._p_changed
0
See the very important note in the comment below!
"""
#################################################################
# IMPORTANT! READ THIS! 8->
#
......@@ -177,84 +81,7 @@ class SampleOverridingGetattributeSetattrAndDelattr(Persistent):
def __setattr__(self, name, value):
"""Set an attribute value
The __setattr__ method is called for all attribute
assignments. It overrides the attribute assignment support
inherited from Persistent.
Implementors of __setattr__ methods:
1. Must call Persistent._p_setattr first to allow it
to handle some attributes and to make sure that the object
is activated if necessary, and
2. Must set _p_changed to mark objects as changed.
See the comments in the source below.
>>> o = SampleOverridingGetattributeSetattrAndDelattr()
>>> o._p_changed
0
>>> o._p_oid
>>> o._p_jar
>>> o.x
Traceback (most recent call last):
...
AttributeError: x
>>> o.x = 1
>>> o.x
1
Because the implementation doesn't store attributes directly
in the instance dictionary, we don't have a key for the attribute:
>>> 'x' in o.__dict__
False
Next, we'll give the object a "remembering" jar so we can
deactivate it:
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
We'll modify an attribute
>>> o.y = 2
>>> o.y
2
which reactivates it, and markes it as modified, because our
implementation marked it as modified:
>>> o._p_changed
1
Now, if fake a commit:
>>> jar.fake_commit()
>>> o._p_changed
0
And deactivate the object:
>>> o._p_deactivate()
>>> o._p_changed
and then set a variable with a name starting with 'tmp_',
The object will be activated, but not marked as modified,
because our __setattr__ implementation doesn't mark the
object as changed if the name starts with 'tmp_':
>>> o.tmp_foo = 3
>>> o._p_changed
0
>>> o.tmp_foo
3
"""
#################################################################
# IMPORTANT! READ THIS! 8->
#
......@@ -276,86 +103,7 @@ class SampleOverridingGetattributeSetattrAndDelattr(Persistent):
def __delattr__(self, name):
"""Delete an attribute value
The __delattr__ method is called for all attribute
deletions. It overrides the attribute deletion support
inherited from Persistent.
Implementors of __delattr__ methods:
1. Must call Persistent._p_delattr first to allow it
to handle some attributes and to make sure that the object
is activated if necessary, and
2. Must set _p_changed to mark objects as changed.
See the comments in the source below.
>>> o = SampleOverridingGetattributeSetattrAndDelattr(
... x=1, y=2, tmp_z=3)
>>> o._p_changed
0
>>> o._p_oid
>>> o._p_jar
>>> o.x
1
>>> del o.x
>>> o.x
Traceback (most recent call last):
...
AttributeError: x
Next, we'll save the object in a jar so that we can
deactivate it:
>>> jar = _rememberingJar()
>>> jar.add(o)
>>> o._p_deactivate()
>>> o._p_changed
If we delete an attribute:
>>> del o.y
The object is activated. It is also marked as changed because
our implementation marked it as changed.
>>> o._p_changed
1
>>> o.y
Traceback (most recent call last):
...
AttributeError: y
>>> o.tmp_z
3
Now, if fake a commit:
>>> jar.fake_commit()
>>> o._p_changed
0
And deactivate the object:
>>> o._p_deactivate()
>>> o._p_changed
and then delete a variable with a name starting with 'tmp_',
The object will be activated, but not marked as modified,
because our __delattr__ implementation doesn't mark the
object as changed if the name starts with 'tmp_':
>>> del o.tmp_z
>>> o._p_changed
0
>>> o.tmp_z
Traceback (most recent call last):
...
AttributeError: tmp_z
"""
#################################################################
# IMPORTANT! READ THIS! 8->
#
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment