Commit 5283e749 authored by Tres Seaver's avatar Tres Seaver

Merge pull request #4 from NextThought/use-py-proxy-base

Make PyContainedProxyBase inherit directly from AbstractPyProxyBase. 
parents 2ef11f47 542e966f
......@@ -6,6 +6,7 @@ env:
- TOXENV=pypy
- TOXENV=py33
- TOXENV=py34
- TOXENV=py27-pure-zodb
install:
- pip install tox
script:
......
......@@ -73,6 +73,8 @@ install_requires = [
'zope.size',
'zope.traversing>=4.0.0a1',
'zope.publisher',
'zope.proxy>=4.1.5',
'persistent>=4.1.0',
'BTrees'
]
if not is_pypy:
......
......@@ -12,57 +12,34 @@
#
##############################################################################
# This is very similar to the code in zope.proxy.__init__, but modified
# to work properly when extending Persistent.
import operator
import sys
from zope.proxy import PyNonOverridable
from zope.proxy import AbstractPyProxyBase
_MARKER = object()
from persistent import Persistent
def _special_name(name):
"attribute names we delegate to super for"
"attribute names we delegate to Persistent for"
return (name.startswith('_Persistent')
or name.startswith('_p_')
or name.startswith('_v_')
or name in PyContainedProxyBase.__slots__)
class PyContainedProxyBase(Persistent):
class PyContainedProxyBase(AbstractPyProxyBase, Persistent):
"""Persistent proxy
"""
__slots__ = ('_wrapped', '__parent__', '__name__')
__slots__ = ('_wrapped', '__parent__', '__name__', '__weakref__')
def __new__(cls, obj):
inst = Persistent.__new__(cls)
inst._wrapped = obj
inst = super(PyContainedProxyBase, cls).__new__(cls, obj)
inst.__parent__ = None
inst.__name__ = None
inst._Persistent__flags = None
return inst
def __init__(self, obj):
self._wrapped = obj
super(PyContainedProxyBase, self).__init__(obj)
self.__parent__ = None
self.__name__ = None
self._Persistent__flags = None
def __call__(self, *args, **kw):
return self._wrapped(*args, **kw)
def __repr__(self):
return repr(self._wrapped)
def __str__(self):
return str(self._wrapped)
def __unicode__(self):
return unicode(self._wrapped)
def __reduce__(self):
return (type(self),
......@@ -73,13 +50,8 @@ class PyContainedProxyBase(Persistent):
return self.__reduce__()
def __setstate__(self, state):
before = self._Persistent__flags
self.__parent__ = state[0]
self.__name__ = state[1]
# The C implementation doesn't set itself to changed
# when the state is loaded from the database,
# we take care to copy that behaviour
self._Persistent__flags = before
object.__setattr__(self, '__parent__', state[0])
object.__setattr__(self, '__name__', state[1])
def __getstate__(self):
return (self.__parent__, self.__name__)
......@@ -101,340 +73,25 @@ class PyContainedProxyBase(Persistent):
except AttributeError:
pass
def _p_accessed(self):
# The superclass has issues changing the MRU
# during initial serialization because we're not
# yet in the picklecache
try:
Persistent._p_accessed(self)
except KeyError:
pass
# Rich comparison protocol
def __lt__(self, other):
return self._wrapped < other
def __le__(self, other):
return self._wrapped <= other
def __eq__(self, other):
return self._wrapped == other
def __ne__(self, other):
return self._wrapped != other
def __gt__(self, other):
return self._wrapped > other
def __ge__(self, other):
return self._wrapped >= other
def __nonzero__(self):
return bool(self._wrapped)
__bool__ = __nonzero__ # Python3 compat
def __hash__(self):
return hash(self._wrapped)
# Attribute protocol
def __getattribute__(self, name):
if _special_name(name):
return super(PyContainedProxyBase, self).__getattribute__(name)
# Our own attribute names need to go to Persistent so we get
# activated
return Persistent.__getattribute__(self, name)
if name in ('__reduce__', '__reduce_ex__', '__getstate__', '__setstate__', '__getnewargs__'):
return object.__getattribute__(self, name)
# Only access this if we need it, otherwise persistence problems
if name == '_wrapped':
return super(PyContainedProxyBase, self).__getattribute__('_wrapped')
try:
mine = super(PyContainedProxyBase, self).__getattribute__(name)
except AttributeError:
mine = _MARKER
else: # pragma NO COVER PyPy
# PyPy returns non-slot attributes for some reason, so we have to
# doctor things up a bit.
# if (PYPY and
# name in ('__providedBy__', '__provides__', '__implemented__')):
# mine = _MARKER
if isinstance(mine, PyNonOverridable):
return mine.desc.__get__(self)
try:
try:
wrapped = super(PyContainedProxyBase, self).__getattribute__('_wrapped')
except KeyError:
# During commit time of a persistent transaction, we can
# be in the state where we have an oid, but we are not actually
# in the picklecache yet. This causes a KeyError when the superclass
# tries to use _p_accessed; fortunately, it's ignorable as we
# know we are active
wrapped = object.__getattribute__(self, '_wrapped')
return getattr(wrapped, name)
except AttributeError:
if mine is not _MARKER:
return mine
raise
def __getattr__(self, name):
return getattr(self._wrapped, name)
return super(PyContainedProxyBase,self).__getattribute__(name)
def __setattr__(self, name, value):
if _special_name(name):
return super(PyContainedProxyBase, self).__setattr__(name, value)
try:
super(PyContainedProxyBase, self).__getattribute__(name)
except AttributeError:
return setattr(self._wrapped, name, value)
else:
return super(PyContainedProxyBase, self).__setattr__(name, value)
def __delattr__(self, name):
if name in PyContainedProxyBase.__slots__:
raise AttributeError()
delattr(self._wrapped, name)
# Container protocols
def __len__(self):
return len(self._wrapped)
def __getitem__(self, key):
if isinstance(key, slice):
if isinstance(self._wrapped, (list, tuple)):
return self._wrapped[key]
start, stop = key.start, key.stop
if start is None:
start = 0
if start < 0:
start += len(self._wrapped)
if stop is None:
stop = sys.maxint
if stop < 0:
stop += len(self._wrapped)
return operator.getslice(self._wrapped, start, stop)
return self._wrapped[key]
def __setitem__(self, key, value):
self._wrapped[key] = value
def __delitem__(self, key):
del self._wrapped[key]
def __iter__(self):
# This handles a custom __iter__ and generator support at the same
# time.
return iter(self._wrapped)
def next(self):
# Called when we wrap an iterator itself.
return self._wrapped.next()
def __next__(self): # pragma NO COVER Python3
return self._wrapped.__next__()
# Python 2.7 won't let the C wrapper support __reversed__ :(
# def __reversed__(self):
# return reversed(self._wrapped)
def __contains__(self, item):
return item in self._wrapped
# Numeric protocol: unary operators
def __neg__(self):
return -self._wrapped
def __pos__(self):
return +self._wrapped
def __abs__(self):
return abs(self._wrapped)
def __invert__(self):
return ~self._wrapped
# Numeric protocol: unary conversions
def __complex__(self):
return complex(self._wrapped)
def __int__(self):
return int(self._wrapped)
def __long__(self):
return long(self._wrapped)
def __float__(self):
return float(self._wrapped)
def __oct__(self):
return oct(self._wrapped)
def __hex__(self):
return hex(self._wrapped)
def __index__(self):
return operator.index(self._wrapped)
# Numeric protocol: binary coercion
def __coerce__(self, other):
left, right = coerce(self._wrapped, other)
if left == self._wrapped and type(left) is type(self._wrapped):
left = self
return left, right
# Numeric protocol: binary arithmetic operators
def __add__(self, other):
return self._wrapped + other
def __sub__(self, other):
return self._wrapped - other
def __mul__(self, other):
return self._wrapped * other
def __floordiv__(self, other):
return self._wrapped // other
def __truediv__(self, other): # pragma NO COVER
# Only one of __truediv__ and __div__ is meaningful at any one time.
return self._wrapped / other
def __div__(self, other): # pragma NO COVER
# Only one of __truediv__ and __div__ is meaningful at any one time.
return self._wrapped / other
def __mod__(self, other):
return self._wrapped % other
def __divmod__(self, other):
return divmod(self._wrapped, other)
def __pow__(self, other, modulus=None):
if modulus is None:
return pow(self._wrapped, other)
return pow(self._wrapped, other, modulus)
def __radd__(self, other):
return other + self._wrapped
def __rsub__(self, other):
return other - self._wrapped
def __rmul__(self, other):
return other * self._wrapped
def __rfloordiv__(self, other):
return other // self._wrapped
def __rtruediv__(self, other): # pragma NO COVER
# Only one of __rtruediv__ and __rdiv__ is meaningful at any one time.
return other / self._wrapped
def __rdiv__(self, other): # pragma NO COVER
# Only one of __rtruediv__ and __rdiv__ is meaningful at any one time.
return other / self._wrapped
def __rmod__(self, other):
return other % self._wrapped
def __rdivmod__(self, other):
return divmod(other, self._wrapped)
def __rpow__(self, other, modulus=None):
if modulus is None:
return pow(other, self._wrapped)
# We can't actually get here, because we can't lie about our type()
return pow(other, self._wrapped, modulus) # pragma NO COVER
# Numeric protocol: binary bitwise operators
def __lshift__(self, other):
return self._wrapped << other
def __rshift__(self, other):
return self._wrapped >> other
def __and__(self, other):
return self._wrapped & other
def __xor__(self, other):
return self._wrapped ^ other
def __or__(self, other):
return self._wrapped | other
def __rlshift__(self, other):
return other << self._wrapped
def __rrshift__(self, other):
return other >> self._wrapped
def __rand__(self, other):
return other & self._wrapped
def __rxor__(self, other):
return other ^ self._wrapped
def __ror__(self, other):
return other | self._wrapped
# Numeric protocol: binary in-place operators
def __iadd__(self, other):
self._wrapped += other
return self
def __isub__(self, other):
self._wrapped -= other
return self
def __imul__(self, other):
self._wrapped *= other
return self
def __idiv__(self, other): # pragma NO COVER
# Only one of __itruediv__ and __idiv__ is meaningful at any one time.
self._wrapped /= other
return self
def __itruediv__(self, other): # pragma NO COVER
# Only one of __itruediv__ and __idiv__ is meaningful at any one time.
self._wrapped /= other
return self
def __ifloordiv__(self, other):
self._wrapped //= other
return self
def __imod__(self, other):
self._wrapped %= other
return self
def __ilshift__(self, other):
self._wrapped <<= other
return self
def __irshift__(self, other):
self._wrapped >>= other
return self
def __iand__(self, other):
self._wrapped &= other
return self
def __ixor__(self, other):
self._wrapped ^= other
return self
def __ior__(self, other):
self._wrapped |= other
return self
# Our own attribute names need to go directly to Persistent
# so that _p_changed gets set, in addition to the _p values themselves
return Persistent.__setattr__(self, name, value)
def __ipow__(self, other, modulus=None):
if modulus is None:
self._wrapped **= other
else: # pragma NO COVER
# There is no syntax which triggers in-place pow w/ modulus
self._wrapped = pow(self._wrapped, other, modulus)
return self
return super(PyContainedProxyBase, self).__setattr__(name, value)
def py_getProxiedObject(obj):
......
......@@ -62,7 +62,8 @@ def test_basic_persistent_w_non_persistent_proxied():
>>> p2._p_changed
0
>>> p2._p_deactivate()
>>> p2._p_changed
>>> bool(p2._p_changed)
False
>>> p2.__name__
'test'
......@@ -188,7 +189,7 @@ def test_proxy_cache_interaction():
We've also accessed the root object. If we garbage-collect the
cache:
>>> conn._cache.incrgc()
>>> _ = conn._cache.incrgc()
Then the root object will still be active, because it was accessed
recently:
......@@ -204,7 +205,8 @@ def test_proxy_cache_interaction():
But it's a ghost:
>>> conn.root()['p']._p_changed
>>> bool(conn.root()['p']._p_changed)
False
If we deactivate the root object:
......
[tox]
envlist = py26,py27,pypy,py33,py34,docs
envlist = py26,py27,pypy,py33,py34,py27-zodb,pypy-zodb,py27-pure-zodb,docs
[testenv]
commands = python setup.py -q test -q
......@@ -23,6 +23,29 @@ deps =
zope.testrunner
zope.testing
[testenv:py27-zodb]
basepython =
python2.7
deps =
{[testenv]deps}
ZODB
[testenv:py27-pure-zodb]
basepython =
python2.7
setenv =
PURE_PYTHON = 1
deps =
{[testenv]deps}
ZODB
[testenv:pypy-zodb]
basepython =
pypy
deps =
{[testenv]deps}
ZODB
[testenv:coverage]
usedevelop = true
basepython =
......@@ -38,7 +61,7 @@ deps =
[testenv:docs]
basepython =
python2.7
commands =
commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
deps =
......
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