Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Acquisition
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
Acquisition
Commits
8a77d12c
Commit
8a77d12c
authored
Mar 30, 2015
by
Tres Seaver
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'pypy-py3k' of
git://github.com/NextThought/Acquisition
into NextThought-pypy-py3k
parents
2667b34d
ba35cf6f
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1435 additions
and
100 deletions
+1435
-100
.travis.yml
.travis.yml
+2
-0
CHANGES.rst
CHANGES.rst
+7
-2
README.rst
README.rst
+7
-7
setup.py
setup.py
+24
-8
src/Acquisition/__init__.py
src/Acquisition/__init__.py
+830
-3
src/Acquisition/tests.py
src/Acquisition/tests.py
+565
-80
No files found.
.travis.yml
View file @
8a77d12c
...
@@ -3,6 +3,8 @@ sudo: false
...
@@ -3,6 +3,8 @@ sudo: false
python
:
python
:
-
2.6
-
2.6
-
2.7
-
2.7
-
3.4
-
pypy
install
:
install
:
-
python bootstrap.py
-
python bootstrap.py
-
bin/buildout
-
bin/buildout
...
...
CHANGES.rst
View file @
8a77d12c
Changelog
Changelog
=========
=========
4.2 (unreleased)
----------------
- Add support for PyPy and Python 3.
4.1 (2014-12-18)
4.1 (2014-12-18)
----------------
----------------
...
@@ -62,7 +67,7 @@ Changelog
...
@@ -62,7 +67,7 @@ Changelog
- Add ``aq_explicit`` to ``IAcquisitionWrapper``.
- Add ``aq_explicit`` to ``IAcquisitionWrapper``.
- Fixed bug: ``unicode(wrapped)`` was not calling a ``__unicode__``
- Fixed bug: ``unicode(wrapped)`` was not calling a ``__unicode__``
method on wrapped objects.
method on wrapped objects.
2.13.5 (2010-09-29)
2.13.5 (2010-09-29)
...
@@ -133,7 +138,7 @@ Changelog
...
@@ -133,7 +138,7 @@ Changelog
2.12.2 (2009-08-02)
2.12.2 (2009-08-02)
-------------------
-------------------
- Fixed 64-bit compatibility issues for Python 2.5.x / 2.6.x. See
- Fixed 64-bit compatibility issues for Python 2.5.x / 2.6.x. See
http://www.python.org/dev/peps/pep-0353/ for details.
http://www.python.org/dev/peps/pep-0353/ for details.
2.12.1 (2009-04-15)
2.12.1 (2009-04-15)
...
...
README.rst
View file @
8a77d12c
...
@@ -34,7 +34,7 @@ class. For example::
...
@@ -34,7 +34,7 @@ class. For example::
>>> class A(Acquisition.Implicit):
>>> class A(Acquisition.Implicit):
... def report(self):
... def report(self):
... print
self.color
... print
(self.color)
...
...
>>> a = A()
>>> a = A()
>>> c = C()
>>> c = C()
...
@@ -107,7 +107,7 @@ When explicit acquisition is used, attributes are not automatically
...
@@ -107,7 +107,7 @@ When explicit acquisition is used, attributes are not automatically
obtained from the environment. Instead, the method aq_acquire must be
obtained from the environment. Instead, the method aq_acquire must be
used. For example::
used. For example::
>>> print
c.a.aq_acquire('color'
)
>>> print
(c.a.aq_acquire('color')
)
red
red
To support explicit acquisition, your class should inherit from the
To support explicit acquisition, your class should inherit from the
...
@@ -178,7 +178,7 @@ Here's an example::
...
@@ -178,7 +178,7 @@ Here's an example::
>>> class E(Explicit, HandyForTesting): pass
>>> class E(Explicit, HandyForTesting): pass
...
...
>>> class Nice(HandyForTesting):
>>> class Nice(HandyForTesting):
... isNice = 1
... isNice = 1
... def __str__(self):
... def __str__(self):
... return HandyForTesting.__str__(self)+' and I am nice!'
... return HandyForTesting.__str__(self)+' and I am nice!'
... __repr__ = __str__
... __repr__ = __str__
...
@@ -192,7 +192,7 @@ Here's an example::
...
@@ -192,7 +192,7 @@ Here's an example::
>>> def find_nice(self, ancestor, name, object, extra):
>>> def find_nice(self, ancestor, name, object, extra):
... return hasattr(object,'isNice') and object.isNice
... return hasattr(object,'isNice') and object.isNice
>>> print
a.b.c.aq_acquire('p', find_nice
)
>>> print
(a.b.c.aq_acquire('p', find_nice)
)
spam(Nice) and I am nice!
spam(Nice) and I am nice!
The filtered acquisition in the last line skips over the first
The filtered acquisition in the last line skips over the first
...
@@ -221,7 +221,7 @@ method. For example::
...
@@ -221,7 +221,7 @@ method. For example::
>>> a = C()
>>> a = C()
>>> b = C()
>>> b = C()
>>> a.color = "red"
>>> a.color = "red"
>>> print
b.__of__(a).color
>>> print
(b.__of__(a).color)
red
red
In this case, ``a`` does not contain ``b``, but it is put in ``b``'s
In this case, ``a`` does not contain ``b``, but it is put in ``b``'s
...
@@ -241,7 +241,7 @@ acquisition context that includes non-container objects::
...
@@ -241,7 +241,7 @@ acquisition context that includes non-container objects::
>>> a.b.color = "red"
>>> a.b.color = "red"
>>> a.x = C("x")
>>> a.x = C("x")
>>> print
a.b.x.color
>>> print
(a.b.x.color)
red
red
Even though ``b`` does not contain ``x``, ``x`` can acquire the color
Even though ``b`` does not contain ``x``, ``x`` can acquire the color
...
@@ -262,7 +262,7 @@ If in the example above suppose both a and b have an color attribute::
...
@@ -262,7 +262,7 @@ If in the example above suppose both a and b have an color attribute::
>>> a.b.color = "red"
>>> a.b.color = "red"
>>> a.x = C("x")
>>> a.x = C("x")
>>> print
a.b.x.color
>>> print
(a.b.x.color)
green
green
Why does ``a.b.x.color`` acquire color from ``a`` and not from ``b``?
Why does ``a.b.x.color`` acquire color from ``a`` and not from ``b``?
...
...
setup.py
View file @
8a77d12c
...
@@ -14,6 +14,8 @@
...
@@ -14,6 +14,8 @@
"""Setup for the Acquisition distribution
"""Setup for the Acquisition distribution
"""
"""
import
os
import
os
import
platform
import
sys
from
setuptools
import
setup
,
find_packages
,
Extension
from
setuptools
import
setup
,
find_packages
,
Extension
with
open
(
'README.rst'
)
as
f
:
with
open
(
'README.rst'
)
as
f
:
...
@@ -22,9 +24,24 @@ with open('README.rst') as f:
...
@@ -22,9 +24,24 @@ with open('README.rst') as f:
with
open
(
'CHANGES.rst'
)
as
f
:
with
open
(
'CHANGES.rst'
)
as
f
:
CHANGES
=
f
.
read
()
CHANGES
=
f
.
read
()
# PyPy won't build the extension.
py_impl
=
getattr
(
platform
,
'python_implementation'
,
lambda
:
None
)
is_pypy
=
py_impl
()
==
'PyPy'
is_pure
=
'PURE_PYTHON'
in
os
.
environ
py3k
=
sys
.
version_info
>=
(
3
,
)
if
is_pypy
or
is_pure
or
py3k
:
ext_modules
=
[]
else
:
ext_modules
=
[
Extension
(
"Acquisition._Acquisition"
,
[
os
.
path
.
join
(
'src'
,
'Acquisition'
,
'_Acquisition.c'
)],
include_dirs
=
[
'include'
,
'src'
]),
]
setup
(
setup
(
name
=
'Acquisition'
,
name
=
'Acquisition'
,
version
=
'4.
1
'
,
version
=
'4.
2.dev0
'
,
url
=
'https://github.com/zopefoundation/Acquisition'
,
url
=
'https://github.com/zopefoundation/Acquisition'
,
license
=
'ZPL 2.1'
,
license
=
'ZPL 2.1'
,
description
=
"Acquisition is a mechanism that allows objects to obtain "
description
=
"Acquisition is a mechanism that allows objects to obtain "
...
@@ -41,18 +58,17 @@ setup(
...
@@ -41,18 +58,17 @@ setup(
"License :: OSI Approved :: Zope Public License"
,
"License :: OSI Approved :: Zope Public License"
,
"Operating System :: OS Independent"
,
"Operating System :: OS Independent"
,
"Programming Language :: Python"
,
"Programming Language :: Python"
,
"Programming Language :: Python :: 2
:: Only
"
,
"Programming Language :: Python :: 2"
,
"Programming Language :: Python :: 2.6"
,
"Programming Language :: Python :: 2.6"
,
"Programming Language :: Python :: 2.7"
,
"Programming Language :: Python :: 2.7"
,
"Programming Language :: Python :: 3"
,
"Programming Language :: Python :: 3.4"
,
"Programming Language :: Python :: Implementation :: CPython"
,
"Programming Language :: Python :: Implementation :: CPython"
,
"Programming Language :: Python :: Implementation :: PyPy"
,
],
],
ext_modules
=
[
Extension
(
"Acquisition._Acquisition"
,
ext_modules
=
ext_modules
,
[
os
.
path
.
join
(
'src'
,
'Acquisition'
,
'_Acquisition.c'
)],
include_dirs
=
[
'include'
,
'src'
]),
],
install_requires
=
[
install_requires
=
[
'ExtensionClass >= 4.1
a
1'
,
'ExtensionClass >= 4.1
.
1'
,
'zope.interface'
,
'zope.interface'
,
],
],
include_package_data
=
True
,
include_package_data
=
True
,
...
...
src/Acquisition/__init__.py
View file @
8a77d12c
from
__future__
import
absolute_import
,
print_function
# pylint:disable=W0212,R0911,R0912
import
os
import
operator
import
sys
import
types
import
ExtensionClass
from
zope.interface
import
classImplements
from
zope.interface
import
classImplements
from
_Acquisition
import
*
from
.interfaces
import
IAcquirer
from
interfaces
import
IAcquirer
from
.interfaces
import
IAcquisitionWrapper
from
interfaces
import
IAcquisitionWrapper
class
Acquired
(
object
):
"Marker for explicit acquisition"
_NOT_FOUND
=
object
()
# marker
###
# Helper functions
###
def
_has__of__
(
obj
):
"""Check whether an object has an __of__ method for returning itself
in the context of a container."""
# It is necessary to check both the type (or we get into cycles)
# as well as the presence of the method (or mixins of Base pre- or
# post-class-creation as done in, e.g.,
# zopefoundation/Persistence) can fail.
return
isinstance
(
obj
,
ExtensionClass
.
Base
)
and
hasattr
(
type
(
obj
),
'__of__'
)
def
_apply_filter
(
predicate
,
inst
,
name
,
result
,
extra
,
orig
):
return
predicate
(
orig
,
inst
,
name
,
result
,
extra
)
if
sys
.
version_info
<
(
3
,):
def
_rebound_method
(
method
,
wrapper
):
"""Returns a version of the method with self bound to `wrapper`"""
if
isinstance
(
method
,
types
.
MethodType
):
method
=
types
.
MethodType
(
method
.
im_func
,
wrapper
,
method
.
im_class
)
return
method
else
:
def
_rebound_method
(
method
,
wrapper
):
"""Returns a version of the method with self bound to `wrapper`"""
if
isinstance
(
method
,
types
.
MethodType
):
method
=
types
.
MethodType
(
method
.
__func__
,
wrapper
)
return
method
###
# Wrapper object protocol, mostly ported from C directly
###
def
_Wrapper_findspecial
(
wrapper
,
name
):
"""
Looks up the special acquisition attributes of an object.
:param str name: The attribute to find, with 'aq' already stripped.
"""
result
=
_NOT_FOUND
if
name
==
'base'
:
result
=
wrapper
.
_obj
while
isinstance
(
result
,
_Wrapper
)
and
result
.
_obj
is
not
None
:
result
=
result
.
_obj
elif
name
==
'parent'
:
result
=
wrapper
.
_container
elif
name
==
'self'
:
result
=
wrapper
.
_obj
elif
name
==
'explicit'
:
if
type
(
wrapper
).
_IS_IMPLICIT
:
result
=
ExplicitAcquisitionWrapper
(
wrapper
.
_obj
,
wrapper
.
_container
)
else
:
result
=
wrapper
elif
name
==
'acquire'
:
result
=
object
.
__getattribute__
(
wrapper
,
'aq_acquire'
)
elif
name
==
'chain'
:
# XXX: C has a second implementation here
result
=
aq_chain
(
wrapper
)
elif
name
==
'inContextOf'
:
result
=
object
.
__getattribute__
(
wrapper
,
'aq_inContextOf'
)
elif
name
==
'inner'
:
# XXX: C has a second implementation here
result
=
aq_inner
(
wrapper
)
elif
name
==
'uncle'
:
result
=
'Bob'
return
result
def
_Wrapper_acquire
(
wrapper
,
name
,
predicate
=
None
,
predicate_extra
=
None
,
orig_object
=
None
,
explicit
=
True
,
containment
=
True
):
"""
Attempt to acquire the `name` from the parent of the wrapper.
:raises AttributeError: If the wrapper has no parent or the attribute cannot
be found.
"""
if
wrapper
.
_container
is
None
:
raise
AttributeError
(
name
)
search_self
=
True
search_parent
=
True
# If the container has an acquisition wrapper itself, we'll use
# _Wrapper_findattr to progress further
if
isinstance
(
wrapper
.
_container
,
_Wrapper
):
if
isinstance
(
wrapper
.
_obj
,
_Wrapper
):
# try to optimize search by recognizing repeated objects in path
if
wrapper
.
_obj
.
_container
is
wrapper
.
_container
.
_container
:
search_parent
=
False
elif
wrapper
.
_obj
.
_container
is
wrapper
.
_container
.
_obj
:
search_self
=
False
# Don't search the container when the container of the container
# is the same object as `wrapper`
if
wrapper
.
_container
.
_container
is
wrapper
.
_obj
:
search_parent
=
False
containment
=
True
result
=
_Wrapper_findattr
(
wrapper
.
_container
,
name
,
predicate
=
predicate
,
predicate_extra
=
predicate_extra
,
orig_object
=
orig_object
,
search_self
=
search_self
,
search_parent
=
search_parent
,
explicit
=
explicit
,
containment
=
containment
)
# XXX: Why does this branch of the C code check __of__, but the next one
# doesn't?
if
_has__of__
(
result
):
result
=
result
.
__of__
(
wrapper
)
return
result
# If the container has a __parent__ pointer, we create an
# acquisition wrapper for it accordingly. Then we can proceed
# with Wrapper_findattr, just as if the container had an
# acquisition wrapper in the first place (see above).
# NOTE: This mutates the wrapper
elif
hasattr
(
wrapper
.
_container
,
'__parent__'
):
parent
=
wrapper
.
_container
.
__parent__
# Don't search the container when the parent of the parent
# is the same object as 'self'
if
parent
is
wrapper
.
_obj
:
search_parent
=
False
elif
isinstance
(
parent
,
_Wrapper
)
and
parent
.
_obj
is
wrapper
.
_obj
:
# XXX: C code just does parent._obj, assumes its a wrapper
search_parent
=
False
wrapper
.
_container
=
ImplicitAcquisitionWrapper
(
wrapper
.
_container
,
parent
)
return
_Wrapper_findattr
(
wrapper
.
_container
,
name
,
predicate
=
predicate
,
predicate_extra
=
predicate_extra
,
orig_object
=
orig_object
,
search_self
=
search_self
,
search_parent
=
search_parent
,
explicit
=
explicit
,
containment
=
containment
)
else
:
# The container is the end of the acquisition chain; if we
# can't look up the attributes here, we can't look it up at all
result
=
getattr
(
wrapper
.
_container
,
name
)
if
result
is
not
Acquired
:
if
predicate
:
if
_apply_filter
(
predicate
,
wrapper
.
_container
,
name
,
result
,
predicate_extra
,
orig_object
):
if
_has__of__
(
result
):
result
=
result
.
__of__
(
wrapper
)
return
result
else
:
raise
AttributeError
(
name
)
else
:
if
_has__of__
(
result
):
result
=
result
.
__of__
(
wrapper
)
return
result
raise
AttributeError
(
name
)
def
_Wrapper_findattr
(
wrapper
,
name
,
predicate
=
None
,
predicate_extra
=
None
,
orig_object
=
None
,
search_self
=
True
,
search_parent
=
True
,
explicit
=
True
,
containment
=
True
):
"""
Search the `wrapper` object for the attribute `name`.
:param bool search_self: Search `wrapper.aq_self` for the attribute.
:param bool search_parent: Search `wrapper.aq_parent` for the attribute.
:param bool explicit: Explicitly acquire the attribute from the parent
(should be assumed with implicit wrapper)
:param bool containment: Use the innermost wrapper (`aq_inner`) for looking up
the attribute.
"""
orig_name
=
name
if
orig_object
is
None
:
orig_object
=
wrapper
# First, special names
if
name
.
startswith
(
'aq'
)
or
name
==
'__parent__'
:
# __parent__ is an alias of aq_parent
if
name
==
'__parent__'
:
name
=
'parent'
else
:
name
=
name
[
3
:]
result
=
_Wrapper_findspecial
(
wrapper
,
name
)
if
result
is
not
_NOT_FOUND
:
if
predicate
:
return
result
if
_apply_filter
(
predicate
,
wrapper
,
orig_name
,
result
,
predicate_extra
,
orig_object
)
else
None
return
result
elif
name
in
(
'__reduce__'
,
'__reduce_ex__'
,
'__getstate__'
,
'__of__'
,
'__cmp__'
,
'__eq__'
,
'__ne__'
,
'__lt__'
,
'__le__'
,
'__gt__'
,
'__ge__'
):
return
object
.
__getattribute__
(
wrapper
,
orig_name
)
# If we're doing a containment search, replace the wrapper with aq_inner
if
containment
:
while
isinstance
(
wrapper
.
_obj
,
_Wrapper
):
wrapper
=
wrapper
.
_obj
if
search_self
and
wrapper
.
_obj
is
not
None
:
if
isinstance
(
wrapper
.
_obj
,
_Wrapper
):
if
wrapper
is
wrapper
.
_obj
:
raise
RuntimeError
(
"Recursion detected in acquisition wrapper"
)
try
:
result
=
_Wrapper_findattr
(
wrapper
.
_obj
,
orig_name
,
predicate
=
predicate
,
predicate_extra
=
predicate_extra
,
orig_object
=
orig_object
,
search_self
=
True
,
search_parent
=
explicit
or
isinstance
(
wrapper
.
_obj
,
ImplicitAcquisitionWrapper
),
explicit
=
explicit
,
containment
=
containment
)
if
isinstance
(
result
,
types
.
MethodType
):
result
=
_rebound_method
(
result
,
wrapper
)
elif
_has__of__
(
result
):
result
=
result
.
__of__
(
wrapper
)
return
result
except
AttributeError
:
pass
# deal with mixed __parent__ / aq_parent circles
elif
(
isinstance
(
wrapper
.
_container
,
_Wrapper
)
and
wrapper
.
_container
.
_container
is
wrapper
):
raise
RuntimeError
(
"Recursion detected in acquisition wrapper"
)
else
:
# normal attribute lookup
try
:
result
=
getattr
(
wrapper
.
_obj
,
orig_name
)
except
AttributeError
:
pass
else
:
if
result
is
Acquired
:
return
_Wrapper_acquire
(
wrapper
,
orig_name
,
predicate
=
predicate
,
predicate_extra
=
predicate_extra
,
orig_object
=
orig_object
,
explicit
=
True
,
containment
=
containment
)
if
isinstance
(
result
,
types
.
MethodType
):
result
=
_rebound_method
(
result
,
wrapper
)
elif
_has__of__
(
result
):
result
=
result
.
__of__
(
wrapper
)
if
predicate
:
if
_apply_filter
(
predicate
,
wrapper
,
orig_name
,
result
,
predicate_extra
,
orig_object
):
return
result
else
:
return
result
# lookup has failed, acquire from the parent
if
search_parent
and
(
not
name
.
startswith
(
'_'
)
or
explicit
):
return
_Wrapper_acquire
(
wrapper
,
orig_name
,
predicate
=
predicate
,
predicate_extra
=
predicate_extra
,
orig_object
=
orig_object
,
explicit
=
explicit
,
containment
=
containment
)
raise
AttributeError
(
orig_name
)
_NOT_GIVEN
=
object
()
# marker
class
_Wrapper
(
ExtensionClass
.
Base
):
__slots__
=
(
'_obj'
,
'_container'
,)
_IS_IMPLICIT
=
None
def
__new__
(
cls
,
obj
,
container
):
inst
=
super
(
_Wrapper
,
cls
).
__new__
(
cls
)
inst
.
_obj
=
obj
inst
.
_container
=
container
return
inst
def
__init__
(
self
,
obj
,
container
):
super
(
_Wrapper
,
self
).
__init__
()
self
.
_obj
=
obj
self
.
_container
=
container
def
__setattr__
(
self
,
name
,
value
):
if
name
==
'__parent__'
or
name
==
'aq_parent'
:
object
.
__setattr__
(
self
,
'_container'
,
value
)
return
if
name
==
'_obj'
or
name
==
'_container'
:
# should only happen at init time
object
.
__setattr__
(
self
,
name
,
value
)
return
# If we are wrapping something, unwrap passed in wrappers
if
self
.
_obj
is
None
:
raise
AttributeError
(
"Attempt to set attribute on empty acquisition wrapper"
)
while
value
is
not
None
and
isinstance
(
value
,
_Wrapper
):
value
=
value
.
_obj
setattr
(
self
.
_obj
,
name
,
value
)
def
__delattr__
(
self
,
name
):
if
name
==
'__parent__'
or
name
==
'aq_parent'
:
self
.
_container
=
None
else
:
delattr
(
self
.
_obj
,
name
)
def
__getattribute__
(
self
,
name
):
if
name
in
(
'_obj'
,
'_container'
):
return
object
.
__getattribute__
(
self
,
name
)
if
self
.
_obj
is
not
None
or
self
.
_container
is
not
None
:
return
_Wrapper_findattr
(
self
,
name
,
None
,
None
,
None
,
True
,
type
(
self
).
_IS_IMPLICIT
,
False
,
False
)
return
object
.
__getattribute__
(
self
,
name
)
def
__of__
(
self
,
parent
):
# Based on __of__ in the C code;
# simplify a layer of wrapping.
# We have to call the raw __of__ method or we recurse on
# our own lookup (the C code does not have this issue, it can use
# the wrapped __of__ method because it gets here via the descriptor code
# path)...
wrapper
=
self
.
_obj
.
__of__
(
parent
)
if
not
isinstance
(
wrapper
,
_Wrapper
)
or
not
isinstance
(
wrapper
.
_container
,
_Wrapper
):
return
wrapper
# but the returned wrapper should be based on this object's
# wrapping chain
wrapper
.
_obj
=
self
while
isinstance
(
wrapper
.
_obj
,
_Wrapper
)
\
and
(
wrapper
.
_obj
.
_container
is
wrapper
.
_container
.
_obj
):
# Since we mutate the wrapper as we walk up, we must copy
wrapper
=
type
(
wrapper
)(
wrapper
.
_obj
,
wrapper
.
_container
)
wrapper
.
_obj
=
wrapper
.
_obj
.
_obj
return
wrapper
def
aq_acquire
(
self
,
name
,
filter
=
None
,
extra
=
None
,
explicit
=
True
,
default
=
_NOT_GIVEN
,
containment
=
False
):
try
:
return
_Wrapper_findattr
(
self
,
name
,
predicate
=
filter
,
predicate_extra
=
extra
,
orig_object
=
self
,
search_self
=
True
,
search_parent
=
explicit
or
type
(
self
).
_IS_IMPLICIT
,
explicit
=
explicit
,
containment
=
containment
)
except
AttributeError
:
if
default
is
_NOT_GIVEN
:
raise
return
default
acquire
=
aq_acquire
def
aq_inContextOf
(
self
,
o
,
inner
=
True
):
return
aq_inContextOf
(
self
,
o
,
inner
=
inner
)
# Wrappers themselves are not picklable, but if the underlying
# object has a _p_oid, then the __getnewargs__ method is allowed
def
__reduce__
(
self
,
*
args
):
raise
TypeError
(
"Can't pickle objects in acquisition wrappers."
)
__reduce_ex__
=
__reduce__
__getstate__
=
__reduce__
def
__getnewargs__
(
self
):
return
()
# Equality and comparisons
def
__hash__
(
self
):
# The C implementation doesn't pass the wrapper
# to any __hash__ that the object implements,
# so it can't access derived attributes.
# (If that changes, just add this to __unary_special_methods__
# and remove this method)
return
hash
(
self
.
_obj
)
# The C implementation forces all comparisons through the
# __cmp__ method, if it's implemented. If it's not implemented,
# then comparisons are based strictly on the memory addresses
# of the underlying object (aq_base). We could mostly emulate this behaviour
# on Python 2, but on Python 3 __cmp__ is gone, so users won't
# have an expectation to write it.
# Because users have never had an expectation that the rich comparison
# methods would be called on their wrapped objects (and so would not be
# accessing acquired attributes there), we can't/don't want to start
# proxying to them?
# For the moment, we settle for an emulation of the C behaviour:
# define __cmp__ the same way, and redirect the rich comparison operators
# to it. (Note that these attributes are also hardcoded in getattribute)
def
__cmp__
(
self
,
other
):
aq_self
=
self
.
_obj
if
hasattr
(
type
(
aq_self
),
'__cmp__'
):
return
_rebound_method
(
type
(
aq_self
),
self
)(
other
)
my_base
=
aq_base
(
self
)
other_base
=
aq_base
(
other
)
if
my_base
is
other_base
:
return
0
return
-
1
if
id
(
my_base
)
<
id
(
other_base
)
else
1
def
__eq__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
==
0
def
__ne__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
!=
0
def
__lt__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
<
0
def
__le__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
<=
0
def
__gt__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
>
0
def
__ge__
(
self
,
other
):
return
self
.
__cmp__
(
other
)
>=
0
# Special methods looked up by the type of self._obj,
# but which must have the wrapper as self when called
def
__nonzero__
(
self
):
aq_self
=
self
.
_obj
type_aq_self
=
type
(
aq_self
)
nonzero
=
getattr
(
type_aq_self
,
'__nonzero__'
,
None
)
if
nonzero
is
None
:
# Py3 bool?
nonzero
=
getattr
(
type_aq_self
,
'__bool__'
,
None
)
if
nonzero
is
None
:
# a len?
nonzero
=
getattr
(
type_aq_self
,
'__len__'
,
None
)
if
nonzero
:
return
bool
(
nonzero
(
self
))
# Py3 is strict about the return type
# If nothing was defined, then it's true
return
True
__bool__
=
__nonzero__
def
__unicode__
(
self
):
f
=
getattr
(
self
.
aq_self
,
'__unicode__'
,
getattr
(
self
.
aq_self
,
'__str__'
,
object
.
__str__
))
return
_rebound_method
(
f
,
self
)()
def
__repr__
(
self
):
aq_self
=
self
.
_obj
return
type
(
aq_self
).
__repr__
(
aq_self
)
def
__str__
(
self
):
aq_self
=
self
.
_obj
return
type
(
aq_self
).
__str__
(
aq_self
)
__binary_special_methods__
=
[
# general numeric
'__add__'
,
'__sub__'
,
'__mul__'
,
'__floordiv__'
,
# not implemented in C
'__mod__'
,
'__divmod__'
,
'__pow__'
,
'__lshift__'
,
'__rshift__'
,
'__and__'
,
'__xor__'
,
'__or__'
,
# division; only one of these will be used at any one time
'__truediv__'
,
'__div__'
,
# reflected numeric
'__radd__'
,
'__rsub__'
,
'__rmul__'
,
'__rdiv__'
,
'__rtruediv__'
,
'__rfloordiv__'
,
'__rmod__'
,
'__rdivmod__'
,
'__rpow__'
,
'__rlshift__'
,
'__rrshift__'
,
'__rand__'
,
'__rxor__'
,
'__ror__'
,
# in place numeric
'__iadd__'
,
'__isub__'
,
'__imul__'
,
'__idiv__'
,
'__itruediv__'
,
'__ifloordiv__'
,
'__imod__'
,
'__idivmod__'
,
'__ipow__'
,
'__ilshift__'
,
'__irshift__'
,
'__iand__'
,
'__ixor__'
,
'__ior__'
,
# conversion
'__coerce__'
,
# container
'__delitem__'
,
]
__unary_special_methods__
=
[
# arithmetic
'__neg__'
,
'__pos__'
,
'__abs__'
,
'__invert__'
,
# conversion
'__complex__'
,
'__int__'
,
'__long__'
,
'__float__'
,
'__oct__'
,
'__hex__'
,
'__index__'
,
#'__len__',
# strings are special
#'__repr__',
#'__str__',
]
for
_name
in
__binary_special_methods__
:
def
_make_op
(
_name
):
def
op
(
self
,
other
):
aq_self
=
self
.
_obj
return
getattr
(
type
(
aq_self
),
_name
)(
self
,
other
)
return
op
locals
()[
_name
]
=
_make_op
(
_name
)
for
_name
in
__unary_special_methods__
:
def
_make_op
(
_name
):
def
op
(
self
):
aq_self
=
self
.
_obj
return
getattr
(
type
(
aq_self
),
_name
)(
self
)
return
op
locals
()[
_name
]
=
_make_op
(
_name
)
del
_make_op
del
_name
# Container protocol
def
__len__
(
self
):
# if len is missing, it should raise TypeError
# (AttributeError is acceptable under Py2, but Py3
# breaks list conversion if AttributeError is raised)
try
:
l
=
getattr
(
type
(
self
.
_obj
),
'__len__'
)
except
AttributeError
:
raise
TypeError
(
'object has no len()'
)
else
:
return
l
(
self
)
def
__iter__
(
self
):
# For things that provide either __iter__ or just __getitem__,
# we need to be sure that the wrapper is provided as self
if
hasattr
(
self
.
_obj
,
'__iter__'
):
return
_rebound_method
(
self
.
_obj
.
__iter__
,
self
)()
if
hasattr
(
self
.
_obj
,
'__getitem__'
):
# Unfortunately we cannot simply call iter(self._obj)
# and rebind im_self like we do above: the Python runtime
# complains (TypeError: 'sequenceiterator' expected, got 'Wrapper' instead)
class
WrapperIter
(
object
):
__slots__
=
(
'_wrapper'
,)
def
__init__
(
self
,
o
):
self
.
_wrapper
=
o
def
__getitem__
(
self
,
i
):
return
self
.
_wrapper
.
__getitem__
(
i
)
it
=
WrapperIter
(
self
)
return
iter
(
it
)
return
iter
(
self
.
_obj
)
def
__contains__
(
self
,
item
):
# First, if the type of the object defines __contains__ then
# use it
aq_self
=
self
.
_obj
aq_contains
=
getattr
(
type
(
aq_self
),
'__contains__'
,
None
)
if
aq_contains
:
return
aq_contains
(
self
,
item
)
# Next, we should attempt to iterate like the interpreter; but the C code doesn't
# do this, so we don't either.
#return item in iter(self)
raise
AttributeError
(
'__contains__'
)
def
__setitem__
(
self
,
key
,
value
):
aq_self
=
self
.
_obj
_rebound_method
(
getattr
(
type
(
aq_self
),
'__setitem__'
),
self
)(
key
,
value
)
def
__getitem__
(
self
,
key
):
if
isinstance
(
key
,
slice
):
if
isinstance
(
self
.
_obj
,
(
list
,
tuple
)):
return
self
.
_obj
[
key
]
start
,
stop
=
key
.
start
,
key
.
stop
if
start
is
None
:
start
=
0
if
start
<
0
:
start
+=
len
(
self
.
_obj
)
if
stop
is
None
:
stop
=
getattr
(
sys
,
'maxint'
,
None
)
# PY2
elif
stop
<
0
:
stop
+=
len
(
self
.
_obj
)
if
hasattr
(
operator
,
'getslice'
):
# PY2
return
operator
.
getslice
(
self
.
_obj
,
start
,
stop
)
return
self
.
_obj
[
start
:
stop
]
return
self
.
_obj
[
key
]
def
__call__
(
self
,
*
args
,
**
kwargs
):
try
:
# Note we look this up on the completely unwrapped
# object, so as not to get a class
call
=
getattr
(
self
.
aq_base
,
'__call__'
)
except
AttributeError
:
# A TypeError is what the interpreter raises;
# AttributeError is allowed to percolate through the
# C proxy
raise
TypeError
(
'object is not callable'
)
else
:
return
_rebound_method
(
call
,
self
)(
*
args
,
**
kwargs
)
class
ImplicitAcquisitionWrapper
(
_Wrapper
):
_IS_IMPLICIT
=
True
class
ExplicitAcquisitionWrapper
(
_Wrapper
):
_IS_IMPLICIT
=
False
def
__getattribute__
(
self
,
name
):
# Special case backwards-compatible acquire method
if
name
==
'acquire'
:
return
object
.
__getattribute__
(
self
,
name
)
return
_Wrapper
.
__getattribute__
(
self
,
name
)
class
_Acquirer
(
ExtensionClass
.
Base
):
def
__getattribute__
(
self
,
name
):
try
:
return
ExtensionClass
.
Base
.
__getattribute__
(
self
,
name
)
except
AttributeError
:
# the doctests have very specific error message
# requirements
raise
AttributeError
(
name
)
def
__of__
(
self
,
context
):
return
type
(
self
).
_Wrapper
(
self
,
context
)
class
Implicit
(
_Acquirer
):
_Wrapper
=
ImplicitAcquisitionWrapper
ImplicitAcquisitionWrapper
.
_Wrapper
=
ImplicitAcquisitionWrapper
class
Explicit
(
_Acquirer
):
_Wrapper
=
ExplicitAcquisitionWrapper
ExplicitAcquisitionWrapper
.
_Wrapper
=
ExplicitAcquisitionWrapper
###
# Exported module functions
###
def
aq_acquire
(
obj
,
name
,
filter
=
None
,
extra
=
None
,
explicit
=
True
,
default
=
_NOT_GIVEN
,
containment
=
False
):
if
isinstance
(
obj
,
_Wrapper
):
return
obj
.
aq_acquire
(
name
,
filter
=
filter
,
extra
=
extra
,
default
=
default
,
explicit
=
explicit
or
type
(
obj
).
_IS_IMPLICIT
,
containment
=
containment
)
# Does it have a parent, or do we have a filter?
# Then go through the acquisition code
if
hasattr
(
obj
,
'__parent__'
)
or
filter
is
not
None
:
parent
=
getattr
(
obj
,
'__parent__'
,
None
)
return
aq_acquire
(
ImplicitAcquisitionWrapper
(
obj
,
parent
),
name
,
filter
=
filter
,
extra
=
extra
,
default
=
default
,
explicit
=
explicit
,
containment
=
containment
)
# no parent and no filter, simple case
try
:
return
getattr
(
obj
,
name
)
except
AttributeError
:
if
default
is
_NOT_GIVEN
:
raise
AttributeError
(
name
)
# doctests are strict
return
default
def
aq_parent
(
obj
):
# needs to be safe to call from __getattribute__ of a wrapper
# and reasonably fast
if
isinstance
(
obj
,
_Wrapper
):
return
object
.
__getattribute__
(
obj
,
'_container'
)
# if not a wrapper, deal with the __parent__
return
getattr
(
obj
,
'__parent__'
,
None
)
def
aq_chain
(
obj
,
containment
=
False
):
result
=
[]
while
True
:
if
isinstance
(
obj
,
_Wrapper
):
if
obj
.
_obj
is
not
None
:
if
containment
:
while
isinstance
(
obj
.
_obj
,
_Wrapper
):
obj
=
obj
.
_obj
result
.
append
(
obj
)
if
obj
.
_container
is
not
None
:
obj
=
obj
.
_container
continue
else
:
result
.
append
(
obj
)
obj
=
getattr
(
obj
,
'__parent__'
,
None
)
if
obj
is
not
None
:
continue
break
return
result
def
aq_base
(
obj
):
result
=
obj
while
isinstance
(
result
,
_Wrapper
):
result
=
result
.
aq_self
return
result
def
aq_get
(
obj
,
name
,
default
=
_NOT_GIVEN
,
containment
=
False
):
# Not wrapped. If we have a __parent__ pointer, create a wrapper
# and go as usual
if
not
isinstance
(
obj
,
_Wrapper
)
and
hasattr
(
obj
,
'__parent__'
):
obj
=
ImplicitAcquisitionWrapper
(
obj
,
obj
.
__parent__
)
try
:
# We got a wrapped object, business as usual
return
(
_Wrapper_findattr
(
obj
,
name
,
None
,
None
,
obj
,
True
,
True
,
True
,
containment
)
if
isinstance
(
obj
,
_Wrapper
)
# ok, plain getattr
else
getattr
(
obj
,
name
))
except
AttributeError
:
if
default
is
_NOT_GIVEN
:
raise
return
default
def
aq_inner
(
obj
):
if
not
isinstance
(
obj
,
_Wrapper
):
return
obj
result
=
obj
.
_obj
while
isinstance
(
result
,
_Wrapper
):
obj
=
result
result
=
result
.
_obj
result
=
obj
return
result
def
aq_self
(
obj
):
if
isinstance
(
obj
,
_Wrapper
):
return
obj
.
aq_self
return
obj
def
aq_inContextOf
(
self
,
o
,
inner
=
True
):
next
=
self
o
=
aq_base
(
o
)
while
True
:
if
aq_base
(
next
)
is
o
:
return
1
if
inner
:
self
=
aq_inner
(
next
)
if
self
is
None
:
break
else
:
self
=
next
next
=
aq_parent
(
self
)
if
next
is
None
:
break
return
0
if
'PURE_PYTHON'
not
in
os
.
environ
:
# pragma no cover
try
:
from
._Acquisition
import
*
except
ImportError
:
pass
classImplements
(
Explicit
,
IAcquirer
)
classImplements
(
Explicit
,
IAcquirer
)
classImplements
(
ExplicitAcquisitionWrapper
,
IAcquisitionWrapper
)
classImplements
(
ExplicitAcquisitionWrapper
,
IAcquisitionWrapper
)
...
...
src/Acquisition/tests.py
View file @
8a77d12c
...
@@ -31,7 +31,7 @@
...
@@ -31,7 +31,7 @@
>>> class A(Acquisition.Implicit):
>>> class A(Acquisition.Implicit):
... def report(self):
... def report(self):
... print
self.color
... print
(self.color)
>>> a = A()
>>> a = A()
>>> c = C()
>>> c = C()
...
@@ -117,7 +117,7 @@
...
@@ -117,7 +117,7 @@
automatically obtained from the environment. Instead, the
automatically obtained from the environment. Instead, the
method 'aq_aquire' must be used, as in::
method 'aq_aquire' must be used, as in::
print
c.a.aq_acquire('color'
)
print
(c.a.aq_acquire('color')
)
To support explicit acquisition, an object should inherit
To support explicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Explicit'.
from the mix-in class 'Acquisition.Explicit'.
...
@@ -170,7 +170,7 @@
...
@@ -170,7 +170,7 @@
... __roles__ = Acquisition.Acquired
... __roles__ = Acquisition.Acquired
>>> c.x = C()
>>> c.x = C()
>>> c.x.__roles__
>>> c.x.__roles__
# doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Traceback (most recent call last):
...
...
AttributeError: __roles__
AttributeError: __roles__
...
@@ -231,7 +231,7 @@
...
@@ -231,7 +231,7 @@
>>> def find_nice(self, ancestor, name, object, extra):
>>> def find_nice(self, ancestor, name, object, extra):
... return hasattr(object,'isNice') and object.isNice
... return hasattr(object,'isNice') and object.isNice
>>> print
a.b.c.aq_acquire('p', find_nice
)
>>> print
(a.b.c.aq_acquire('p', find_nice)
)
spam(Nice) and I am nice!
spam(Nice) and I am nice!
The filtered acquisition in the last line skips over the first
The filtered acquisition in the last line skips over the first
...
@@ -328,6 +328,36 @@
...
@@ -328,6 +328,36 @@
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
"""
"""
from
__future__
import
print_function
import
gc
import
unittest
import
sys
import
platform
import
operator
from
doctest
import
DocTestSuite
,
DocFileSuite
if
sys
.
version_info
>=
(
3
,):
PY3
=
True
PY2
=
False
def
unicode
(
self
):
# For test purposes, redirect the unicode
# to the type of the object, just like Py2 did
try
:
return
type
(
self
).
__unicode__
(
self
)
except
AttributeError
as
e
:
return
type
(
self
).
__str__
(
self
)
long
=
int
else
:
PY2
=
True
PY3
=
False
py_impl
=
getattr
(
platform
,
'python_implementation'
,
lambda
:
None
)
PYPY
=
py_impl
()
==
'PyPy'
if
not
hasattr
(
gc
,
'get_threshold'
):
# PyPy
gc
.
get_threshold
=
lambda
:
()
gc
.
set_threshold
=
lambda
*
x
:
None
import
ExtensionClass
import
ExtensionClass
import
Acquisition
import
Acquisition
...
@@ -558,7 +588,6 @@ def test_simple():
...
@@ -558,7 +588,6 @@ def test_simple():
True
True
"""
"""
def
test__of__exception
():
def
test__of__exception
():
"""
"""
Wrapper_findattr did't check for an exception in a user defined
Wrapper_findattr did't check for an exception in a user defined
...
@@ -566,13 +595,10 @@ def test__of__exception():
...
@@ -566,13 +595,10 @@ def test__of__exception():
case the 'value' argument of the filter was NULL, which caused
case the 'value' argument of the filter was NULL, which caused
a segfault when being accessed.
a segfault when being accessed.
>>> class UserError(Exception):
... pass
...
>>> class X(Acquisition.Implicit):
>>> class X(Acquisition.Implicit):
... def __of__(self, parent):
... def __of__(self, parent):
... if Acquisition.aq_base(parent) is not parent:
... if Acquisition.aq_base(parent) is not parent:
... raise
UserError, 'ack'
... raise
NotImplementedError('ack')
... return X.inheritedAttribute('__of__')(self, parent)
... return X.inheritedAttribute('__of__')(self, parent)
...
...
>>> a = I('a')
>>> a = I('a')
...
@@ -582,7 +608,7 @@ def test__of__exception():
...
@@ -582,7 +608,7 @@ def test__of__exception():
... lambda self, object, name, value, extra: repr(value))
... lambda self, object, name, value, extra: repr(value))
Traceback (most recent call last):
Traceback (most recent call last):
...
...
User
Error: ack
NotImplemented
Error: ack
"""
"""
...
@@ -1236,7 +1262,8 @@ def test_aq_inContextOf():
...
@@ -1236,7 +1262,8 @@ def test_aq_inContextOf():
>>> class A(Acquisition.Implicit):
>>> class A(Acquisition.Implicit):
... def hi(self):
... def hi(self):
... print "%s()" % self.__class__.__name__, self.color
... print(self.__class__.__name__)
... print(self.color)
>>> class Location(object):
>>> class Location(object):
... __parent__ = None
... __parent__ = None
...
@@ -1244,15 +1271,17 @@ def test_aq_inContextOf():
...
@@ -1244,15 +1271,17 @@ def test_aq_inContextOf():
>>> b=B()
>>> b=B()
>>> b.a=A()
>>> b.a=A()
>>> b.a.hi()
>>> b.a.hi()
A() red
A
red
>>> b.a.color='green'
>>> b.a.color='green'
>>> b.a.hi()
>>> b.a.hi()
A() green
A
green
>>> try:
>>> try:
... A().hi()
... A().hi()
... raise
'Program error', 'spam'
... raise
RuntimeError( 'Program error', 'spam')
... except AttributeError: pass
... except AttributeError: pass
A
()
A
New test for wrapper comparisons.
New test for wrapper comparisons.
...
@@ -1349,7 +1378,7 @@ def test_AqAlg():
...
@@ -1349,7 +1378,7 @@ def test_AqAlg():
[A]
[A]
>>> Acquisition.aq_chain(A, 1)
>>> Acquisition.aq_chain(A, 1)
[A]
[A]
>>>
map(Acquisition.aq_base, Acquisition.aq_chain(A, 1
))
>>>
list(map(Acquisition.aq_base, Acquisition.aq_chain(A, 1)
))
[A]
[A]
>>> A.C
>>> A.C
C
C
...
@@ -1357,7 +1386,7 @@ def test_AqAlg():
...
@@ -1357,7 +1386,7 @@ def test_AqAlg():
[C, A]
[C, A]
>>> Acquisition.aq_chain(A.C, 1)
>>> Acquisition.aq_chain(A.C, 1)
[C, A]
[C, A]
>>>
map(Acquisition.aq_base, Acquisition.aq_chain(A.C, 1
))
>>>
list(map(Acquisition.aq_base, Acquisition.aq_chain(A.C, 1)
))
[C, A]
[C, A]
>>> A.C.D
>>> A.C.D
...
@@ -1366,7 +1395,7 @@ def test_AqAlg():
...
@@ -1366,7 +1395,7 @@ def test_AqAlg():
[D, C, A]
[D, C, A]
>>> Acquisition.aq_chain(A.C.D, 1)
>>> Acquisition.aq_chain(A.C.D, 1)
[D, C, A]
[D, C, A]
>>>
map(Acquisition.aq_base, Acquisition.aq_chain(A.C.D, 1
))
>>>
list(map(Acquisition.aq_base, Acquisition.aq_chain(A.C.D, 1)
))
[D, C, A]
[D, C, A]
>>> A.B.C
>>> A.B.C
...
@@ -1375,7 +1404,7 @@ def test_AqAlg():
...
@@ -1375,7 +1404,7 @@ def test_AqAlg():
[C, B, A]
[C, B, A]
>>> Acquisition.aq_chain(A.B.C, 1)
>>> Acquisition.aq_chain(A.B.C, 1)
[C, A]
[C, A]
>>>
map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C, 1
))
>>>
list(map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C, 1)
))
[C, A]
[C, A]
>>> A.B.C.D
>>> A.B.C.D
...
@@ -1384,7 +1413,7 @@ def test_AqAlg():
...
@@ -1384,7 +1413,7 @@ def test_AqAlg():
[D, C, B, A]
[D, C, B, A]
>>> Acquisition.aq_chain(A.B.C.D, 1)
>>> Acquisition.aq_chain(A.B.C.D, 1)
[D, C, A]
[D, C, A]
>>>
map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C.D, 1
))
>>>
list(map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C.D, 1)
))
[D, C, A]
[D, C, A]
...
@@ -1407,19 +1436,22 @@ def test_explicit_acquisition():
...
@@ -1407,19 +1436,22 @@ def test_explicit_acquisition():
>>> class A(Acquisition.Explicit):
>>> class A(Acquisition.Explicit):
... def hi(self):
... def hi(self):
... print self.__class__.__name__, self.acquire('color')
... print(self.__class__.__name__)
... print(self.acquire('color'))
>>> b=B()
>>> b=B()
>>> b.a=A()
>>> b.a=A()
>>> b.a.hi()
>>> b.a.hi()
A red
A
red
>>> b.a.color='green'
>>> b.a.color='green'
>>> b.a.hi()
>>> b.a.hi()
A green
A
green
>>> try:
>>> try:
... A().hi()
... A().hi()
... raise
'Program error', 'spam'
... raise
RuntimeError('Program error', 'spam')
... except AttributeError: pass
... except AttributeError: pass
A
A
...
@@ -1442,7 +1474,7 @@ def test_creating_wrappers_directly():
...
@@ -1442,7 +1474,7 @@ def test_creating_wrappers_directly():
>>> w.color
>>> w.color
'red'
'red'
>>> w = ImplicitAcquisitionWrapper(a.b)
>>> w = ImplicitAcquisitionWrapper(a.b)
# doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Traceback (most recent call last):
...
...
TypeError: __init__() takes exactly 2 arguments (1 given)
TypeError: __init__() takes exactly 2 arguments (1 given)
...
@@ -1464,17 +1496,17 @@ def test_creating_wrappers_directly():
...
@@ -1464,17 +1496,17 @@ def test_creating_wrappers_directly():
Note that messing with the wrapper won't in any way affect the
Note that messing with the wrapper won't in any way affect the
wrapped object:
wrapped object:
>>> Acquisition.aq_base(w).__parent__
>>> Acquisition.aq_base(w).__parent__
# doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Traceback (most recent call last):
...
...
AttributeError: __parent__
AttributeError: __parent__
>>> w = ImplicitAcquisitionWrapper()
>>> w = ImplicitAcquisitionWrapper()
# doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Traceback (most recent call last):
...
...
TypeError: __init__() takes exactly 2 arguments (0 given)
TypeError: __init__() takes exactly 2 arguments (0 given)
>>> w = ImplicitAcquisitionWrapper(obj=1)
>>> w = ImplicitAcquisitionWrapper(obj=1)
# doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
Traceback (most recent call last):
...
...
TypeError: kwyword arguments not allowed
TypeError: kwyword arguments not allowed
...
@@ -1579,7 +1611,10 @@ def test_cant_pickle_acquisition_wrappers_newstyle():
...
@@ -1579,7 +1611,10 @@ def test_cant_pickle_acquisition_wrappers_newstyle():
def
test_cant_persist_acquisition_wrappers_classic
():
def
test_cant_persist_acquisition_wrappers_classic
():
"""
"""
>>> import cPickle
>>> try:
... import cPickle
... except ImportError:
... import pickle as cPickle
>>> class X:
>>> class X:
... _p_oid = '1234'
... _p_oid = '1234'
...
@@ -1604,8 +1639,8 @@ def test_cant_persist_acquisition_wrappers_classic():
...
@@ -1604,8 +1639,8 @@ def test_cant_persist_acquisition_wrappers_classic():
Check custom pickler:
Check custom pickler:
>>> from
cStringIO import String
IO
>>> from
io import Bytes
IO
>>> file =
String
IO()
>>> file =
Bytes
IO()
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler.dump(w)
>>> pickler.dump(w)
...
@@ -1616,29 +1651,35 @@ def test_cant_persist_acquisition_wrappers_classic():
...
@@ -1616,29 +1651,35 @@ def test_cant_persist_acquisition_wrappers_classic():
Check custom pickler with a persistent_id method matching the semantics
Check custom pickler with a persistent_id method matching the semantics
in ZODB.serialize.ObjectWriter.persistent_id:
in ZODB.serialize.ObjectWriter.persistent_id:
>>> file =
String
IO()
>>> file =
Bytes
IO()
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler = cPickle.Pickler(file, 1)
>>> def persistent_id(obj):
>>> def persistent_id(obj):
... if not hasattr(obj, '_p_oid'): return None
... klass = type(obj)
... klass = type(obj)
... oid = obj._p_oid
... oid = obj._p_oid
... if hasattr(klass, '__getnewargs__'):
... if hasattr(klass, '__getnewargs__'):
... return oid
... return oid
... return 'class_and_oid', klass
... return 'class_and_oid', klass
>>> pickler.inst_persistent_id = persistent_id
>>> try: pickler.inst_persistent_id = persistent_id
... except AttributeError: pass
>>> pickler.persistent_id = persistent_id #PyPy and Py3k
>>> _ = pickler.dump(w)
>>> _ = pickler.dump(w)
>>> state = file.getvalue()
>>> state = file.getvalue()
>>> '1234' in state
>>>
b
'1234' in state
True
True
>>> 'class_and_oid' in state
>>>
b
'class_and_oid' in state
False
False
"""
"""
def
test_cant_persist_acquisition_wrappers_newstyle
():
def
test_cant_persist_acquisition_wrappers_newstyle
():
"""
"""
>>> import cPickle
>>> try:
... import cPickle
... except ImportError:
... import pickle as cPickle
>>> class X(object):
>>> class X(object):
... _p_oid = '1234'
... _p_oid = '1234'
...
@@ -1663,8 +1704,8 @@ def test_cant_persist_acquisition_wrappers_newstyle():
...
@@ -1663,8 +1704,8 @@ def test_cant_persist_acquisition_wrappers_newstyle():
Check custom pickler:
Check custom pickler:
>>> from
cStringIO import String
IO
>>> from
io import Bytes
IO
>>> file =
String
IO()
>>> file =
Bytes
IO()
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler.dump(w)
>>> pickler.dump(w)
...
@@ -1675,22 +1716,26 @@ def test_cant_persist_acquisition_wrappers_newstyle():
...
@@ -1675,22 +1716,26 @@ def test_cant_persist_acquisition_wrappers_newstyle():
Check custom pickler with a persistent_id method matching the semantics
Check custom pickler with a persistent_id method matching the semantics
in ZODB.serialize.ObjectWriter.persistent_id:
in ZODB.serialize.ObjectWriter.persistent_id:
>>> file =
String
IO()
>>> file =
Bytes
IO()
>>> pickler = cPickle.Pickler(file, 1)
>>> pickler = cPickle.Pickler(file, 1)
>>> def persistent_id(obj):
>>> def persistent_id(obj):
... if not hasattr(obj, '_p_oid'): return None
... klass = type(obj)
... klass = type(obj)
... oid = obj._p_oid
... oid = obj._p_oid
... if hasattr(klass, '__getnewargs__'):
... if hasattr(klass, '__getnewargs__'):
... return oid
... return oid
... return 'class_and_oid', klass
... return 'class_and_oid', klass
>>> pickler.inst_persistent_id = persistent_id
>>> try: pickler.inst_persistent_id = persistent_id
... except AttributeError: pass
>>> pickler.persistent_id = persistent_id #PyPy and Py3k
>>> _ = pickler.dump(w)
>>> _ = pickler.dump(w)
>>> state = file.getvalue()
>>> state = file.getvalue()
>>> '1234' in state
>>>
b
'1234' in state
True
True
>>> 'class_and_oid' in state
>>>
b
'class_and_oid' in state
False
False
"""
"""
...
@@ -1721,9 +1766,94 @@ def test_interfaces():
...
@@ -1721,9 +1766,94 @@ def test_interfaces():
True
True
"""
"""
if
PY2
:
# Assigning to __bases__ is difficult under Python 3.
# In this example, you get:
# "TypeError: __bases__ assignment: 'Base' deallocator differs from 'object'"
# I don't know what the workaround is; the old one of using a dummy
# superclass no longer works. See http://bugs.python.org/issue672115
def
test_mixin_post_class_definition
():
"""
Mixing in Base after class definition doesn't break anything,
but also doesn't result in any wrappers.
>>> from ExtensionClass import Base
>>> class Plain(object):
... pass
>>> Plain.__bases__ == (object,)
True
>>> Plain.__bases__ = (Base,)
>>> isinstance(Plain(), Base)
True
Even after mixing in that base, when we request such an object
from an implicit acquiring base, it doesn't come out wrapped:
>>> from Acquisition import Implicit
>>> class I(Implicit):
... pass
>>> root = I()
>>> root.a = I()
>>> root.a.b = Plain()
>>> type(root.a.b) is Plain
True
This is because after the mixin, even though Plain is-a Base,
it's still not an Explicit/Implicit acquirer and provides
neither the `__of__` nor `__get__` methods necessary
(`__get__` is added as a consequence of `__of__` at class creation time):
>>> hasattr(Plain, '__get__')
False
>>> hasattr(Plain, '__of__')
False
"""
def
test_mixin_base
():
"""
We can mix-in Base as part of multiple inheritance.
>>> from ExtensionClass import Base
>>> class MyBase(object):
... pass
>>> class MixedIn(Base,MyBase):
... pass
>>> MixedIn.__bases__ == (Base,MyBase)
True
>>> isinstance(MixedIn(), Base)
True
Because it's not an acquiring object and doesn't provide `__of__`
or `__get__`, when accessed from implicit contexts it doesn't come
out wrapped:
>>> from Acquisition import Implicit
>>> class I(Implicit):
... pass
>>> root = I()
>>> root.a = I()
>>> root.a.b = MixedIn()
>>> type(root.a.b) is MixedIn
True
This is because after the mixin, even though Plain is-a Base,
it doesn't provide the `__of__` method used for wrapping, and so
the class definition code that would add the `__get__` method also
doesn't run:
>>> hasattr(MixedIn, '__of__')
False
>>> hasattr(MixedIn, '__get__')
False
"""
def
show
(
x
):
def
show
(
x
):
print
showaq
(
x
).
strip
(
)
print
(
showaq
(
x
).
strip
()
)
def
showaq
(
m_self
,
indent
=
''
):
def
showaq
(
m_self
,
indent
=
''
):
...
@@ -1754,9 +1884,10 @@ def showaq(m_self, indent=''):
...
@@ -1754,9 +1884,10 @@ def showaq(m_self, indent=''):
rval
=
rval
+
indent
+
id
+
"
\
n
"
rval
=
rval
+
indent
+
id
+
"
\
n
"
return
rval
return
rval
def
test_Basic_gc
():
def
test_Basic_gc
():
"""Test to make sure that EC instances participate in GC
"""Test to make sure that EC instances participate in GC.
Note that PyPy always reports 0 collected objects even
though we can see its finalizers run.
>>> from ExtensionClass import Base
>>> from ExtensionClass import Base
>>> import gc
>>> import gc
...
@@ -1769,7 +1900,7 @@ def test_Basic_gc():
...
@@ -1769,7 +1900,7 @@ def test_Basic_gc():
...
...
... class C2(Base):
... class C2(Base):
... def __del__(self):
... def __del__(self):
... print
'removed'
... print
('removed')
...
...
... a=C1('a')
... a=C1('a')
... a.b = C1('a.b')
... a.b = C1('a.b')
...
@@ -1778,7 +1909,7 @@ def test_Basic_gc():
...
@@ -1778,7 +1909,7 @@ def test_Basic_gc():
... ignore = gc.collect()
... ignore = gc.collect()
... del a
... del a
... removed = gc.collect()
... removed = gc.collect()
... print
removed > 0
... print
(removed > 0 or PYPY)
removed
removed
True
True
removed
removed
...
@@ -1787,10 +1918,10 @@ def test_Basic_gc():
...
@@ -1787,10 +1918,10 @@ def test_Basic_gc():
>>> gc.set_threshold(*thresholds)
>>> gc.set_threshold(*thresholds)
"""
"""
def
test_Wrapper_gc
():
def
test_Wrapper_gc
():
"""Test to make sure that EC instances participate in GC
"""Test to make sure that EC instances participate in GC.
Note that PyPy always reports 0 collected objects even
though we can see its finalizers run.
>>> import gc
>>> import gc
>>> thresholds = gc.get_threshold()
>>> thresholds = gc.get_threshold()
...
@@ -1799,7 +1930,7 @@ def test_Wrapper_gc():
...
@@ -1799,7 +1930,7 @@ def test_Wrapper_gc():
>>> for B in I, E:
>>> for B in I, E:
... class C:
... class C:
... def __del__(self):
... def __del__(self):
... print
'removed'
... print
('removed')
...
...
... a=B('a')
... a=B('a')
... a.b = B('b')
... a.b = B('b')
...
@@ -1808,7 +1939,7 @@ def test_Wrapper_gc():
...
@@ -1808,7 +1939,7 @@ def test_Wrapper_gc():
... ignored = gc.collect()
... ignored = gc.collect()
... del a
... del a
... removed = gc.collect()
... removed = gc.collect()
... removed > 0
... removed > 0
or PYPY
removed
removed
True
True
removed
removed
...
@@ -1816,11 +1947,11 @@ def test_Wrapper_gc():
...
@@ -1816,11 +1947,11 @@ def test_Wrapper_gc():
>>> gc.set_threshold(*thresholds)
>>> gc.set_threshold(*thresholds)
"""
"""
def
test_proxying
():
def
test_
container_
proxying
():
"""Make sure that recent python slots are proxied.
"""Make sure that recent python
container-related
slots are proxied.
>>> import sys
>>> import sys
>>> import Acquisition
>>> import Acquisition
...
@@ -1829,18 +1960,21 @@ def test_proxying():
...
@@ -1829,18 +1960,21 @@ def test_proxying():
>>> class C(Acquisition.Implicit):
>>> class C(Acquisition.Implicit):
... def __getitem__(self, key):
... def __getitem__(self, key):
... print 'getitem', key
... if isinstance(key, slice):
... print('slicing...')
... return (key.start,key.stop)
... print('getitem', key)
... if key == 4:
... if key == 4:
... raise IndexError
... raise IndexError
... return key
... return key
... def __contains__(self, key):
... def __contains__(self, key):
... print
'contains', repr(key
)
... print
('contains', repr(key)
)
... return key == 5
... return key == 5
... def __iter__(self):
... def __iter__(self):
... print
'iterating...'
... print
('iterating...')
... return iter((42,))
... return iter((42,))
... def __getslice__(self, start, end):
... def __getslice__(self, start, end):
... print
'slicing...'
... print
('slicing...')
... return (start, end)
... return (start, end)
The naked class behaves like this:
The naked class behaves like this:
...
@@ -1858,7 +1992,7 @@ def test_proxying():
...
@@ -1858,7 +1992,7 @@ def test_proxying():
>>> c[5:10]
>>> c[5:10]
slicing...
slicing...
(5, 10)
(5, 10)
>>> c[5:] == (5, sys.maxsize)
>>> c[5:] == (5, sys.maxsize
if PY2 else None
)
slicing...
slicing...
True
True
...
@@ -1881,7 +2015,7 @@ def test_proxying():
...
@@ -1881,7 +2015,7 @@ def test_proxying():
>>> i.c[5:10]
>>> i.c[5:10]
slicing...
slicing...
(5, 10)
(5, 10)
>>> i.c[5:] == (5, sys.maxsize)
>>> i.c[5:] == (5, sys.maxsize
if PY2 else None
)
slicing...
slicing...
True
True
...
@@ -1893,18 +2027,21 @@ def test_proxying():
...
@@ -1893,18 +2027,21 @@ def test_proxying():
>>> class C(Acquisition.Explicit):
>>> class C(Acquisition.Explicit):
... def __getitem__(self, key):
... def __getitem__(self, key):
... print 'getitem', key
... if isinstance(key, slice):
... print('slicing...')
... return (key.start,key.stop)
... print('getitem', key)
... if key == 4:
... if key == 4:
... raise IndexError
... raise IndexError
... return key
... return key
... def __contains__(self, key):
... def __contains__(self, key):
... print
'contains', repr(key
)
... print
('contains', repr(key)
)
... return key == 5
... return key == 5
... def __iter__(self):
... def __iter__(self):
... print
'iterating...'
... print
('iterating...')
... return iter((42,))
... return iter((42,))
... def __getslice__(self, start, end):
... def __getslice__(self, start, end):
... print
'slicing...'
... print
('slicing...')
... return (start, end)
... return (start, end)
The naked class behaves like this:
The naked class behaves like this:
...
@@ -1922,7 +2059,7 @@ def test_proxying():
...
@@ -1922,7 +2059,7 @@ def test_proxying():
>>> c[5:10]
>>> c[5:10]
slicing...
slicing...
(5, 10)
(5, 10)
>>> c[5:] == (5, sys.maxsize)
>>> c[5:] == (5, sys.maxsize
if PY2 else None
)
slicing...
slicing...
True
True
...
@@ -1945,7 +2082,7 @@ def test_proxying():
...
@@ -1945,7 +2082,7 @@ def test_proxying():
>>> i.c[5:10]
>>> i.c[5:10]
slicing...
slicing...
(5, 10)
(5, 10)
>>> i.c[5:] == (5, sys.maxsize)
>>> i.c[5:] == (5, sys.maxsize
if PY2 else None
)
slicing...
slicing...
True
True
...
@@ -1959,14 +2096,14 @@ def test_proxying():
...
@@ -1959,14 +2096,14 @@ def test_proxying():
... return self.l[i]
... return self.l[i]
>>> c1 = C()
>>> c1 = C()
>>> type(iter(c1))
>>> type(iter(c1))
#doctest: +ELLIPSIS
<
type '
iterator'>
<
... '...
iterator'>
>>> list(c1)
>>> list(c1)
[1, 2, 3]
[1, 2, 3]
>>> c2 = C().__of__(c1)
>>> c2 = C().__of__(c1)
>>> type(iter(c2))
>>> type(iter(c2))
#doctest: +ELLIPSIS
<
type '
iterator'>
<
... '...
iterator'>
>>> list(c2)
>>> list(c2)
[1, 2, 3]
[1, 2, 3]
...
@@ -1975,7 +2112,7 @@ def test_proxying():
...
@@ -1975,7 +2112,7 @@ def test_proxying():
>>> class C(Acquisition.Implicit):
>>> class C(Acquisition.Implicit):
... def __iter__(self):
... def __iter__(self):
... print
'iterating...'
... print
('iterating...')
... for i in range(5):
... for i in range(5):
... yield i, self.aq_parent.name
... yield i, self.aq_parent.name
>>> c = C()
>>> c = C()
...
@@ -2007,10 +2144,10 @@ def test_proxying():
...
@@ -2007,10 +2144,10 @@ def test_proxying():
>>> c = C()
>>> c = C()
>>> i = Impl()
>>> i = Impl()
>>> i.c = c
>>> i.c = c
>>> list(i.c)
>>> list(i.c)
#doctest: +ELLIPSIS
Traceback (most recent call last):
Traceback (most recent call last):
...
...
TypeError:
iteration over non-sequence
TypeError:
...iter...
>>> class C(Acquisition.Implicit):
>>> class C(Acquisition.Implicit):
... def __iter__(self):
... def __iter__(self):
...
@@ -2018,14 +2155,13 @@ def test_proxying():
...
@@ -2018,14 +2155,13 @@ def test_proxying():
>>> c = C()
>>> c = C()
>>> i = Impl()
>>> i = Impl()
>>> i.c = c
>>> i.c = c
>>> list(i.c)
>>> list(i.c)
#doctest: +ELLIPSIS
Traceback (most recent call last):
Traceback (most recent call last):
...
...
TypeError: iter() returned non-iterator
of type 'list'
TypeError: iter() returned non-iterator
...
"""
"""
class
Location
(
object
):
class
Location
(
object
):
__parent__
=
None
__parent__
=
None
...
@@ -2388,7 +2524,8 @@ def test___parent__aq_parent_circles():
...
@@ -2388,7 +2524,8 @@ def test___parent__aq_parent_circles():
>>> x.__parent__.aq_base is y.aq_base
>>> x.__parent__.aq_base is y.aq_base
True
True
>>> Acquisition.aq_parent(x) is y
True
>>> x.__parent__.__parent__ is x
>>> x.__parent__.__parent__ is x
True
True
...
@@ -2463,8 +2600,6 @@ def test__iter__after_AttributeError():
...
@@ -2463,8 +2600,6 @@ def test__iter__after_AttributeError():
... raise
... raise
"""
"""
import
unittest
from
doctest
import
DocTestSuite
,
DocFileSuite
class
TestParent
(
unittest
.
TestCase
):
class
TestParent
(
unittest
.
TestCase
):
...
@@ -2635,6 +2770,355 @@ class TestUnicode(unittest.TestCase):
...
@@ -2635,6 +2770,355 @@ class TestUnicode(unittest.TestCase):
inner
=
A
().
__of__
(
outer
)
inner
=
A
().
__of__
(
outer
)
self
.
assertEqual
(
u'True'
,
unicode
(
inner
))
self
.
assertEqual
(
u'True'
,
unicode
(
inner
))
class
TestProxying
(
unittest
.
TestCase
):
__binary_numeric_methods__
=
[
'__add__'
,
'__sub__'
,
'__mul__'
,
# '__floordiv__', # not implemented in C
'__mod__'
,
'__divmod__'
,
'__pow__'
,
'__lshift__'
,
'__rshift__'
,
'__and__'
,
'__xor__'
,
'__or__'
,
# division
'__truediv__'
,
'__div__'
,
# reflected
'__radd__'
,
'__rsub__'
,
'__rmul__'
,
'__rdiv__'
,
'__rtruediv__'
,
'__rfloordiv__'
,
'__rmod__'
,
'__rdivmod__'
,
'__rpow__'
,
'__rlshift__'
,
'__rrshift__'
,
'__rand__'
,
'__rxor__'
,
'__ror__'
,
# in place
'__iadd__'
,
'__isub__'
,
'__imul__'
,
'__idiv__'
,
'__itruediv__'
,
'__ifloordiv__'
,
'__imod__'
,
'__idivmod__'
,
'__ipow__'
,
'__ilshift__'
,
'__irshift__'
,
'__iand__'
,
'__ixor__'
,
'__ior__'
,
# conversion
# implementing it messes up all the arithmetic tests
#'__coerce__',
]
__unary_special_methods__
=
[
# arithmetic
'__neg__'
,
'__pos__'
,
'__abs__'
,
'__invert__'
,
]
__unary_conversion_methods__
=
{
# conversion
'__complex__'
:
complex
,
'__int__'
:
int
,
'__long__'
:
long
,
'__float__'
:
float
,
'__oct__'
:
oct
,
'__hex__'
:
hex
,
'__len__'
:
lambda
o
:
o
if
isinstance
(
o
,
int
)
else
len
(
o
),
#'__index__': operator.index, # not implemented in C
}
def
_check_special_methods
(
self
,
base_class
=
Acquisition
.
Implicit
):
"Check that special methods are proxied when called implicitly by the interpreter"
def
binary_acquired_func
(
self
,
other
):
return
self
.
value
def
unary_acquired_func
(
self
):
return
self
.
value
acquire_meths
=
{}
for
k
in
self
.
__binary_numeric_methods__
:
acquire_meths
[
k
]
=
binary_acquired_func
for
k
in
self
.
__unary_special_methods__
:
acquire_meths
[
k
]
=
unary_acquired_func
def
make_converter
(
f
):
def
converter
(
self
,
*
args
):
return
f
(
self
.
value
)
return
converter
for
k
,
convert
in
self
.
__unary_conversion_methods__
.
items
():
acquire_meths
[
k
]
=
make_converter
(
convert
)
acquire_meths
[
'__len__'
]
=
lambda
self
:
self
.
value
if
PY3
:
# Under Python 3, oct() and hex() call __index__ directly
acquire_meths
[
'__index__'
]
=
acquire_meths
[
'__int__'
]
if
base_class
==
Acquisition
.
Explicit
:
acquire_meths
[
'value'
]
=
Acquisition
.
Acquired
AcquireValue
=
type
(
'AcquireValue'
,
(
base_class
,),
acquire_meths
)
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
42
base
.
derived
=
AcquireValue
()
# one syntax check for the heck of it
self
.
assertEqual
(
base
.
value
,
base
.
derived
+
1
)
# divmod is not in the operator module
self
.
assertEqual
(
base
.
value
,
divmod
(
base
.
derived
,
1
))
_found_at_least_one_div
=
False
for
meth
in
self
.
__binary_numeric_methods__
:
# called on the instance
self
.
assertEqual
(
base
.
value
,
getattr
(
base
.
derived
,
meth
)(
-
1
))
# called on the type, as the interpreter does
# Note that the C version can only implement either __truediv__
# or __div__, not both
op
=
getattr
(
operator
,
meth
,
None
)
if
op
is
not
None
:
try
:
self
.
assertEqual
(
base
.
value
,
op
(
base
.
derived
,
1
))
if
meth
in
(
'__div__'
,
'__truediv__'
):
_found_at_least_one_div
=
True
except
TypeError
:
if
meth
in
(
'__div__'
,
'__truediv__'
):
pass
self
.
assertTrue
(
_found_at_least_one_div
,
"Must implement at least one of __div__ and __truediv__"
)
# Unary methods
for
meth
in
self
.
__unary_special_methods__
:
self
.
assertEqual
(
base
.
value
,
getattr
(
base
.
derived
,
meth
)())
op
=
getattr
(
operator
,
meth
)
self
.
assertEqual
(
base
.
value
,
op
(
base
.
derived
)
)
# Conversion functions
for
meth
,
converter
in
self
.
__unary_conversion_methods__
.
items
():
if
not
converter
:
continue
self
.
assertEqual
(
converter
(
base
.
value
),
getattr
(
base
.
derived
,
meth
)())
self
.
assertEqual
(
converter
(
base
.
value
),
converter
(
base
.
derived
))
def
test_implicit_proxy_special_meths
(
self
):
self
.
_check_special_methods
()
def
test_explicit_proxy_special_meths
(
self
):
self
.
_check_special_methods
(
base_class
=
Acquisition
.
Explicit
)
def
_check_contains
(
self
,
base_class
=
Acquisition
.
Implicit
):
# Contains has lots of fallback behaviour
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
42
# The simple case is if the object implements contains itself
class
ReallyContains
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__contains__
(
self
,
item
):
return
self
.
value
==
item
base
.
derived
=
ReallyContains
()
self
.
assertTrue
(
42
in
base
.
derived
)
self
.
assertFalse
(
24
in
base
.
derived
)
# Iterable objects are NOT iterated
# XXX: Is this a bug in the C code? Shouldn't it do
# what the interpreter does and fallback to iteration?
class
IterContains
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__iter__
(
self
):
return
iter
((
42
,))
base
.
derived
=
IterContains
()
self
.
assertRaises
(
AttributeError
,
operator
.
contains
,
base
.
derived
,
42
)
def
test_implicit_proxy_contains
(
self
):
self
.
_check_contains
()
def
test_explicit_proxy_contains
(
self
):
self
.
_check_contains
(
base_class
=
Acquisition
.
Explicit
)
def
_check_call
(
self
,
base_class
=
Acquisition
.
Implicit
):
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
42
class
Callable
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__call__
(
self
,
arg
,
k
=
None
):
return
self
.
value
,
arg
,
k
base
.
derived
=
Callable
()
self
.
assertEqual
(
base
.
derived
(
1
,
k
=
2
),
(
42
,
1
,
2
))
if
not
PYPY
:
# XXX: This test causes certain versions
# of PyPy to segfault (at least 2.6.0-alpha1)
class
NotCallable
(
base_class
):
pass
base
.
derived
=
NotCallable
()
try
:
base
.
derived
()
self
.
fail
(
"Not callable"
)
except
(
TypeError
,
AttributeError
):
pass
def
test_implicit_proxy_call
(
self
):
self
.
_check_call
()
def
test_explicit_proxy_call
(
self
):
self
.
_check_call
(
base_class
=
Acquisition
.
Explicit
)
def
_check_hash
(
self
,
base_class
=
Acquisition
.
Implicit
):
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
B
()
base
.
value
.
hash
=
42
class
NoAcquired
(
base_class
):
def
__hash__
(
self
):
return
1
hashable
=
NoAcquired
()
base
.
derived
=
hashable
self
.
assertEqual
(
1
,
hash
(
hashable
))
self
.
assertEqual
(
1
,
hash
(
base
.
derived
))
# cannot access acquired attributes during
# __hash__
class
CannotAccessAcquiredAttributesAtHash
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__hash__
(
self
):
return
self
.
value
.
hash
hashable
=
CannotAccessAcquiredAttributesAtHash
()
base
.
derived
=
hashable
self
.
assertRaises
(
AttributeError
,
hash
,
hashable
)
self
.
assertRaises
(
AttributeError
,
hash
,
base
.
derived
)
def
test_implicit_proxy_hash
(
self
):
self
.
_check_hash
()
def
test_explicit_proxy_hash
(
self
):
self
.
_check_hash
(
base_class
=
Acquisition
.
Explicit
)
def
_check_comparison
(
self
,
base_class
=
Acquisition
.
Implicit
):
# Comparison behaviour is complex; see notes in _Wrapper
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
42
rich_cmp_methods
=
[
'__lt__'
,
'__gt__'
,
'__eq__'
,
'__ne__'
,
'__ge__'
,
'__le__'
]
def
_never_called
(
self
,
other
):
raise
RuntimeError
(
"This should never be called"
)
class
RichCmpNeverCalled
(
base_class
):
for
_name
in
rich_cmp_methods
:
locals
()[
_name
]
=
_never_called
base
.
derived
=
RichCmpNeverCalled
()
base
.
derived2
=
RichCmpNeverCalled
()
# We can access all of the operators, but only because
# they are masked
for
name
in
rich_cmp_methods
:
getattr
(
operator
,
name
)(
base
.
derived
,
base
.
derived2
)
self
.
assertFalse
(
base
.
derived2
==
base
.
derived
)
self
.
assertEquals
(
base
.
derived
,
base
.
derived
)
def
test_implicit_proxy_comporison
(
self
):
self
.
_check_comparison
()
def
test_explicit_proxy_comporison
(
self
):
self
.
_check_comparison
(
base_class
=
Acquisition
.
Explicit
)
def
_check_bool
(
self
,
base_class
=
Acquisition
.
Implicit
):
class
B
(
Acquisition
.
Implicit
):
pass
base
=
B
()
base
.
value
=
42
class
WithBool
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__nonzero__
(
self
):
return
bool
(
self
.
value
)
__bool__
=
__nonzero__
class
WithLen
(
base_class
):
if
base_class
is
Acquisition
.
Explicit
:
value
=
Acquisition
.
Acquired
def
__len__
(
self
):
return
self
.
value
class
WithNothing
(
base_class
):
pass
base
.
wbool
=
WithBool
()
base
.
wlen
=
WithLen
()
base
.
wnothing
=
WithNothing
()
self
.
assertEqual
(
bool
(
base
.
wbool
),
True
)
self
.
assertEqual
(
bool
(
base
.
wlen
),
True
)
self
.
assertEqual
(
bool
(
base
.
wnothing
),
True
)
base
.
value
=
0
self
.
assertFalse
(
base
.
wbool
)
self
.
assertFalse
(
base
.
wlen
)
def
test_implicit_proxy_bool
(
self
):
self
.
_check_bool
()
def
test_explicit_proxy_bool
(
self
):
self
.
_check_bool
(
base_class
=
Acquisition
.
Explicit
)
def
test_suite
():
def
test_suite
():
import
os.path
import
os.path
...
@@ -2647,6 +3131,7 @@ def test_suite():
...
@@ -2647,6 +3131,7 @@ def test_suite():
unittest
.
makeSuite
(
TestParent
),
unittest
.
makeSuite
(
TestParent
),
unittest
.
makeSuite
(
TestAcquire
),
unittest
.
makeSuite
(
TestAcquire
),
unittest
.
makeSuite
(
TestUnicode
),
unittest
.
makeSuite
(
TestUnicode
),
unittest
.
makeSuite
(
TestProxying
),
]
]
# This file is only available in a source checkout, skip it
# This file is only available in a source checkout, skip it
...
...
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