Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
persistent
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
persistent
Commits
590ae47e
Commit
590ae47e
authored
Jun 28, 2012
by
Tres Seaver
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert attribut hook doctests to unittests + Sphinx API docs.
parent
cbf4700b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
318 additions
and
258 deletions
+318
-258
docs/api.rst
docs/api.rst
+1
-0
docs/api/attributes.rst
docs/api/attributes.rst
+311
-0
persistent/tests/test_overriding_attrs.py
persistent/tests/test_overriding_attrs.py
+6
-258
No files found.
docs/api.rst
View file @
590ae47e
...
...
@@ -5,4 +5,5 @@
:maxdepth: 2
api/interfaces
api/attributes
api/pickling
docs/api/attributes.rst
0 → 100644
View file @
590ae47e
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
persistent/tests/test_overriding_attrs.py
View file @
590ae47e
...
...
@@ -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->
#
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment