tested django-newsletter
This commit is contained in:
@@ -0,0 +1,697 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: Acquisition
|
||||
Version: 4.5
|
||||
Summary: Acquisition is a mechanism that allows objects to obtain attributes from the containment hierarchy they're in.
|
||||
Home-page: https://github.com/zopefoundation/Acquisition
|
||||
Author: Zope Foundation and Contributors
|
||||
Author-email: zope-dev@zope.org
|
||||
License: ZPL 2.1
|
||||
Description: Environmental Acquisiton
|
||||
========================
|
||||
|
||||
This package implements "environmental acquisiton" for Python, as
|
||||
proposed in the OOPSLA96_ paper by Joseph Gil and David H. Lorenz:
|
||||
|
||||
We propose a new programming paradigm, environmental acquisition in
|
||||
the context of object aggregation, in which objects acquire
|
||||
behaviour from their current containers at runtime. The key idea is
|
||||
that the behaviour of a component may depend upon its enclosing
|
||||
composite(s). In particular, we propose a form of feature sharing in
|
||||
which an object "inherits" features from the classes of objects in
|
||||
its environment. By examining the declaration of classes, it is
|
||||
possible to determine which kinds of classes may contain a
|
||||
component, and which components must be contained in a given kind of
|
||||
composite. These relationships are the basis for language constructs
|
||||
that supports acquisition.
|
||||
|
||||
.. _OOPSLA96: http://www.cs.virginia.edu/~lorenz/papers/oopsla96/>`_:
|
||||
|
||||
.. contents::
|
||||
|
||||
Introductory Example
|
||||
--------------------
|
||||
|
||||
Zope implements acquisition with "Extension Class" mix-in classes. To
|
||||
use acquisition your classes must inherit from an acquisition base
|
||||
class. For example::
|
||||
|
||||
>>> import ExtensionClass, Acquisition
|
||||
|
||||
>>> class C(ExtensionClass.Base):
|
||||
... color = 'red'
|
||||
|
||||
>>> class A(Acquisition.Implicit):
|
||||
... def report(self):
|
||||
... print(self.color)
|
||||
...
|
||||
>>> a = A()
|
||||
>>> c = C()
|
||||
>>> c.a = a
|
||||
|
||||
>>> c.a.report()
|
||||
red
|
||||
|
||||
>>> d = C()
|
||||
>>> d.color = 'green'
|
||||
>>> d.a = a
|
||||
|
||||
>>> d.a.report()
|
||||
green
|
||||
|
||||
>>> try:
|
||||
... a.report()
|
||||
... except AttributeError:
|
||||
... pass
|
||||
... else:
|
||||
... raise AssertionError('AttributeError not raised.')
|
||||
|
||||
The class ``A`` inherits acquisition behavior from
|
||||
``Acquisition.Implicit``. The object, ``a``, "has" the color of
|
||||
objects ``c`` and d when it is accessed through them, but it has no
|
||||
color by itself. The object ``a`` obtains attributes from its
|
||||
environment, where its environment is defined by the access path used
|
||||
to reach ``a``.
|
||||
|
||||
Acquisition Wrappers
|
||||
--------------------
|
||||
|
||||
When an object that supports acquisition is accessed through an
|
||||
extension class instance, a special object, called an acquisition
|
||||
wrapper, is returned. In the example above, the expression ``c.a``
|
||||
returns an acquisition wrapper that contains references to both ``c``
|
||||
and ``a``. It is this wrapper that performs attribute lookup in ``c``
|
||||
when an attribute cannot be found in ``a``.
|
||||
|
||||
Acquisition wrappers provide access to the wrapped objects through the
|
||||
attributes ``aq_parent``, ``aq_self``, ``aq_base``. Continue the
|
||||
example from above::
|
||||
|
||||
>>> c.a.aq_parent is c
|
||||
True
|
||||
>>> c.a.aq_self is a
|
||||
True
|
||||
|
||||
Explicit and Implicit Acquisition
|
||||
---------------------------------
|
||||
|
||||
Two styles of acquisition are supported: implicit and explicit
|
||||
acquisition.
|
||||
|
||||
Implicit acquisition
|
||||
--------------------
|
||||
|
||||
Implicit acquisition is so named because it searches for attributes
|
||||
from the environment automatically whenever an attribute cannot be
|
||||
obtained directly from an object or through inheritance.
|
||||
|
||||
An attribute can be implicitly acquired if its name does not begin
|
||||
with an underscore.
|
||||
|
||||
To support implicit acquisition, your class should inherit from the
|
||||
mix-in class ``Acquisition.Implicit``.
|
||||
|
||||
Explicit Acquisition
|
||||
--------------------
|
||||
|
||||
When explicit acquisition is used, attributes are not automatically
|
||||
obtained from the environment. Instead, the method aq_acquire must be
|
||||
used. For example::
|
||||
|
||||
>>> print(c.a.aq_acquire('color'))
|
||||
red
|
||||
|
||||
To support explicit acquisition, your class should inherit from the
|
||||
mix-in class ``Acquisition.Explicit``.
|
||||
|
||||
Controlling Acquisition
|
||||
-----------------------
|
||||
|
||||
A class (or instance) can provide attribute by attribute control over
|
||||
acquisition. Your should subclass from ``Acquisition.Explicit``, and set
|
||||
all attributes that should be acquired to the special value
|
||||
``Acquisition.Acquired``. Setting an attribute to this value also allows
|
||||
inherited attributes to be overridden with acquired ones. For example::
|
||||
|
||||
>>> class C(Acquisition.Explicit):
|
||||
... id = 1
|
||||
... secret = 2
|
||||
... color = Acquisition.Acquired
|
||||
... __roles__ = Acquisition.Acquired
|
||||
|
||||
The only attributes that are automatically acquired from containing
|
||||
objects are color, and ``__roles__``. Note that the ``__roles__``
|
||||
attribute is acquired even though its name begins with an
|
||||
underscore. In fact, the special ``Acquisition.Acquired`` value can be
|
||||
used in ``Acquisition.Implicit`` objects to implicitly acquire
|
||||
selected objects that smell like private objects.
|
||||
|
||||
Sometimes, you want to dynamically make an implicitly acquiring object
|
||||
acquire explicitly. You can do this by getting the object's
|
||||
aq_explicit attribute. This attribute provides the object with an
|
||||
explicit wrapper that replaces the original implicit wrapper.
|
||||
|
||||
Filtered Acquisition
|
||||
--------------------
|
||||
|
||||
The acquisition method, ``aq_acquire``, accepts two optional
|
||||
arguments. The first of the additional arguments is a "filtering"
|
||||
function that is used when considering whether to acquire an
|
||||
object. The second of the additional arguments is an object that is
|
||||
passed as extra data when calling the filtering function and which
|
||||
defaults to ``None``. The filter function is called with five
|
||||
arguments:
|
||||
|
||||
* The object that the aq_acquire method was called on,
|
||||
|
||||
* The object where an object was found,
|
||||
|
||||
* The name of the object, as passed to aq_acquire,
|
||||
|
||||
* The object found, and
|
||||
|
||||
* The extra data passed to aq_acquire.
|
||||
|
||||
If the filter returns a true object that the object found is returned,
|
||||
otherwise, the acquisition search continues.
|
||||
|
||||
Here's an example::
|
||||
|
||||
>>> from Acquisition import Explicit
|
||||
|
||||
>>> class HandyForTesting(object):
|
||||
... def __init__(self, name):
|
||||
... self.name = name
|
||||
... def __str__(self):
|
||||
... return "%s(%s)" % (self.name, self.__class__.__name__)
|
||||
... __repr__=__str__
|
||||
...
|
||||
>>> class E(Explicit, HandyForTesting): pass
|
||||
...
|
||||
>>> class Nice(HandyForTesting):
|
||||
... isNice = 1
|
||||
... def __str__(self):
|
||||
... return HandyForTesting.__str__(self)+' and I am nice!'
|
||||
... __repr__ = __str__
|
||||
...
|
||||
>>> a = E('a')
|
||||
>>> a.b = E('b')
|
||||
>>> a.b.c = E('c')
|
||||
>>> a.p = Nice('spam')
|
||||
>>> a.b.p = E('p')
|
||||
|
||||
>>> def find_nice(self, ancestor, name, object, extra):
|
||||
... return hasattr(object,'isNice') and object.isNice
|
||||
|
||||
>>> print(a.b.c.aq_acquire('p', find_nice))
|
||||
spam(Nice) and I am nice!
|
||||
|
||||
The filtered acquisition in the last line skips over the first
|
||||
attribute it finds with the name ``p``, because the attribute doesn't
|
||||
satisfy the condition given in the filter.
|
||||
|
||||
Filtered acquisition is rarely used in Zope.
|
||||
|
||||
Acquiring from Context
|
||||
----------------------
|
||||
|
||||
Normally acquisition allows objects to acquire data from their
|
||||
containers. However an object can acquire from objects that aren't its
|
||||
containers.
|
||||
|
||||
Most of the examples we've seen so far show establishing of an
|
||||
acquisition context using getattr semantics. For example, ``a.b`` is a
|
||||
reference to ``b`` in the context of ``a``.
|
||||
|
||||
You can also manually set acquisition context using the ``__of__``
|
||||
method. For example::
|
||||
|
||||
>>> from Acquisition import Implicit
|
||||
>>> class C(Implicit): pass
|
||||
...
|
||||
>>> a = C()
|
||||
>>> b = C()
|
||||
>>> a.color = "red"
|
||||
>>> print(b.__of__(a).color)
|
||||
red
|
||||
|
||||
In this case, ``a`` does not contain ``b``, but it is put in ``b``'s
|
||||
context using the ``__of__`` method.
|
||||
|
||||
Here's another subtler example that shows how you can construct an
|
||||
acquisition context that includes non-container objects::
|
||||
|
||||
>>> from Acquisition import Implicit
|
||||
|
||||
>>> class C(Implicit):
|
||||
... def __init__(self, name):
|
||||
... self.name = name
|
||||
|
||||
>>> a = C("a")
|
||||
>>> a.b = C("b")
|
||||
>>> a.b.color = "red"
|
||||
>>> a.x = C("x")
|
||||
|
||||
>>> print(a.b.x.color)
|
||||
red
|
||||
|
||||
Even though ``b`` does not contain ``x``, ``x`` can acquire the color
|
||||
attribute from ``b``. This works because in this case, ``x`` is accessed
|
||||
in the context of ``b`` even though it is not contained by ``b``.
|
||||
|
||||
Here acquisition context is defined by the objects used to access
|
||||
another object.
|
||||
|
||||
Containment Before Context
|
||||
--------------------------
|
||||
|
||||
If in the example above suppose both a and b have an color attribute::
|
||||
|
||||
>>> a = C("a")
|
||||
>>> a.color = "green"
|
||||
>>> a.b = C("b")
|
||||
>>> a.b.color = "red"
|
||||
>>> a.x = C("x")
|
||||
|
||||
>>> print(a.b.x.color)
|
||||
green
|
||||
|
||||
Why does ``a.b.x.color`` acquire color from ``a`` and not from ``b``?
|
||||
The answer is that an object acquires from its containers before
|
||||
non-containers in its context.
|
||||
|
||||
To see why consider this example in terms of expressions using the
|
||||
``__of__`` method::
|
||||
|
||||
a.x -> x.__of__(a)
|
||||
|
||||
a.b -> b.__of__(a)
|
||||
|
||||
a.b.x -> x.__of__(a).__of__(b.__of__(a))
|
||||
|
||||
Keep in mind that attribute lookup in a wrapper is done by trying to
|
||||
look up the attribute in the wrapped object first and then in the
|
||||
parent object. So in the expressions above proceeds from left to
|
||||
right.
|
||||
|
||||
The upshot of these rules is that attributes are looked up by
|
||||
containment before context.
|
||||
|
||||
This rule holds true also for more complex examples. For example,
|
||||
``a.b.c.d.e.f.g.attribute`` would search for attribute in ``g`` and
|
||||
all its containers first. (Containers are searched in order from the
|
||||
innermost parent to the outermost container.) If the attribute is not
|
||||
found in ``g`` or any of its containers, then the search moves to
|
||||
``f`` and all its containers, and so on.
|
||||
|
||||
Additional Attributes and Methods
|
||||
---------------------------------
|
||||
|
||||
You can use the special method ``aq_inner`` to access an object
|
||||
wrapped only by containment. So in the example above,
|
||||
``a.b.x.aq_inner`` is equivalent to ``a.x``.
|
||||
|
||||
You can find out the acquisition context of an object using the
|
||||
aq_chain method like so:
|
||||
|
||||
>>> [obj.name for obj in a.b.x.aq_chain]
|
||||
['x', 'b', 'a']
|
||||
|
||||
You can find out if an object is in the containment context of another
|
||||
object using the ``aq_inContextOf`` method. For example:
|
||||
|
||||
>>> a.b.aq_inContextOf(a)
|
||||
True
|
||||
|
||||
.. Note: as of this writing the aq_inContextOf examples don't work the
|
||||
way they should be working. According to Jim, this is because
|
||||
aq_inContextOf works by comparing object pointer addresses, which
|
||||
(because they are actually different wrapper objects) doesn't give
|
||||
you the expected results. He acknowledges that this behavior is
|
||||
controversial, and says that there is a collector entry to change
|
||||
it so that you would get the answer you expect in the above. (We
|
||||
just need to get to it).
|
||||
|
||||
Acquisition Module Functions
|
||||
----------------------------
|
||||
|
||||
In addition to using acquisition attributes and methods directly on
|
||||
objects you can use similar functions defined in the ``Acquisition``
|
||||
module. These functions have the advantage that you don't need to
|
||||
check to make sure that the object has the method or attribute before
|
||||
calling it.
|
||||
|
||||
``aq_acquire(object, name [, filter, extra, explicit, default, containment])``
|
||||
Acquires an object with the given name.
|
||||
|
||||
This function can be used to explictly acquire when using explicit
|
||||
acquisition and to acquire names that wouldn't normally be
|
||||
acquired.
|
||||
|
||||
The function accepts a number of optional arguments:
|
||||
|
||||
``filter``
|
||||
A callable filter object that is used to decide if an object
|
||||
should be acquired.
|
||||
|
||||
The filter is called with five arguments:
|
||||
|
||||
* The object that the aq_acquire method was called on,
|
||||
|
||||
* The object where an object was found,
|
||||
|
||||
* The name of the object, as passed to aq_acquire,
|
||||
|
||||
* The object found, and
|
||||
|
||||
* The extra argument passed to aq_acquire.
|
||||
|
||||
If the filter returns a true object that the object found is
|
||||
returned, otherwise, the acquisition search continues.
|
||||
|
||||
``extra``
|
||||
Extra data to be passed as the last argument to the filter.
|
||||
|
||||
``explicit``
|
||||
A flag (boolean value) indicating whether explicit acquisition
|
||||
should be used. The default value is true. If the flag is
|
||||
true, then acquisition will proceed regardless of whether
|
||||
wrappers encountered in the search of the acquisition
|
||||
hierarchy are explicit or implicit wrappers. If the flag is
|
||||
false, then parents of explicit wrappers are not searched.
|
||||
|
||||
This argument is useful if you want to apply a filter without
|
||||
overriding explicit wrappers.
|
||||
|
||||
``default``
|
||||
A default value to return if no value can be acquired.
|
||||
|
||||
``containment``
|
||||
A flag indicating whether the search should be limited to the
|
||||
containment hierarchy.
|
||||
|
||||
In addition, arguments can be provided as keywords.
|
||||
|
||||
``aq_base(object)``
|
||||
Return the object with all wrapping removed.
|
||||
|
||||
``aq_chain(object [, containment])``
|
||||
Return a list containing the object and it's acquisition
|
||||
parents. The optional argument, containment, controls whether the
|
||||
containment or access hierarchy is used.
|
||||
|
||||
``aq_get(object, name [, default, containment])``
|
||||
Acquire an attribute, name. A default value can be provided, as
|
||||
can a flag that limits search to the containment hierarchy.
|
||||
|
||||
``aq_inner(object)``
|
||||
Return the object with all but the innermost layer of wrapping
|
||||
removed.
|
||||
|
||||
``aq_parent(object)``
|
||||
Return the acquisition parent of the object or None if the object
|
||||
is unwrapped.
|
||||
|
||||
``aq_self(object)``
|
||||
Return the object with one layer of wrapping removed, unless the
|
||||
object is unwrapped, in which case the object is returned.
|
||||
|
||||
In most cases it is more convenient to use these module functions
|
||||
instead of the acquisition attributes and methods directly.
|
||||
|
||||
Acquisition and Methods
|
||||
-----------------------
|
||||
|
||||
Python methods of objects that support acquisition can use acquired
|
||||
attributes. When a Python method is called on an object that is
|
||||
wrapped by an acquisition wrapper, the wrapper is passed to the method
|
||||
as the first argument. This rule also applies to user-defined method
|
||||
types and to C methods defined in pure mix-in classes.
|
||||
|
||||
Unfortunately, C methods defined in extension base classes that define
|
||||
their own data structures, cannot use aquired attributes at this
|
||||
time. This is because wrapper objects do not conform to the data
|
||||
structures expected by these methods. In practice, you will seldom
|
||||
find this a problem.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
Acquisition provides a powerful way to dynamically share information
|
||||
between objects. Zope 2 uses acquisition for a number of its key
|
||||
features including security, object publishing, and DTML variable
|
||||
lookup. Acquisition also provides an elegant solution to the problem
|
||||
of circular references for many classes of problems. While acquisition
|
||||
is powerful, you should take care when using acquisition in your
|
||||
applications. The details can get complex, especially with the
|
||||
differences between acquiring from context and acquiring from
|
||||
containment.
|
||||
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
4.5 (2018-10-05)
|
||||
----------------
|
||||
|
||||
- Avoid deprecation warnings by using current API.
|
||||
|
||||
- Add support for Python 3.7.
|
||||
|
||||
4.4.4 (2017-11-24)
|
||||
------------------
|
||||
|
||||
- Add Appveyor configuration to automate building Windows eggs.
|
||||
|
||||
4.4.3 (2017-11-23)
|
||||
------------------
|
||||
|
||||
- Fix the extremely rare potential for a crash when the C extensions
|
||||
are in use. See `issue 21 <https://github.com/zopefoundation/Acquisition/issues/21>`_.
|
||||
|
||||
4.4.2 (2017-05-12)
|
||||
------------------
|
||||
|
||||
- Fix C capsule name to fix import errors.
|
||||
|
||||
- Ensure our dependencies match our expactations about C extensions.
|
||||
|
||||
4.4.1 (2017-05-04)
|
||||
------------------
|
||||
|
||||
- Fix C code under Python 3.4, with missing Py_XSETREF.
|
||||
|
||||
4.4.0 (2017-05-04)
|
||||
------------------
|
||||
|
||||
- Enable the C extension under Python 3.
|
||||
|
||||
- Drop support for Python 3.3.
|
||||
|
||||
4.3.0 (2017-01-20)
|
||||
------------------
|
||||
|
||||
- Make tests compatible with ExtensionClass 4.2.0.
|
||||
|
||||
- Drop support for Python 2.6 and 3.2.
|
||||
|
||||
- Add support for Python 3.5 and 3.6.
|
||||
|
||||
4.2.2 (2015-05-19)
|
||||
------------------
|
||||
|
||||
- Make the pure-Python Acquirer objects cooperatively use the
|
||||
superclass ``__getattribute__`` method, like the C implementation.
|
||||
See https://github.com/zopefoundation/Acquisition/issues/7.
|
||||
|
||||
- The pure-Python implicit acquisition wrapper allows wrapped objects
|
||||
to use ``object.__getattribute__(self, name)``. This differs from
|
||||
the C implementation, but is important for compatibility with the
|
||||
pure-Python versions of libraries like ``persistent``. See
|
||||
https://github.com/zopefoundation/Acquisition/issues/9.
|
||||
|
||||
4.2.1 (2015-04-23)
|
||||
------------------
|
||||
|
||||
- Correct several dangling pointer uses in the C extension,
|
||||
potentially fixing a few interpreter crashes. See
|
||||
https://github.com/zopefoundation/Acquisition/issues/5.
|
||||
|
||||
4.2 (2015-04-04)
|
||||
----------------
|
||||
|
||||
- Add support for PyPy, PyPy3, and Python 3.2, 3.3, and 3.4.
|
||||
|
||||
4.1 (2014-12-18)
|
||||
----------------
|
||||
|
||||
- Bump dependency on ``ExtensionClass`` to match current release.
|
||||
|
||||
4.0.3 (2014-11-02)
|
||||
------------------
|
||||
|
||||
- Skip readme.rst tests when tests are run outside a source checkout.
|
||||
|
||||
4.0.2 (2014-11-02)
|
||||
------------------
|
||||
|
||||
- Include ``*.rst`` files in the release.
|
||||
|
||||
4.0.1 (2014-10-30)
|
||||
------------------
|
||||
|
||||
- Tolerate Unicode attribute names (ASCII only). LP #143358.
|
||||
|
||||
- Make module-level ``aq_acquire`` API respect the ``default`` parameter.
|
||||
LP #1387363.
|
||||
|
||||
- Don't raise an attribute error for ``__iter__`` if the fallback to
|
||||
``__getitem__`` succeeds. LP #1155760.
|
||||
|
||||
|
||||
4.0 (2013-02-24)
|
||||
----------------
|
||||
|
||||
- Added trove classifiers to project metadata.
|
||||
|
||||
4.0a1 (2011-12-13)
|
||||
------------------
|
||||
|
||||
- Raise `RuntimeError: Recursion detected in acquisition wrapper` if an object
|
||||
with a `__parent__` pointer points to a wrapper that in turn points to the
|
||||
original object.
|
||||
|
||||
- Prevent wrappers to be created while accessing `__parent__` on types derived
|
||||
from Explicit or Implicit base classes.
|
||||
|
||||
2.13.9 (2015-02-17)
|
||||
-------------------
|
||||
|
||||
- Tolerate Unicode attribute names (ASCII only). LP #143358.
|
||||
|
||||
- Make module-level ``aq_acquire`` API respect the ``default`` parameter.
|
||||
LP #1387363.
|
||||
|
||||
- Don't raise an attribute error for ``__iter__`` if the fallback to
|
||||
``__getitem__`` succeeds. LP #1155760.
|
||||
|
||||
2.13.8 (2011-06-11)
|
||||
-------------------
|
||||
|
||||
- Fixed a segfault on 64bit platforms when providing the `explicit` argument to
|
||||
the aq_acquire method of an Acquisition wrapper. Thx to LP #675064 for the
|
||||
hint to the solution. The code passed an int instead of a pointer into a
|
||||
function.
|
||||
|
||||
2.13.7 (2011-03-02)
|
||||
-------------------
|
||||
|
||||
- Fixed bug: When an object did not implement ``__unicode__``, calling
|
||||
``unicode(wrapped)`` was calling ``__str__`` with an unwrapped ``self``.
|
||||
|
||||
2.13.6 (2011-02-19)
|
||||
-------------------
|
||||
|
||||
- Add ``aq_explicit`` to ``IAcquisitionWrapper``.
|
||||
|
||||
- Fixed bug: ``unicode(wrapped)`` was not calling a ``__unicode__``
|
||||
method on wrapped objects.
|
||||
|
||||
2.13.5 (2010-09-29)
|
||||
-------------------
|
||||
|
||||
- Fixed unit tests that failed on 64bit Python on Windows machines.
|
||||
|
||||
2.13.4 (2010-08-31)
|
||||
-------------------
|
||||
|
||||
- LP 623665: Fixed typo in Acquisition.h.
|
||||
|
||||
2.13.3 (2010-04-19)
|
||||
-------------------
|
||||
|
||||
- Use the doctest module from the standard library and no longer depend on
|
||||
zope.testing.
|
||||
|
||||
2.13.2 (2010-04-04)
|
||||
-------------------
|
||||
|
||||
- Give both wrapper classes a ``__getnewargs__`` method, which causes the ZODB
|
||||
optimization to fail and create persistent references using the ``_p_oid``
|
||||
alone. This happens to be the persistent oid of the wrapped object. This lets
|
||||
these objects to be persisted correctly, even though they are passed to the
|
||||
ZODB in a wrapped state.
|
||||
|
||||
- Added failing tests for http://dev.plone.org/plone/ticket/10318. This shows
|
||||
an edge-case where AQ wrappers can be pickled using the specific combination
|
||||
of cPickle, pickle protocol one and a custom Pickler class with an
|
||||
``inst_persistent_id`` hook. Unfortunately this is the exact combination used
|
||||
by ZODB3.
|
||||
|
||||
2.13.1 (2010-02-23)
|
||||
-------------------
|
||||
|
||||
- Update to include ExtensionClass 2.13.0.
|
||||
|
||||
- Fix the ``tp_name`` of the ImplicitAcquisitionWrapper and
|
||||
ExplicitAcquisitionWrapper to match their Python visible names and thus have
|
||||
a correct ``__name__``.
|
||||
|
||||
- Expand the ``tp_name`` of our extension types to hold the fully qualified
|
||||
name. This ensures classes have their ``__module__`` set correctly.
|
||||
|
||||
2.13.0 (2010-02-14)
|
||||
-------------------
|
||||
|
||||
- Added support for method cache in Acquisition. Patch contributed by
|
||||
Yoshinori K. Okuji. See https://bugs.launchpad.net/zope2/+bug/486182.
|
||||
|
||||
2.12.4 (2009-10-29)
|
||||
-------------------
|
||||
|
||||
- Fix iteration proxying to pass `self` acquisition-wrapped into both
|
||||
`__iter__` as well as `__getitem__` (this fixes
|
||||
https://bugs.launchpad.net/zope2/+bug/360761).
|
||||
|
||||
- Add tests for the __getslice__ proxying, including open-ended slicing.
|
||||
|
||||
2.12.3 (2009-08-08)
|
||||
-------------------
|
||||
|
||||
- More 64-bit fixes in Py_BuildValue calls.
|
||||
|
||||
- More 64-bit issues fixed: Use correct integer size for slice operations.
|
||||
|
||||
2.12.2 (2009-08-02)
|
||||
-------------------
|
||||
|
||||
- Fixed 64-bit compatibility issues for Python 2.5.x / 2.6.x. See
|
||||
http://www.python.org/dev/peps/pep-0353/ for details.
|
||||
|
||||
2.12.1 (2009-04-15)
|
||||
-------------------
|
||||
|
||||
- Update for iteration proxying: The proxy for `__iter__` must not rely on the
|
||||
object to have an `__iter__` itself, but also support fall-back iteration via
|
||||
`__getitem__` (this fixes https://bugs.launchpad.net/zope2/+bug/360761).
|
||||
|
||||
2.12 (2009-01-25)
|
||||
-----------------
|
||||
|
||||
- Release as separate package.
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Framework :: Zope2
|
||||
Classifier: License :: OSI Approved :: Zope Public License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
@@ -0,0 +1,25 @@
|
||||
.coveragerc
|
||||
.gitignore
|
||||
CHANGES.rst
|
||||
COPYRIGHT.txt
|
||||
LICENSE.txt
|
||||
MANIFEST.in
|
||||
README.rst
|
||||
buildout.cfg
|
||||
pip-delete-this-directory.txt
|
||||
setup.cfg
|
||||
setup.py
|
||||
tox.ini
|
||||
include/ExtensionClass/ExtensionClass.h
|
||||
include/ExtensionClass/_compat.h
|
||||
src/Acquisition/Acquisition.h
|
||||
src/Acquisition/_Acquisition.c
|
||||
src/Acquisition/__init__.py
|
||||
src/Acquisition/interfaces.py
|
||||
src/Acquisition/tests.py
|
||||
src/Acquisition.egg-info/PKG-INFO
|
||||
src/Acquisition.egg-info/SOURCES.txt
|
||||
src/Acquisition.egg-info/dependency_links.txt
|
||||
src/Acquisition.egg-info/not-zip-safe
|
||||
src/Acquisition.egg-info/requires.txt
|
||||
src/Acquisition.egg-info/top_level.txt
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
../Acquisition/Acquisition.h
|
||||
../Acquisition/_Acquisition.c
|
||||
../Acquisition/_Acquisition.cpython-36m-darwin.so
|
||||
../Acquisition/__init__.py
|
||||
../Acquisition/__pycache__/__init__.cpython-36.pyc
|
||||
../Acquisition/__pycache__/interfaces.cpython-36.pyc
|
||||
../Acquisition/__pycache__/tests.cpython-36.pyc
|
||||
../Acquisition/interfaces.py
|
||||
../Acquisition/tests.py
|
||||
PKG-INFO
|
||||
SOURCES.txt
|
||||
dependency_links.txt
|
||||
not-zip-safe
|
||||
requires.txt
|
||||
top_level.txt
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ExtensionClass>=4.2.0
|
||||
zope.interface
|
||||
@@ -0,0 +1 @@
|
||||
Acquisition
|
||||
@@ -0,0 +1,53 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996-2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ACQUISITION_H_
|
||||
#define __ACQUISITION_H_
|
||||
|
||||
typedef struct {
|
||||
PyObject *(*AQ_Acquire) (PyObject *obj, PyObject *name, PyObject *filter,
|
||||
PyObject *extra, int explicit, PyObject *deflt,
|
||||
int containment);
|
||||
PyObject *(*AQ_Get) (PyObject *obj, PyObject *name, PyObject *deflt,
|
||||
int containment);
|
||||
int (*AQ_IsWrapper) (PyObject *obj);
|
||||
PyObject *(*AQ_Base) (PyObject *obj);
|
||||
PyObject *(*AQ_Parent) (PyObject *obj);
|
||||
PyObject *(*AQ_Self) (PyObject *obj);
|
||||
PyObject *(*AQ_Inner) (PyObject *obj);
|
||||
PyObject *(*AQ_Chain) (PyObject *obj, int containment);
|
||||
} ACQUISITIONCAPI;
|
||||
|
||||
#ifndef _IN_ACQUISITION_C
|
||||
|
||||
#define aq_Acquire(obj, name, filter, extra, explicit, deflt, containment ) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, filter, extra, explicit, deflt, containment)))
|
||||
#define aq_acquire(obj, name) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, NULL, NULL, 1, NULL, 0)))
|
||||
#define aq_get(obj, name, deflt, containment) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Get(obj, name, deflt, containment)))
|
||||
#define aq_isWrapper(obj) (AcquisitionCAPI == NULL ? -1 : (AcquisitionCAPI->AQ_IsWrapper(obj)))
|
||||
#define aq_base(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Base(obj)))
|
||||
#define aq_parent(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Parent(obj)))
|
||||
#define aq_self(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Self(obj)))
|
||||
#define aq_inner(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Inner(obj)))
|
||||
#define aq_chain(obj, containment) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_CHain(obj, containment)))
|
||||
|
||||
static ACQUISITIONCAPI *AcquisitionCAPI = NULL;
|
||||
|
||||
#define aq_init() { \
|
||||
AcquisitionCAPI = PyCapsule_Import("Acquisition.AcquisitionCAPI", 0); \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -0,0 +1,953 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
# pylint:disable=W0212,R0911,R0912
|
||||
|
||||
|
||||
import os
|
||||
import operator
|
||||
import platform
|
||||
import sys
|
||||
import types
|
||||
import weakref
|
||||
|
||||
import ExtensionClass
|
||||
|
||||
from zope.interface import classImplements
|
||||
|
||||
from .interfaces import IAcquirer
|
||||
from .interfaces import IAcquisitionWrapper
|
||||
|
||||
IS_PYPY = getattr(platform, 'python_implementation', lambda: None)() == 'PyPy'
|
||||
IS_PURE = 'PURE_PYTHON' in os.environ
|
||||
|
||||
|
||||
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,):
|
||||
import copy_reg
|
||||
|
||||
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
|
||||
exec("""def _reraise(tp, value, tb=None):
|
||||
raise tp, value, tb
|
||||
""")
|
||||
else: # pragma: no cover (python 2 is currently our reference)
|
||||
import copyreg as copy_reg
|
||||
|
||||
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
|
||||
|
||||
def _reraise(tp, value, tb=None):
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
###
|
||||
# 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):
|
||||
return (result.__of__(wrapper)
|
||||
if _has__of__(result) else result)
|
||||
else:
|
||||
raise AttributeError(name)
|
||||
else:
|
||||
if _has__of__(result):
|
||||
result = result.__of__(wrapper)
|
||||
return result
|
||||
|
||||
# this line cannot be reached
|
||||
raise AttributeError(name) # pragma: no cover
|
||||
|
||||
|
||||
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:
|
||||
if _apply_filter(predicate, wrapper, orig_name,
|
||||
result, predicate_extra, orig_object):
|
||||
return result
|
||||
else:
|
||||
raise AttributeError(orig_name)
|
||||
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), # NOQA
|
||||
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
|
||||
_OGA = object.__getattribute__
|
||||
|
||||
# Map from object types with slots to their generated, derived
|
||||
# types (or None if no derived type is needed)
|
||||
_wrapper_subclass_cache = weakref.WeakKeyDictionary()
|
||||
|
||||
|
||||
def _make_wrapper_subclass_if_needed(cls, obj, container):
|
||||
# If the type of an object to be wrapped has __slots__, then we
|
||||
# must create a wrapper subclass that has descriptors for those
|
||||
# same slots. In this way, its methods that use object.__getattribute__
|
||||
# directly will continue to work, even when given an instance of _Wrapper
|
||||
if getattr(cls, '_Wrapper__DERIVED', False):
|
||||
return None
|
||||
type_obj = type(obj)
|
||||
wrapper_subclass = _wrapper_subclass_cache.get(type_obj, _NOT_GIVEN)
|
||||
if wrapper_subclass is _NOT_GIVEN:
|
||||
slotnames = copy_reg._slotnames(type_obj)
|
||||
if slotnames and not isinstance(obj, _Wrapper):
|
||||
new_type_dict = {'_Wrapper__DERIVED': True}
|
||||
|
||||
def _make_property(slotname):
|
||||
return property(lambda s: getattr(s._obj, slotname),
|
||||
lambda s, v: setattr(s._obj, slotname, v),
|
||||
lambda s: delattr(s._obj, slotname))
|
||||
for slotname in slotnames:
|
||||
new_type_dict[slotname] = _make_property(slotname)
|
||||
new_type = type(cls.__name__ + '_' + type_obj.__name__,
|
||||
(cls,),
|
||||
new_type_dict)
|
||||
else:
|
||||
new_type = None
|
||||
wrapper_subclass = _wrapper_subclass_cache[type_obj] = new_type
|
||||
|
||||
return wrapper_subclass
|
||||
|
||||
|
||||
class _Wrapper(ExtensionClass.Base):
|
||||
__slots__ = ('_obj', '_container', '__dict__')
|
||||
_IS_IMPLICIT = None
|
||||
|
||||
def __new__(cls, obj, container):
|
||||
wrapper_subclass = _make_wrapper_subclass_if_needed(cls, obj, container) # NOQA
|
||||
if wrapper_subclass:
|
||||
inst = wrapper_subclass(obj, container)
|
||||
else:
|
||||
inst = super(_Wrapper, cls).__new__(cls)
|
||||
inst._obj = obj
|
||||
inst._container = container
|
||||
if hasattr(obj, '__dict__') and not isinstance(obj, _Wrapper):
|
||||
# Make our __dict__ refer to the same dict as the other object,
|
||||
# so that if it has methods that use `object.__getattribute__`
|
||||
# they still work. Note that because we have slots,
|
||||
# we won't interfere with the contents of that dict.
|
||||
object.__setattr__(inst, '__dict__', obj.__dict__)
|
||||
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 _OGA(self, name)
|
||||
if (_OGA(self, '_obj') is not None or
|
||||
_OGA(self, '_container') is not None):
|
||||
return _Wrapper_findattr(self, name, None, None, None, True,
|
||||
type(self)._IS_IMPLICIT, False, False)
|
||||
return _OGA(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
|
||||
# XXX: This comes from the C implementation. Do we really need to
|
||||
# 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, # NOQA
|
||||
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(aq_self.__cmp__, 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
|
||||
try:
|
||||
return _rebound_method(aq_self.__repr__, self)()
|
||||
except (AttributeError, TypeError):
|
||||
return repr(aq_self)
|
||||
|
||||
def __str__(self):
|
||||
aq_self = self._obj
|
||||
try:
|
||||
return _rebound_method(aq_self.__str__, self)()
|
||||
except (AttributeError, TypeError): # pragma: no cover (Only Py3)
|
||||
return str(aq_self)
|
||||
|
||||
__binary_special_methods__ = [
|
||||
# general numeric
|
||||
'__add__',
|
||||
'__sub__',
|
||||
'__mul__',
|
||||
'__matmul__',
|
||||
'__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__',
|
||||
'__imatmul__',
|
||||
'__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
|
||||
try:
|
||||
setter = type(aq_self).__setitem__
|
||||
except AttributeError:
|
||||
raise AttributeError("__setitem__") # doctests care about the name
|
||||
else:
|
||||
setter(self, key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, slice) and hasattr(operator, 'getslice'):
|
||||
# Only on Python 2
|
||||
# XXX: This is probably not proxying correctly, but the existing
|
||||
# tests pass with this behaviour
|
||||
return operator.getslice(
|
||||
self._obj,
|
||||
key.start if key.start is not None else 0,
|
||||
key.stop if key.stop is not None else sys.maxint)
|
||||
|
||||
aq_self = self._obj
|
||||
try:
|
||||
getter = type(aq_self).__getitem__
|
||||
except AttributeError:
|
||||
raise AttributeError("__getitem__") # doctests care about the name
|
||||
else:
|
||||
return getter(self, 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: # pragma: no cover
|
||||
# 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 super(_Acquirer, self).__getattribute__(name)
|
||||
except AttributeError:
|
||||
# the doctests have very specific error message
|
||||
# requirements (but at least we can preserve the traceback)
|
||||
_, _, tb = sys.exc_info()
|
||||
try:
|
||||
_reraise(AttributeError, AttributeError(name), tb)
|
||||
finally:
|
||||
del tb
|
||||
|
||||
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._obj
|
||||
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 True
|
||||
|
||||
if inner:
|
||||
self = aq_inner(next)
|
||||
if self is None: # pragma: no cover
|
||||
# This branch is normally impossible to hit,
|
||||
# it just mirrors a check in C
|
||||
break
|
||||
else:
|
||||
self = next
|
||||
|
||||
next = aq_parent(self)
|
||||
if next is None:
|
||||
break
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if not (IS_PYPY or IS_PURE): # pragma: no cover
|
||||
# Make sure we can import the C extension of our dependency.
|
||||
from ExtensionClass import _ExtensionClass # NOQA
|
||||
from ._Acquisition import * # NOQA
|
||||
|
||||
classImplements(Explicit, IAcquirer)
|
||||
classImplements(ExplicitAcquisitionWrapper, IAcquisitionWrapper)
|
||||
classImplements(Implicit, IAcquirer)
|
||||
classImplements(ImplicitAcquisitionWrapper, IAcquisitionWrapper)
|
||||
@@ -0,0 +1,62 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2005 Zope Foundation and Contributors.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Acquisition z3 interfaces.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
|
||||
|
||||
class IAcquirer(Interface):
|
||||
|
||||
"""Acquire attributes from containers.
|
||||
"""
|
||||
|
||||
def __of__(context):
|
||||
"""Get the object in a context.
|
||||
"""
|
||||
|
||||
|
||||
class IAcquisitionWrapper(Interface):
|
||||
|
||||
"""Wrapper object for acquisition.
|
||||
"""
|
||||
|
||||
def aq_acquire(name, filter=None, extra=None, explicit=True, default=0,
|
||||
containment=0):
|
||||
"""Get an attribute, acquiring it if necessary.
|
||||
"""
|
||||
|
||||
def aq_inContextOf(obj, inner=1):
|
||||
"""Test whether the object is currently in the context of the argument.
|
||||
"""
|
||||
|
||||
aq_base = Attribute(
|
||||
"""Get the object unwrapped.""")
|
||||
|
||||
aq_parent = Attribute(
|
||||
"""Get the parent of an object.""")
|
||||
|
||||
aq_self = Attribute(
|
||||
"""Get the object with the outermost wrapper removed.""")
|
||||
|
||||
aq_inner = Attribute(
|
||||
"""Get the object with all but the innermost wrapper removed.""")
|
||||
|
||||
aq_chain = Attribute(
|
||||
"""Get a list of objects in the acquisition environment.""")
|
||||
|
||||
aq_explicit = Attribute(
|
||||
"""Get the object with an explicit acquisition wrapper.""")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
AuthEncoding is a framework for handling LDAP style password hashes.
|
||||
|
||||
It is used in Zope2 but does not depend on any other Zope package.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
4.0.0 (2015-09-30)
|
||||
------------------
|
||||
|
||||
- Supporting Python 3.3 up to 3.5 and PyPy2.
|
||||
|
||||
- Added ``SHA256DigestScheme``.
|
||||
|
||||
|
||||
3.0.0 (2015-09-28)
|
||||
------------------
|
||||
|
||||
- Extracted from ``AccessControl 3.0.11``
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,54 @@
|
||||
Metadata-Version: 2.0
|
||||
Name: AuthEncoding
|
||||
Version: 4.0.0
|
||||
Summary: Framework for handling LDAP style password hashes.
|
||||
Home-page: https://github.com/zopefoundation/AuthEncoding
|
||||
Author: Zope Foundation and Contributors
|
||||
Author-email: zope-dev@zope.org
|
||||
License: ZPL 2.1
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Framework :: Zope2
|
||||
Classifier: License :: OSI Approved :: Zope Public License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
|
||||
Requires-Dist: six
|
||||
Provides-Extra: test
|
||||
Requires-Dist: pytest; extra == 'test'
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
AuthEncoding is a framework for handling LDAP style password hashes.
|
||||
|
||||
It is used in Zope2 but does not depend on any other Zope package.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
4.0.0 (2015-09-30)
|
||||
------------------
|
||||
|
||||
- Supporting Python 3.3 up to 3.5 and PyPy2.
|
||||
|
||||
- Added ``SHA256DigestScheme``.
|
||||
|
||||
|
||||
3.0.0 (2015-09-28)
|
||||
------------------
|
||||
|
||||
- Extracted from ``AccessControl 3.0.11``
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
AuthEncoding-4.0.0.dist-info/DESCRIPTION.rst,sha256=UKoEVLqA6eoGuaGlkVcj4mZJUnzBr3IdyZ4rLj9tBY4,381
|
||||
AuthEncoding-4.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
AuthEncoding-4.0.0.dist-info/METADATA,sha256=LMz4ZjZ94m7OrJJAs4_0fs1X4C-gvWPTEw8uGwX-LvY,1588
|
||||
AuthEncoding-4.0.0.dist-info/RECORD,,
|
||||
AuthEncoding-4.0.0.dist-info/RECORD.jws,,
|
||||
AuthEncoding-4.0.0.dist-info/WHEEL,sha256=5wvfB7GvgZAbKBSE9uX9Zbi6LCL-_KgezgHblXhCRnM,113
|
||||
AuthEncoding-4.0.0.dist-info/metadata.json,sha256=yBATAjh7I5YnUKtIQHGhhActRCg60BSsaAeM0r5dPFY,1294
|
||||
AuthEncoding-4.0.0.dist-info/top_level.txt,sha256=r_cFzzt28gdOYPoRX7HjrbsJE6G-ZPK0n3sWbqE4PdQ,13
|
||||
AuthEncoding/AuthEncoding.py,sha256=A82pAZQA-r1NSG7jBf1qDJn4utAQXMHMSF09Do35t0w,7265
|
||||
AuthEncoding/__init__.py,sha256=9fdxLPEIHDUW2ig-pE92y7BkZAHItLvvFpgXTs4byiU,791
|
||||
AuthEncoding/__pycache__/AuthEncoding.cpython-36.pyc,,
|
||||
AuthEncoding/__pycache__/__init__.cpython-36.pyc,,
|
||||
AuthEncoding/__pycache__/compat.cpython-36.pyc,,
|
||||
AuthEncoding/compat.py,sha256=o2nbexNN16x67B8xLKZRQQo_F4LheBnRtKgO---vuPY,347
|
||||
AuthEncoding/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
AuthEncoding/tests/__pycache__/__init__.cpython-36.pyc,,
|
||||
AuthEncoding/tests/__pycache__/test_AuthEncoding.cpython-36.pyc,,
|
||||
AuthEncoding/tests/test_AuthEncoding.py,sha256=zEj6Uz2eC3VPxmafNcy2ZvdSQLfVSu_Yf9X63X2XHfM,3814
|
||||
@@ -0,0 +1 @@
|
||||
{"payload": "eyJoYXNoIjogInNoYTI1Nj1FbUpVbjFTUEdYeXhKc2FnMWpxNGJkMnlnYW80TlhzY2hORW5iSEFISHdnIn0", "recipients": [{"header": "eyJhbGciOiAiRWQyNTUxOSIsICJqd2siOiB7Imt0eSI6ICJFZDI1NTE5IiwgInZrIjogInZUZU1lalVPMTV1SkQzY203enZPLV9oc0dMSld4ZlFNMEFFd2R5SUQyeTQifX0", "signature": "D1i5q57sD-x3nU_8L_49SwW7b--DjetQhUpnMDwDDui6PVsLfsAr65teCK_GbT4txrxkqzCHEMV8byYKzz4XDw"}]}
|
||||
@@ -0,0 +1,6 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.30.0.a0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"classifiers": ["Development Status :: 6 - Mature", "Environment :: Web Environment", "Framework :: Zope2", "License :: OSI Approved :: Zope Public License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP"], "extensions": {"python.details": {"contacts": [{"email": "zope-dev@zope.org", "name": "Zope Foundation and Contributors", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zopefoundation/AuthEncoding"}}}, "extras": ["test"], "generator": "bdist_wheel (0.30.0.a0)", "license": "ZPL 2.1", "metadata_version": "2.0", "name": "AuthEncoding", "run_requires": [{"extra": "test", "requires": ["pytest"]}, {"requires": ["six"]}], "summary": "Framework for handling LDAP style password hashes.", "version": "4.0.0"}
|
||||
@@ -0,0 +1 @@
|
||||
AuthEncoding
|
||||
@@ -0,0 +1,253 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002, 2015 Zope Foundation and Contributors.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import binascii
|
||||
import six
|
||||
from binascii import b2a_base64, a2b_base64
|
||||
from hashlib import sha1 as sha
|
||||
from hashlib import sha256
|
||||
from os import getpid
|
||||
import time
|
||||
from .compat import long, b, u
|
||||
|
||||
|
||||
# Use the system PRNG if possible
|
||||
import random
|
||||
try:
|
||||
random = random.SystemRandom()
|
||||
using_sysrandom = True
|
||||
except NotImplementedError:
|
||||
using_sysrandom = False
|
||||
|
||||
|
||||
def _reseed():
|
||||
if not using_sysrandom:
|
||||
# This is ugly, and a hack, but it makes things better than
|
||||
# the alternative of predictability. This re-seeds the PRNG
|
||||
# using a value that is hard for an attacker to predict, every
|
||||
# time a random string is required. This may change the
|
||||
# properties of the chosen random sequence slightly, but this
|
||||
# is better than absolute predictability.
|
||||
random.seed(sha256(
|
||||
"%s%s%s" % (random.getstate(), time.time(), getpid())
|
||||
).digest())
|
||||
|
||||
|
||||
def _choice(c):
|
||||
_reseed()
|
||||
return random.choice(c)
|
||||
|
||||
|
||||
def _randrange(r):
|
||||
_reseed()
|
||||
return random.randrange(r)
|
||||
|
||||
|
||||
def constant_time_compare(val1, val2):
|
||||
"""
|
||||
Returns True if the two strings are equal, False otherwise.
|
||||
|
||||
The time taken is independent of the number of characters that match.
|
||||
"""
|
||||
if len(val1) != len(val2):
|
||||
return False
|
||||
result = 0
|
||||
for x, y in zip(six.iterbytes(val1), six.iterbytes(val2)):
|
||||
result |= x ^ y
|
||||
return result == 0
|
||||
|
||||
|
||||
class PasswordEncryptionScheme: # An Interface
|
||||
|
||||
def encrypt(pw):
|
||||
"""
|
||||
Encrypt the provided plain text password.
|
||||
"""
|
||||
|
||||
def validate(reference, attempt):
|
||||
"""
|
||||
Validate the provided password string. Reference is the
|
||||
correct password, which may be encrypted; attempt is clear text
|
||||
password attempt.
|
||||
"""
|
||||
|
||||
|
||||
_schemes = []
|
||||
|
||||
|
||||
def registerScheme(id, s):
|
||||
'''
|
||||
Registers an LDAP password encoding scheme.
|
||||
'''
|
||||
_schemes.append((id, u'{%s}' % id, s))
|
||||
|
||||
|
||||
def listSchemes():
|
||||
return [id for id, prefix, scheme in _schemes]
|
||||
|
||||
|
||||
class SSHADigestScheme:
|
||||
'''
|
||||
SSHA is a modification of the SHA digest scheme with a salt
|
||||
starting at byte 20 of the base64-encoded string.
|
||||
'''
|
||||
# Source: http://developer.netscape.com/docs/technote/ldap/pass_sha.html
|
||||
|
||||
def generate_salt(self):
|
||||
# Salt can be any length, but not more than about 37 characters
|
||||
# because of limitations of the binascii module.
|
||||
# 7 is what Netscape's example used and should be enough.
|
||||
# All 256 characters are available.
|
||||
salt = b''
|
||||
for n in range(7):
|
||||
salt += six.int2byte(_randrange(256))
|
||||
return salt
|
||||
|
||||
def encrypt(self, pw):
|
||||
return self._encrypt_with_salt(pw, self.generate_salt())
|
||||
|
||||
def validate(self, reference, attempt):
|
||||
try:
|
||||
ref = a2b_base64(reference)
|
||||
except binascii.Error:
|
||||
# Not valid base64.
|
||||
return 0
|
||||
salt = ref[20:]
|
||||
compare = self._encrypt_with_salt(attempt, salt)
|
||||
return constant_time_compare(compare, reference)
|
||||
|
||||
def _encrypt_with_salt(self, pw, salt):
|
||||
pw = b(pw)
|
||||
return b2a_base64(sha(pw + salt).digest() + salt)[:-1]
|
||||
|
||||
registerScheme(u'SSHA', SSHADigestScheme())
|
||||
|
||||
|
||||
class SHADigestScheme:
|
||||
|
||||
def encrypt(self, pw):
|
||||
return self._encrypt(pw)
|
||||
|
||||
def validate(self, reference, attempt):
|
||||
compare = self._encrypt(attempt)
|
||||
return constant_time_compare(compare, reference)
|
||||
|
||||
def _encrypt(self, pw):
|
||||
pw = b(pw)
|
||||
return b2a_base64(sha(pw).digest())[:-1]
|
||||
|
||||
|
||||
registerScheme(u'SHA', SHADigestScheme())
|
||||
|
||||
|
||||
class SHA256DigestScheme:
|
||||
|
||||
def encrypt(self, pw):
|
||||
return b(sha256(b(pw)).hexdigest())
|
||||
|
||||
def validate(self, reference, attempt):
|
||||
a = self.encrypt(attempt)
|
||||
return constant_time_compare(a, reference)
|
||||
|
||||
registerScheme(u'SHA256', SHA256DigestScheme())
|
||||
|
||||
|
||||
# Bogosity on various platforms due to ITAR restrictions
|
||||
try:
|
||||
from crypt import crypt
|
||||
except ImportError:
|
||||
crypt = None
|
||||
|
||||
if crypt is not None:
|
||||
|
||||
class CryptDigestScheme:
|
||||
|
||||
def generate_salt(self):
|
||||
choices = (u"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
u"abcdefghijklmnopqrstuvwxyz"
|
||||
u"0123456789./")
|
||||
return _choice(choices) + _choice(choices)
|
||||
|
||||
def encrypt(self, pw):
|
||||
return b(crypt(self._recode_password(pw), self.generate_salt()))
|
||||
|
||||
def validate(self, reference, attempt):
|
||||
attempt = self._recode_password(attempt)
|
||||
a = b(crypt(attempt, reference[:2].decode('ascii')))
|
||||
return constant_time_compare(a, reference)
|
||||
|
||||
def _recode_password(self, pw):
|
||||
# crypt always requires `str` which has a different meaning among
|
||||
# the Python versions:
|
||||
if six.PY3:
|
||||
return u(pw)
|
||||
return b(pw)
|
||||
|
||||
registerScheme(u'CRYPT', CryptDigestScheme())
|
||||
|
||||
|
||||
class MySQLDigestScheme:
|
||||
|
||||
def encrypt(self, pw):
|
||||
pw = u(pw)
|
||||
nr = long(1345345333)
|
||||
add = 7
|
||||
nr2 = long(0x12345671)
|
||||
for i in pw:
|
||||
if i == ' ' or i == '\t':
|
||||
continue
|
||||
nr ^= (((nr & 63) + add) * ord(i)) + (nr << 8)
|
||||
nr2 += (nr2 << 8) ^ nr
|
||||
add += ord(i)
|
||||
r0 = nr & ((long(1) << 31) - long(1))
|
||||
r1 = nr2 & ((long(1) << 31) - long(1))
|
||||
return (u"%08lx%08lx" % (r0, r1)).encode('ascii')
|
||||
|
||||
def validate(self, reference, attempt):
|
||||
a = self.encrypt(attempt)
|
||||
return constant_time_compare(a, reference)
|
||||
|
||||
registerScheme(u'MYSQL', MySQLDigestScheme())
|
||||
|
||||
|
||||
def pw_validate(reference, attempt):
|
||||
"""Validate the provided password string, which uses LDAP-style encoding
|
||||
notation. Reference is the correct password, attempt is clear text
|
||||
password attempt."""
|
||||
reference = b(reference)
|
||||
for id, prefix, scheme in _schemes:
|
||||
lp = len(prefix)
|
||||
if reference[:lp] == b(prefix):
|
||||
return scheme.validate(reference[lp:], attempt)
|
||||
# Assume cleartext.
|
||||
return constant_time_compare(reference, b(attempt))
|
||||
|
||||
|
||||
def is_encrypted(pw):
|
||||
for id, prefix, scheme in _schemes:
|
||||
lp = len(prefix)
|
||||
if pw[:lp] == b(prefix):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def pw_encrypt(pw, encoding=u'SSHA'):
|
||||
"""Encrypt the provided plain text password using the encoding if provided
|
||||
and return it in an LDAP-style representation."""
|
||||
encoding = u(encoding)
|
||||
for id, prefix, scheme in _schemes:
|
||||
if encoding == id:
|
||||
return b(prefix) + scheme.encrypt(pw)
|
||||
raise ValueError('Not supported: %s' % encoding)
|
||||
|
||||
pw_encode = pw_encrypt # backward compatibility
|
||||
@@ -0,0 +1,17 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002,2015 Zope Foundation and Contributors.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
from .AuthEncoding import (is_encrypted, pw_encrypt, pw_validate,
|
||||
registerScheme, listSchemes,
|
||||
constant_time_compare)
|
||||
@@ -0,0 +1,20 @@
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
long = int
|
||||
else:
|
||||
long = long
|
||||
|
||||
|
||||
def b(arg):
|
||||
"""Convert `arg` to bytes."""
|
||||
if isinstance(arg, six.text_type):
|
||||
arg = arg.encode("latin-1")
|
||||
return arg
|
||||
|
||||
|
||||
def u(arg):
|
||||
"""Convert `arg` to text."""
|
||||
if isinstance(arg, six.binary_type):
|
||||
arg = arg.decode('ascii', 'replace')
|
||||
return arg
|
||||
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002, 2015 Zope Foundation and Contributors.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
"""Test of AuthEncoding
|
||||
"""
|
||||
|
||||
from AuthEncoding import AuthEncoding
|
||||
from ..compat import b, u
|
||||
import pytest
|
||||
|
||||
|
||||
def testListSchemes():
|
||||
assert len(AuthEncoding.listSchemes()) > 0 # At least one must exist!
|
||||
|
||||
|
||||
@pytest.mark.parametrize('schema_id', AuthEncoding.listSchemes())
|
||||
@pytest.mark.parametrize('password', [u'good_pw', u'gööd_pw', b(u'gööd_pw')])
|
||||
def testGoodPassword(schema_id, password):
|
||||
enc = AuthEncoding.pw_encrypt(password, schema_id)
|
||||
assert enc != password
|
||||
assert AuthEncoding.pw_validate(enc, password)
|
||||
assert AuthEncoding.pw_validate(u(enc), password)
|
||||
assert AuthEncoding.is_encrypted(enc)
|
||||
assert not AuthEncoding.is_encrypted(password)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('schema_id', AuthEncoding.listSchemes())
|
||||
@pytest.mark.parametrize(
|
||||
'password', [u'OK_pa55w0rd \n', u'OK_pä55w0rd \n', b(u'OK_pä55w0rd \n')])
|
||||
def testBadPassword(schema_id, password):
|
||||
enc = AuthEncoding.pw_encrypt(password, schema_id)
|
||||
assert enc != password
|
||||
assert not AuthEncoding.pw_validate(enc, u'xxx')
|
||||
assert not AuthEncoding.pw_validate(enc, b'xxx')
|
||||
assert not AuthEncoding.pw_validate(u(enc), u'xxx')
|
||||
assert not AuthEncoding.pw_validate(enc, enc)
|
||||
if schema_id != u'CRYPT':
|
||||
# crypt truncates passwords and would fail this test.
|
||||
assert not AuthEncoding.pw_validate(enc, password[:-1])
|
||||
assert not AuthEncoding.pw_validate(enc, password[1:])
|
||||
assert AuthEncoding.pw_validate(enc, password)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('schema_id', AuthEncoding.listSchemes())
|
||||
def testShortPassword(schema_id):
|
||||
pw = u'1'
|
||||
enc = AuthEncoding.pw_encrypt(pw, schema_id)
|
||||
assert AuthEncoding.pw_validate(enc, pw)
|
||||
assert not AuthEncoding.pw_validate(enc, enc)
|
||||
assert not AuthEncoding.pw_validate(enc, u'xxx')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('schema_id', AuthEncoding.listSchemes())
|
||||
def testLongPassword(schema_id):
|
||||
pw = u'Pw' * 2000
|
||||
enc = AuthEncoding.pw_encrypt(pw, schema_id)
|
||||
assert AuthEncoding.pw_validate(enc, pw)
|
||||
assert not AuthEncoding.pw_validate(enc, enc)
|
||||
assert not AuthEncoding.pw_validate(enc, u'xxx')
|
||||
if u'CRYPT' not in schema_id:
|
||||
# crypt and bcrypt truncates passwords and would fail these tests.
|
||||
assert not AuthEncoding.pw_validate(enc, pw[:-2])
|
||||
assert not AuthEncoding.pw_validate(enc, pw[2:])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('schema_id', AuthEncoding.listSchemes())
|
||||
def testBlankPassword(schema_id):
|
||||
pw = u''
|
||||
enc = AuthEncoding.pw_encrypt(pw, schema_id)
|
||||
assert enc != pw
|
||||
assert AuthEncoding.pw_validate(enc, pw)
|
||||
assert not AuthEncoding.pw_validate(enc, enc)
|
||||
assert not AuthEncoding.pw_validate(enc, u'xxx')
|
||||
|
||||
|
||||
def testUnencryptedPassword():
|
||||
# Sanity check
|
||||
pw = u'my-password'
|
||||
assert AuthEncoding.pw_validate(pw, pw)
|
||||
assert not AuthEncoding.pw_validate(pw, pw + u'asdf')
|
||||
|
||||
|
||||
def testEncryptWithNotSupportedScheme():
|
||||
with pytest.raises(ValueError) as err:
|
||||
AuthEncoding.pw_encrypt(u'asdf', 'MD1')
|
||||
assert 'Not supported: MD1' == str(err.value)
|
||||
|
||||
|
||||
def testEncryptAcceptsTextAndBinaryEncodingNames():
|
||||
assert (AuthEncoding.pw_encrypt(u'asdf', b'SHA') ==
|
||||
AuthEncoding.pw_encrypt(u'asdf', u'SHA'))
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,348 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: BTrees
|
||||
Version: 4.5.1
|
||||
Summary: Scalable persistent object containers
|
||||
Home-page: https://github.com/zopefoundation/BTrees
|
||||
Author: Zope Foundation
|
||||
Author-email: zodb-dev@zope.org
|
||||
License: ZPL 2.1
|
||||
Platform: any
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: License :: OSI Approved :: Zope Public License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Framework :: ZODB
|
||||
Classifier: Topic :: Database
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Operating System :: Microsoft :: Windows
|
||||
Classifier: Operating System :: Unix
|
||||
Provides-Extra: test
|
||||
Provides-Extra: ZODB
|
||||
Provides-Extra: docs
|
||||
Requires-Dist: persistent (>=4.1.0)
|
||||
Requires-Dist: zope.interface
|
||||
Provides-Extra: ZODB
|
||||
Requires-Dist: ZODB; extra == 'ZODB'
|
||||
Provides-Extra: docs
|
||||
Requires-Dist: Sphinx; extra == 'docs'
|
||||
Requires-Dist: repoze.sphinx.autointerface; extra == 'docs'
|
||||
Provides-Extra: test
|
||||
Requires-Dist: transaction; extra == 'test'
|
||||
Requires-Dist: zope.testrunner; extra == 'test'
|
||||
|
||||
``BTrees``: scalable persistent components
|
||||
===========================================
|
||||
|
||||
.. image:: https://travis-ci.org/zopefoundation/BTrees.svg?branch=master
|
||||
:target: https://travis-ci.org/zopefoundation/BTrees
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/github/zopefoundation/BTrees?branch=master&svg=true
|
||||
:target: https://ci.appveyor.com/project/mgedmin/BTrees
|
||||
|
||||
.. image:: https://coveralls.io/repos/github/zopefoundation/BTrees/badge.svg?branch=master
|
||||
:target: https://coveralls.io/github/zopefoundation/BTrees?branch=master
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/BTrees.svg
|
||||
:target: https://pypi.org/project/BTrees/
|
||||
:alt: Current version on PyPI
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/BTrees.svg
|
||||
:target: https://pypi.org/project/BTrees/
|
||||
:alt: Supported Python versions
|
||||
|
||||
|
||||
This package contains a set of persistent object containers built around
|
||||
a modified BTree data structure. The trees are optimized for use inside
|
||||
ZODB's "optimistic concurrency" paradigm, and include explicit resolution
|
||||
of conflicts detected by that mechanism.
|
||||
|
||||
Please see `the Sphinx documentation <http://btrees.readthedocs.io/>`_ for further
|
||||
information.
|
||||
|
||||
|
||||
``BTrees`` Changelog
|
||||
====================
|
||||
|
||||
4.5.1 (2018-08-09)
|
||||
------------------
|
||||
|
||||
- Produce binary wheels for Python 3.7.
|
||||
|
||||
- Use pyproject.toml to specify build dependencies. This requires pip
|
||||
18 or later to build from source.
|
||||
|
||||
|
||||
4.5.0 (2018-04-23)
|
||||
------------------
|
||||
|
||||
- Add support for Python 3.6 and 3.7.
|
||||
- Drop support for Python 3.3.
|
||||
- Raise an ``ImportError`` consistently on Python 3 if the C extension for
|
||||
BTrees is used but the ``persistent`` C extension is not available.
|
||||
Previously this could result in an odd ``AttributeError``. See
|
||||
https://github.com/zopefoundation/BTrees/pull/55
|
||||
- Fix the possibility of a rare crash in the C extension when
|
||||
deallocating items. See https://github.com/zopefoundation/BTrees/issues/75
|
||||
- Respect the ``PURE_PYTHON`` environment variable at runtime even if
|
||||
the C extensions are available. See
|
||||
https://github.com/zopefoundation/BTrees/issues/78
|
||||
- Always attempt to build the C extensions, but make their success
|
||||
optional.
|
||||
- Fix a ``DeprecationWarning`` that could come from I and L objects in
|
||||
Python 2 in pure-Python mode. See https://github.com/zopefoundation/BTrees/issues/79
|
||||
|
||||
4.4.1 (2017-01-24)
|
||||
------------------
|
||||
|
||||
Fixed a packaging bug that caused extra files to be included (some of
|
||||
which caused problems in some platforms).
|
||||
|
||||
4.4.0 (2017-01-11)
|
||||
------------------
|
||||
|
||||
- Allow None as a special key (sorted smaller than all others).
|
||||
|
||||
This is a bit of a return to BTrees 3 behavior in that Nones are
|
||||
allowed as keys again. Other objects with default ordering are
|
||||
still not allowed as keys.
|
||||
|
||||
4.3.2 (2017-01-05)
|
||||
------------------
|
||||
|
||||
- Make the CPython implementation consistent with the pure-Python
|
||||
implementation and only check object keys for default comparison
|
||||
when setting keys. In Python 2 this makes it possible to remove keys
|
||||
that were added using a less restrictive version of BTrees. (In
|
||||
Python 3 keys that are unorderable still cannot be removed.)
|
||||
Likewise, all versions can unpickle trees that already had such
|
||||
keys. See: https://github.com/zopefoundation/BTrees/issues/53 and
|
||||
https://github.com/zopefoundation/BTrees/issues/51
|
||||
|
||||
- Make the Python implementation consistent with the CPython
|
||||
implementation and check object key identity before checking
|
||||
equality and performing comparisons. This can allow fixing trees
|
||||
that have keys that now have broken comparison functions. See
|
||||
https://github.com/zopefoundation/BTrees/issues/50
|
||||
|
||||
- Make the CPython implementation consistent with the pure-Python
|
||||
implementation and no longer raise ``TypeError`` for an object key
|
||||
(in object-keyed trees) with default comparison on ``__getitem__``,
|
||||
``get`` or ``in`` operations. Instead, the results will be a
|
||||
``KeyError``, the default value, and ``False``, respectively.
|
||||
Previously, CPython raised a ``TypeError`` in those cases, while the
|
||||
Python implementation behaved as specified.
|
||||
|
||||
Likewise, non-integer keys in integer-keyed trees
|
||||
will raise ``KeyError``, return the default and return ``False``,
|
||||
respectively, in both implementations. Previously, pure-Python
|
||||
raised a ``KeyError``, returned the default, and raised a
|
||||
``TypeError``, while CPython raised ``TypeError`` in all three cases.
|
||||
|
||||
4.3.1 (2016-05-16)
|
||||
------------------
|
||||
|
||||
- Packaging: fix password used to automate wheel creation on Travis.
|
||||
|
||||
4.3.0 (2016-05-10)
|
||||
------------------
|
||||
|
||||
- Fix unexpected ``OverflowError`` when passing 64bit values to long
|
||||
keys / values on Win64. See:
|
||||
https://github.com/zopefoundation/BTrees/issues/32
|
||||
|
||||
- When testing ``PURE_PYTHON`` environments under ``tox``, avoid poisoning
|
||||
the user's global wheel cache.
|
||||
|
||||
- Ensure that the pure-Python implementation, used on PyPy and when a C
|
||||
compiler isn't available for CPython, pickles identically to the C
|
||||
version. Unpickling will choose the best available implementation.
|
||||
This change prevents interoperability problems and database corruption if
|
||||
both implementations are in use. While it is no longer possible to
|
||||
pickle a Python implementation and have it unpickle to the Python
|
||||
implementation if the C implementation is available, existing Python
|
||||
pickles will still unpickle to the Python implementation (until
|
||||
pickled again). See:
|
||||
https://github.com/zopefoundation/BTrees/issues/19
|
||||
|
||||
- Avoid creating invalid objects when unpickling empty BTrees in a pure-Python
|
||||
environment.
|
||||
|
||||
- Drop support for Python 2.6 and 3.2.
|
||||
|
||||
4.2.0 (2015-11-13)
|
||||
------------------
|
||||
|
||||
- Add support for Python 3.5.
|
||||
|
||||
4.1.4 (2015-06-02)
|
||||
------------------
|
||||
|
||||
- Ensure that pure-Python Bucket and Set objects have a human readable
|
||||
``__repr__`` like the C versions.
|
||||
|
||||
4.1.3 (2015-05-19)
|
||||
------------------
|
||||
|
||||
- Fix ``_p_changed`` when removing items from small pure-Python
|
||||
BTrees/TreeSets and when adding items to small pure-Python Sets. See:
|
||||
https://github.com/zopefoundation/BTrees/issues/13
|
||||
|
||||
|
||||
4.1.2 (2015-04-07)
|
||||
------------------
|
||||
|
||||
- Suppress testing 64-bit values in OLBTrees on 32 bit machines.
|
||||
See: https://github.com/zopefoundation/BTrees/issues/9
|
||||
|
||||
- Fix ``_p_changed`` when adding items to small pure-Python
|
||||
BTrees/TreeSets. See:
|
||||
https://github.com/zopefoundation/BTrees/issues/11
|
||||
|
||||
|
||||
4.1.1 (2014-12-27)
|
||||
------------------
|
||||
|
||||
- Accomodate long values in pure-Python OLBTrees.
|
||||
|
||||
|
||||
4.1.0 (2014-12-26)
|
||||
------------------
|
||||
|
||||
- Add support for PyPy and PyPy3.
|
||||
|
||||
- Add support for Python 3.4.
|
||||
|
||||
- BTree subclasses can define ``max_leaf_size`` or ``max_internal_size``
|
||||
to control maximum sizes for Bucket/Set and BTree/TreeSet nodes.
|
||||
|
||||
- Detect integer overflow on 32-bit machines correctly under Python 3.
|
||||
|
||||
- Update pure-Python and C trees / sets to accept explicit None to indicate
|
||||
max / min value for ``minKey``, ``maxKey``. (PR #3)
|
||||
|
||||
- Update pure-Python trees / sets to accept explicit None to indicate
|
||||
open ranges for ``keys``, ``values``, ``items``. (PR #3)
|
||||
|
||||
|
||||
4.0.8 (2013-05-25)
|
||||
------------------
|
||||
|
||||
- Fix value-based comparison for objects under Py3k: addresses invalid
|
||||
merges of ``[OLI]OBTrees/OBuckets``.
|
||||
|
||||
- Ensure that pure-Python implementation of ``OOBTree.byValue`` matches
|
||||
semantics (reversed-sort) of C implementation.
|
||||
|
||||
|
||||
4.0.7 (2013-05-22)
|
||||
------------------
|
||||
|
||||
- Issue #2: compilation error on 32-bit mode of OS/X.
|
||||
|
||||
- Test ``PURE_PYTHON`` environment variable support: if set, the C
|
||||
extensions will not be built, imported, or tested.
|
||||
|
||||
|
||||
4.0.6 (2013-05-14)
|
||||
------------------
|
||||
|
||||
- Changed the ``ZODB`` extra to require only the real ``ZODB`` package,
|
||||
rather than the ``ZODB3`` metapackage: depending on the version used,
|
||||
the metapackage could pull in stale versions of **this** package and
|
||||
``persistent``.
|
||||
|
||||
- Fixed Python version check in ``setup.py``.
|
||||
|
||||
|
||||
4.0.5 (2013-01-15)
|
||||
------------------
|
||||
|
||||
- Fit the ``repr`` of bucket objects, which could contain garbage
|
||||
characters.
|
||||
|
||||
|
||||
4.0.4 (2013-01-12)
|
||||
------------------
|
||||
|
||||
- Emulate the (private) iterators used by the C extension modules from
|
||||
pure Python. This change is "cosmetic" only: it prevents the ZCML
|
||||
``zope.app.security:permission.zcml`` from failing. The emulated
|
||||
classes are **not** functional, and should be considered implementation
|
||||
details.
|
||||
|
||||
- Accomodate buildout to the fact that we no longer bundle a copy
|
||||
of 'persistent.h'.
|
||||
|
||||
- Fix test failures on Windows: no longer rely on overflows from
|
||||
``sys.maxint``.
|
||||
|
||||
|
||||
4.0.3 (2013-01-04)
|
||||
------------------
|
||||
|
||||
- Added ``setup_requires==['persistent']``.
|
||||
|
||||
|
||||
4.0.2 (2013-01-03)
|
||||
------------------
|
||||
|
||||
- Updated Trove classifiers.
|
||||
|
||||
- Added explicit support for Python 3.2, Python 3.3, and PyPy.
|
||||
Note that the C extensions are not (yet) available on PyPy.
|
||||
|
||||
- Python reference implementations now tested separately from the C
|
||||
verions on all platforms.
|
||||
|
||||
- 100% unit test coverage.
|
||||
|
||||
|
||||
4.0.1 (2012-10-21)
|
||||
------------------
|
||||
|
||||
- Provide local fallback for persistent C header inclusion if the
|
||||
persistent distribution isn't installed. This makes the winbot happy.
|
||||
|
||||
|
||||
4.0.0 (2012-10-20)
|
||||
------------------
|
||||
|
||||
Platform Changes
|
||||
################
|
||||
|
||||
- Dropped support for Python < 2.6.
|
||||
|
||||
- Factored ``BTrees`` as a separate distribution.
|
||||
|
||||
Testing Changes
|
||||
###############
|
||||
|
||||
- All covered platforms tested under ``tox``.
|
||||
|
||||
- Added support for continuous integration using ``tox`` and ``jenkins``.
|
||||
|
||||
- Added ``setup.py dev`` alias (installs ``nose`` and ``coverage``).
|
||||
|
||||
- Dropped dependency on ``zope.testing`` / ``zope.testrunner``: tests now
|
||||
run with ``setup.py test``.
|
||||
|
||||
Documentation Changes
|
||||
#####################
|
||||
|
||||
- Added API reference, generated via Spinx' autodoc.
|
||||
|
||||
- Added Sphinx documentation based on ZODB Guide (snippets are exercised
|
||||
via 'tox').
|
||||
|
||||
- Added ``setup.py docs`` alias (installs ``Sphinx`` and
|
||||
``repoze.sphinx.autointerface``).
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
BTrees-4.5.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
BTrees-4.5.1.dist-info/METADATA,sha256=Nb2ZkIBPwlBQFnLSaGd_6XWqJwn1DIzl5dOR5nri1Mk,11329
|
||||
BTrees-4.5.1.dist-info/RECORD,,
|
||||
BTrees-4.5.1.dist-info/WHEEL,sha256=lx06sLWNl7U6OABNP9EWDqGNT2LbGyG1l5xyFnYYudo,109
|
||||
BTrees-4.5.1.dist-info/entry_points.txt,sha256=OZFBvh0wrCZW2J7tzw2NztqnLWwpv5WcriQ9x7FELPY,6
|
||||
BTrees-4.5.1.dist-info/top_level.txt,sha256=tyKTCytaCG_dQ7OVIUdgV-PZM9ltjveiOYk5u5FesbU,15
|
||||
BTrees/BTreeItemsTemplate.c,sha256=u5_bDWVQljKp87eX_FIQbm1HoVK9QztsB4UkcVZhVKU,24825
|
||||
BTrees/BTreeModuleTemplate.c,sha256=kO71qy4qU0nzS4PzTDzkvdn6cf-8DpzRFSt16WFp1O0,20611
|
||||
BTrees/BTreeTemplate.c,sha256=qJJnOLO9GJZNNzbBLRO1AxloA62cGDtC-2QFFnupbUY,71551
|
||||
BTrees/BucketTemplate.c,sha256=5sSFK-XD19B13bpIVN-rFS8TTcf6OCAYNecFbVdybqw,53068
|
||||
BTrees/Development.txt,sha256=IJCMs8N362vMCr4H67mznKIr5Lb5BuYh5YRBzHDq8xk,16958
|
||||
BTrees/IFBTree.py,sha256=xTd4UoaUmBhvf0wtco-_AZI2G_2bqymFWKRPDKIHX5g,3452
|
||||
BTrees/IIBTree.py,sha256=43-TgRVrwHKzhwZ7myrbRbvUfRIL-EpXWmhKmYfXuZs,3426
|
||||
BTrees/IOBTree.py,sha256=1PKMEHbm6Q0tOT-nEE66p6oiWUYtRXGs4ca6kVTtGck,2776
|
||||
BTrees/Interfaces.py,sha256=-KxR8M0K5NVfWcvKbo5HkcjHZmPQQ0OXvLxXoaysX08,17831
|
||||
BTrees/LFBTree.py,sha256=4qVsFCP6dm-ocZUBMTM97yNkI4MjB14xoBVJPnIpNLk,3453
|
||||
BTrees/LLBTree.py,sha256=EyzLlsd-vdy12c6tVr57dshtR7yBRSq4ovXLbWnMoXw,3446
|
||||
BTrees/LOBTree.py,sha256=yLR9OWzuytSDeb16vQjoJTCTpmZrAq1d5GMxXhRs9tk,2777
|
||||
BTrees/Length.py,sha256=JX5_JLeRIwh79j_r87GnjFosExLwrj8H2eBjdvfb2IY,1937
|
||||
BTrees/MergeTemplate.c,sha256=oLbW1QqeERrfnkg9YXfDUXU4I4OAJZ7jQ1OJcWQ7mqM,12070
|
||||
BTrees/OIBTree.py,sha256=KvFHcH_80Cxy_Qclv8SlGrUVWapudsSGEKsEhOfcvcE,3338
|
||||
BTrees/OLBTree.py,sha256=SXe4IJ0NOAfd3YJbV5L68VJkfdC3ZqzFOv58SRBXiKQ,3330
|
||||
BTrees/OOBTree.py,sha256=n2dZCvzq83FJZ45lTywvYBvT2ozhJZZDraS6jGDZUaA,2524
|
||||
BTrees/SetOpTemplate.c,sha256=T5hDNXFqSW6i1rKtCVErg6W-H8HOYkc5hCh1j6-8uW8,15571
|
||||
BTrees/SetTemplate.c,sha256=GIW_9986HcjjwCXG2XnDz_BNHgsygf1umnv3FD92Gr8,10119
|
||||
BTrees/TreeSetTemplate.c,sha256=z7sOs0RDVCgoufuDIiMb1oBKEKyJpgGtaUY063cF7ZE,8455
|
||||
BTrees/_IFBTree.c,sha256=BIJrf9LcDM_DRKCtnXJqoAnwvDwhnh_3x_l7CQWzZ3g,1136
|
||||
BTrees/_IFBTree.cpython-36m-darwin.so,sha256=-pDtbOXMyRLnyEzGPESPtNezHMVYjIkcI4_8LjbfQ90,186992
|
||||
BTrees/_IIBTree.c,sha256=DXzS4WGNkVYneiEGtX9F0olC750rxFQfcAVzUlaBCT0,1130
|
||||
BTrees/_IIBTree.cpython-36m-darwin.so,sha256=fY6YmOcdTiBl3DeGES-_KBdMLxDgCcGq7zFNfg8_dfc,182652
|
||||
BTrees/_IOBTree.c,sha256=nL6yr4-u6rPo_y8A8TD1irmPVG0t4szMVh4NKumypgI,1141
|
||||
BTrees/_IOBTree.cpython-36m-darwin.so,sha256=vNJgmpO6m0rYNxQfpSe5Qse1d5BNGEIovI72szY6Ogk,182500
|
||||
BTrees/_LFBTree.c,sha256=vZ8O4_6sYU54cSwbcyy1FXUfbypQzuE59tYN6Po10GE,1208
|
||||
BTrees/_LFBTree.cpython-36m-darwin.so,sha256=KuOsXfRQGAFZZjIKv9RCs_EEiiUXmrhZoVOSLZQQMms,187080
|
||||
BTrees/_LLBTree.c,sha256=P31gcA6clVlSgAlBLzJ4hqdZMPqwV6q9pKfEexXRH9Q,1199
|
||||
BTrees/_LLBTree.cpython-36m-darwin.so,sha256=WOhNkol3_nyCdjN7eUsLfz7hYw1ybbSi4Xz0AtvjmSc,186820
|
||||
BTrees/_LOBTree.c,sha256=-bq0twqbE0oqlxyMS6chYBR5ObvLvFQAa0IyFK-l4ro,1178
|
||||
BTrees/_LOBTree.cpython-36m-darwin.so,sha256=wLbc18-MMpOeFIrdQVm932uKHq-nb_2h3c25OfOieO4,182588
|
||||
BTrees/_OIBTree.c,sha256=6SLJqZskZHEQB4OqCIlZZwGRwADi7MOvu-8W3hRJBo4,1141
|
||||
BTrees/_OIBTree.cpython-36m-darwin.so,sha256=E4ToIjjUlcb1BXQ2XexI17hzxGpLkAhzFdTjZoOi4hA,178356
|
||||
BTrees/_OLBTree.c,sha256=Hu0ylTuFWEPivGsL0ClS1YLdvx3fTIS9phtMpSt9zXo,1178
|
||||
BTrees/_OLBTree.cpython-36m-darwin.so,sha256=qoKSvSbbNErXIMNVKYxyapIqDRNiU9M7VSXNxDE19m4,178428
|
||||
BTrees/_OOBTree.c,sha256=e-Wd39TQyfwnX9X-o1mp9S_hIUpWGmUjmGvdH4f3__Q,1150
|
||||
BTrees/_OOBTree.cpython-36m-darwin.so,sha256=7jOL1UtqyRz7YKvmBGma5asRslm4GlRKpf9sLxteCEk,174148
|
||||
BTrees/__init__.py,sha256=SuUBN3IfVFkSbKLece09uEDFg76AwWWD2qx37AaYXxg,1871
|
||||
BTrees/__pycache__/IFBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/IIBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/IOBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/Interfaces.cpython-36.pyc,,
|
||||
BTrees/__pycache__/LFBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/LLBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/LOBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/Length.cpython-36.pyc,,
|
||||
BTrees/__pycache__/OIBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/OLBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/OOBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/__init__.cpython-36.pyc,,
|
||||
BTrees/__pycache__/_base.cpython-36.pyc,,
|
||||
BTrees/__pycache__/_compat.cpython-36.pyc,,
|
||||
BTrees/__pycache__/check.cpython-36.pyc,,
|
||||
BTrees/__pycache__/fsBTree.cpython-36.pyc,,
|
||||
BTrees/__pycache__/utils.cpython-36.pyc,,
|
||||
BTrees/_base.py,sha256=h6CFY1XeQHNJ9lkH6UWwMSTkTWOoOXNLUdUqvNbo1O4,49411
|
||||
BTrees/_compat.h,sha256=7Juyq0L7R692-QxH7QDk3HD7AA3N0eAnwfP4zoqoGJg,1392
|
||||
BTrees/_compat.py,sha256=30krwQo7chtAhl6V-LY0G4723rA43bZSHiERonPCHQc,2902
|
||||
BTrees/_fsBTree.c,sha256=R2xeZeyvlUvJObF-66R2HJOFoPHLO87p3rVpe9byssY,4805
|
||||
BTrees/_fsBTree.cpython-36m-darwin.so,sha256=3YzUd8gVpOREfkVJzM2NaY_cIYgZgmCULvLXSv01Rg8,174228
|
||||
BTrees/check.py,sha256=73Wkf_42KfDQ_LjUlTC9hYC_v3e2gUXOJFkGbqwAAXM,15481
|
||||
BTrees/floatvaluemacros.h,sha256=8--_OkpCuflybs5jaP3B54BWxw38u8vESph4IM545AI,899
|
||||
BTrees/fsBTree.py,sha256=r1_iTqRLCBqKGPPsvj1D0mYoRzN85amNnrJJbjiHWj0,3185
|
||||
BTrees/intkeymacros.h,sha256=M0lCFColrnPHK6qRbbgwqwAITplrCVfIDCcHp7bSE-I,1547
|
||||
BTrees/intvaluemacros.h,sha256=oL_1993h7sS6O716DaKHytbC094xcBO4L1llPqX20Mo,1704
|
||||
BTrees/objectkeymacros.h,sha256=BGhqG4nV122anWw1CNJE3dj2c5LhqKwDcvvx-Y_8QE0,1285
|
||||
BTrees/objectvaluemacros.h,sha256=duuNoUjv-IwKbzEVxZjvvmDCETmzzASeQp1N2188BuM,460
|
||||
BTrees/sorters.c,sha256=_RFLxqdW3ZhfJnMfo7tS0cQ1OuftmTmdwMLoJmH29p4,15218
|
||||
BTrees/tests/__init__.py,sha256=as-9vgC96TSaF9bHM7EBWBANqSJOgJjwWtUT3lvHQZg,52
|
||||
BTrees/tests/__pycache__/__init__.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/common.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/testBTrees.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/testBTreesUnicode.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/testConflict.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_IFBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_IIBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_IOBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_LFBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_LLBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_LOBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_Length.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_OIBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_OLBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_OOBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test__base.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_btreesubclass.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_check.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_fsBTree.cpython-36.pyc,,
|
||||
BTrees/tests/__pycache__/test_utils.cpython-36.pyc,,
|
||||
BTrees/tests/common.py,sha256=ypzQE17QBOIpZNQmiRBtLMUA3nN68ZpMsjhjcnJmrhU,86610
|
||||
BTrees/tests/testBTrees.py,sha256=9kHPN0-V9ZTYE05-KPocMChxWsrG3YtdGDxy_wfmZAg,17407
|
||||
BTrees/tests/testBTreesUnicode.py,sha256=mPQraEu9VPV4mDlMcJRhpKJC0hvK2-IwJ-aZASP-DsM,2554
|
||||
BTrees/tests/testConflict.py,sha256=O6bjf7ApdPaYzYCpmPcMC4yoEeS07dfvO280sKuNBZU,21323
|
||||
BTrees/tests/test_IFBTree.py,sha256=lDac4rWXBtzaIYcbWWPcEshJKvLlwcJRzPzpltUC4rU,10641
|
||||
BTrees/tests/test_IIBTree.py,sha256=ois9MwkJ33ZoryShqIRt6FE51rGPwBRT-fw8ceoZtmo,14197
|
||||
BTrees/tests/test_IOBTree.py,sha256=LwwX727SNAWCf3tP18ZdY1rxNIYUseCKP2O1I860QE0,11531
|
||||
BTrees/tests/test_LFBTree.py,sha256=RE2Fwo1jPPhSE4WI5R8Q0Q65wyRRydEJ5wMKcBcLK2w,8957
|
||||
BTrees/tests/test_LLBTree.py,sha256=ECYj7Fn_em3RMwpd7PYb7Na5IR0FM5_lkrFbP1IkjP8,11760
|
||||
BTrees/tests/test_LOBTree.py,sha256=RnAkGsl6prphDGpd-twb562MDlhYnn8Ka_EagQb1sEI,10124
|
||||
BTrees/tests/test_Length.py,sha256=gyEXtVvSw7O_AMA9Yd2usemR_Qgie3sdr4lsCjkM0wY,3502
|
||||
BTrees/tests/test_OIBTree.py,sha256=eeB1T_nlMb5DQMbD1ASmEwj-TEC7Pb8fSXwpVh-nyEU,10947
|
||||
BTrees/tests/test_OLBTree.py,sha256=Bm6QqM9NdoqhrRqRykjurAqO9Sg8YzUQ4BrZWA8cc8A,10293
|
||||
BTrees/tests/test_OOBTree.py,sha256=bfnhr0zQzvQVMW9FtL1QWT6J38Lt-b1nVYC4r7hZrS8,12573
|
||||
BTrees/tests/test__base.py,sha256=7teTiKJfi-jc-SG1RL4KVhAYmy_H6oC-bCjY3J-2I2Q,114377
|
||||
BTrees/tests/test_btreesubclass.py,sha256=bT0TwSCViQGoG8hRSzNLC6VAw3wlzaP1n80OZGjwREA,1646
|
||||
BTrees/tests/test_check.py,sha256=UdeT_QGMF5ole6fya4853XcxrJSzeW3GSnz2J4RmngQ,12948
|
||||
BTrees/tests/test_fsBTree.py,sha256=88hNt0wIFTizjPaUgHkz2msFO6-eFPOyHSEQKZKci-0,2136
|
||||
BTrees/tests/test_utils.py,sha256=8B0lqFwYoHZalmEUyogHfDlN6IoERBaPjlr0TD-xu50,2703
|
||||
BTrees/utils.py,sha256=Vnpv1X63hp3aBPUeS7EPuZo0NoGCBmuogfu8pAcLgSk,1442
|
||||
terryfy/__init__.py,sha256=RBtPV4__9gzLYGnKiTMv1tGSXLxzFpIuVjARdyHHi0w,31
|
||||
terryfy/__pycache__/__init__.cpython-36.pyc,,
|
||||
terryfy/__pycache__/bdist_wheel.cpython-36.pyc,,
|
||||
terryfy/__pycache__/cp_suff_real_libs.cpython-36.pyc,,
|
||||
terryfy/__pycache__/fuse_suff_real_libs.cpython-36.pyc,,
|
||||
terryfy/__pycache__/monkeyexec.cpython-36.pyc,,
|
||||
terryfy/__pycache__/repath_lib_names.cpython-36.pyc,,
|
||||
terryfy/__pycache__/test_travisparse.cpython-36.pyc,,
|
||||
terryfy/__pycache__/travisparse.cpython-36.pyc,,
|
||||
terryfy/__pycache__/wafutils.cpython-36.pyc,,
|
||||
terryfy/bdist_wheel.py,sha256=uMS-xIbyD81dC8nyKh6NuFNA3qVWY6TVXyzzsV5DBTc,811
|
||||
terryfy/cp_suff_real_libs.py,sha256=11dwW6AePQ2sVB0C4wurs-tr4z1ytRgiHz_PsXGpFiw,720
|
||||
terryfy/fuse_suff_real_libs.py,sha256=wfsxzciiEf1FgTga7MSJ0KXsulUyWvYaul5Z4lIfM0E,1099
|
||||
terryfy/monkeyexec.py,sha256=QFZ5IB5JwsJK-Pc7IFnwrOD1pygE812zYn98kV-6V8A,1708
|
||||
terryfy/repath_lib_names.py,sha256=kv2dHTRRYHjIwNbcfbFNna_6dHC6yyd-orO2BWMfNOk,1018
|
||||
terryfy/test_travisparse.py,sha256=eGxYZSYdzwkxI6B9oYhZB-iGu4ifVla_F4i3Nkq7efo,2148
|
||||
terryfy/travisparse.py,sha256=Io2vjJJz6iBHtSqZGMUm0YZ8RUO3d4y06bgYMC0qWT4,1763
|
||||
terryfy/wafutils.py,sha256=FIxHuY991jWwdrGB_PVjSHLf3aeZpqKWoK_9rJbH8Vk,9118
|
||||
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.31.1)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp36-cp36m-macosx_10_6_intel
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
BTrees
|
||||
terryfy
|
||||
@@ -0,0 +1,790 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#define BTREEITEMSTEMPLATE_C "$Id$\n"
|
||||
|
||||
/* A BTreeItems struct is returned from calling .items(), .keys() or
|
||||
* .values() on a BTree-based data structure, and is also the result of
|
||||
* taking slices of those. It represents a contiguous slice of a BTree.
|
||||
*
|
||||
* The start of the slice is in firstbucket, at offset first. The end of
|
||||
* the slice is in lastbucket, at offset last. Both endpoints are inclusive.
|
||||
* It must possible to get from firstbucket to lastbucket via following
|
||||
* bucket 'next' pointers zero or more times. firstbucket, first, lastbucket,
|
||||
* and last are readonly after initialization. An empty slice is represented
|
||||
* by firstbucket == lastbucket == currentbucket == NULL.
|
||||
*
|
||||
* 'kind' determines whether this slice represents 'k'eys alone, 'v'alues
|
||||
* alone, or 'i'items (key+value pairs). 'kind' is also readonly after
|
||||
* initialization.
|
||||
*
|
||||
* The combination of currentbucket, currentoffset and pseudoindex acts as
|
||||
* a search finger. Offset currentoffset in bucket currentbucket is at index
|
||||
* pseudoindex, where pseudoindex==0 corresponds to offset first in bucket
|
||||
* firstbucket, and pseudoindex==-1 corresponds to offset last in bucket
|
||||
* lastbucket. The function BTreeItems_seek() can be used to set this combo
|
||||
* correctly for any in-bounds index, and uses this combo on input to avoid
|
||||
* needing to search from the start (or end) on each call. Calling
|
||||
* BTreeItems_seek() with consecutive larger positions is very efficent.
|
||||
* Calling it with consecutive smaller positions is more efficient than if
|
||||
* a search finger weren't being used at all, but is still quadratic time
|
||||
* in the number of buckets in the slice.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
Bucket *firstbucket; /* First bucket */
|
||||
Bucket *currentbucket; /* Current bucket (search finger) */
|
||||
Bucket *lastbucket; /* Last bucket */
|
||||
int currentoffset; /* Offset in currentbucket */
|
||||
int pseudoindex; /* search finger index */
|
||||
int first; /* Start offset in firstbucket */
|
||||
int last; /* End offset in lastbucket */
|
||||
char kind; /* 'k', 'v', 'i' */
|
||||
} BTreeItems;
|
||||
|
||||
#define ITEMS(O)((BTreeItems*)(O))
|
||||
|
||||
static PyObject *
|
||||
newBTreeItems(char kind,
|
||||
Bucket *lowbucket, int lowoffset,
|
||||
Bucket *highbucket, int highoffset);
|
||||
|
||||
static void
|
||||
BTreeItems_dealloc(BTreeItems *self)
|
||||
{
|
||||
Py_XDECREF(self->firstbucket);
|
||||
Py_XDECREF(self->lastbucket);
|
||||
Py_XDECREF(self->currentbucket);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero)
|
||||
{
|
||||
Py_ssize_t r;
|
||||
Bucket *b, *next;
|
||||
|
||||
b = self->firstbucket;
|
||||
if (b == NULL)
|
||||
return 0;
|
||||
|
||||
r = self->last + 1 - self->first;
|
||||
|
||||
if (nonzero && r > 0)
|
||||
/* Short-circuit if all we care about is nonempty */
|
||||
return 1;
|
||||
|
||||
if (b == self->lastbucket)
|
||||
return r;
|
||||
|
||||
Py_INCREF(b);
|
||||
PER_USE_OR_RETURN(b, -1);
|
||||
while ((next = b->next))
|
||||
{
|
||||
r += b->len;
|
||||
if (nonzero && r > 0)
|
||||
/* Short-circuit if all we care about is nonempty */
|
||||
break;
|
||||
|
||||
if (next == self->lastbucket)
|
||||
break; /* we already counted the last bucket */
|
||||
|
||||
Py_INCREF(next);
|
||||
PER_UNUSE(b);
|
||||
Py_DECREF(b);
|
||||
b = next;
|
||||
PER_USE_OR_RETURN(b, -1);
|
||||
}
|
||||
PER_UNUSE(b);
|
||||
Py_DECREF(b);
|
||||
|
||||
return r >= 0 ? r : 0;
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
BTreeItems_length(BTreeItems *self)
|
||||
{
|
||||
return BTreeItems_length_or_nonzero(self, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_seek
|
||||
**
|
||||
** Find the ith position in the BTreeItems.
|
||||
**
|
||||
** Arguments: self The BTree
|
||||
** i the index to seek to, in 0 .. len(self)-1, or in
|
||||
** -len(self) .. -1, as for indexing a Python sequence.
|
||||
**
|
||||
**
|
||||
** Returns 0 if successful, -1 on failure to seek (like out-of-bounds).
|
||||
** Upon successful return, index i is at offset self->currentoffset in bucket
|
||||
** self->currentbucket.
|
||||
*/
|
||||
static int
|
||||
BTreeItems_seek(BTreeItems *self, Py_ssize_t i)
|
||||
{
|
||||
int delta, pseudoindex, currentoffset;
|
||||
Bucket *b, *currentbucket;
|
||||
int error;
|
||||
|
||||
pseudoindex = self->pseudoindex;
|
||||
currentoffset = self->currentoffset;
|
||||
currentbucket = self->currentbucket;
|
||||
if (currentbucket == NULL)
|
||||
goto no_match;
|
||||
|
||||
delta = i - pseudoindex;
|
||||
while (delta > 0) /* move right */
|
||||
{
|
||||
int max;
|
||||
/* Want to move right delta positions; the most we can move right in
|
||||
* this bucket is currentbucket->len - currentoffset - 1 positions.
|
||||
*/
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
max = currentbucket->len - currentoffset - 1;
|
||||
b = currentbucket->next;
|
||||
PER_UNUSE(currentbucket);
|
||||
if (delta <= max)
|
||||
{
|
||||
currentoffset += delta;
|
||||
pseudoindex += delta;
|
||||
if (currentbucket == self->lastbucket
|
||||
&& currentoffset > self->last)
|
||||
goto no_match;
|
||||
break;
|
||||
}
|
||||
/* Move to start of next bucket. */
|
||||
if (currentbucket == self->lastbucket || b == NULL)
|
||||
goto no_match;
|
||||
currentbucket = b;
|
||||
pseudoindex += max + 1;
|
||||
delta -= max + 1;
|
||||
currentoffset = 0;
|
||||
}
|
||||
while (delta < 0) /* move left */
|
||||
{
|
||||
int status;
|
||||
/* Want to move left -delta positions; the most we can move left in
|
||||
* this bucket is currentoffset positions.
|
||||
*/
|
||||
if ((-delta) <= currentoffset)
|
||||
{
|
||||
currentoffset += delta;
|
||||
pseudoindex += delta;
|
||||
if (currentbucket == self->firstbucket
|
||||
&& currentoffset < self->first)
|
||||
goto no_match;
|
||||
break;
|
||||
}
|
||||
/* Move to end of previous bucket. */
|
||||
if (currentbucket == self->firstbucket)
|
||||
goto no_match;
|
||||
status = PreviousBucket(¤tbucket, self->firstbucket);
|
||||
if (status == 0)
|
||||
goto no_match;
|
||||
else if (status < 0)
|
||||
return -1;
|
||||
pseudoindex -= currentoffset + 1;
|
||||
delta += currentoffset + 1;
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
currentoffset = currentbucket->len - 1;
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
|
||||
assert(pseudoindex == i);
|
||||
|
||||
/* Alas, the user may have mutated the bucket since the last time we
|
||||
* were called, and if they deleted stuff, we may be pointing into
|
||||
* trash memory now.
|
||||
*/
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
error = currentoffset < 0 || currentoffset >= currentbucket->len;
|
||||
PER_UNUSE(currentbucket);
|
||||
if (error)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"the bucket being iterated changed size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_INCREF(currentbucket);
|
||||
Py_DECREF(self->currentbucket);
|
||||
self->currentbucket = currentbucket;
|
||||
self->currentoffset = currentoffset;
|
||||
self->pseudoindex = pseudoindex;
|
||||
return 0;
|
||||
|
||||
no_match:
|
||||
IndexError(i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Return the right kind ('k','v','i') of entry from bucket b at offset i.
|
||||
* b must be activated. Returns NULL on error.
|
||||
*/
|
||||
static PyObject *
|
||||
getBucketEntry(Bucket *b, int i, char kind)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
|
||||
assert(b);
|
||||
assert(0 <= i && i < b->len);
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case 'k':
|
||||
COPY_KEY_TO_OBJECT(result, b->keys[i]);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
COPY_VALUE_TO_OBJECT(result, b->values[i]);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
{
|
||||
PyObject *key;
|
||||
PyObject *value;;
|
||||
|
||||
COPY_KEY_TO_OBJECT(key, b->keys[i]);
|
||||
if (!key)
|
||||
break;
|
||||
|
||||
COPY_VALUE_TO_OBJECT(value, b->values[i]);
|
||||
if (!value)
|
||||
{
|
||||
Py_DECREF(key);
|
||||
break;
|
||||
}
|
||||
|
||||
result = PyTuple_New(2);
|
||||
if (result)
|
||||
{
|
||||
PyTuple_SET_ITEM(result, 0, key);
|
||||
PyTuple_SET_ITEM(result, 1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
PyErr_SetString(PyExc_AssertionError,
|
||||
"getBucketEntry: unknown kind");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_item
|
||||
**
|
||||
** Arguments: self a BTreeItems structure
|
||||
** i Which item to inspect
|
||||
**
|
||||
** Returns: the BTreeItems_item_BTree of self->kind, i
|
||||
** (ie pulls the ith item out)
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeItems_item(BTreeItems *self, Py_ssize_t i)
|
||||
{
|
||||
PyObject *result;
|
||||
|
||||
if (BTreeItems_seek(self, i) < 0)
|
||||
return NULL;
|
||||
|
||||
PER_USE_OR_RETURN(self->currentbucket, NULL);
|
||||
result = getBucketEntry(self->currentbucket, self->currentoffset,
|
||||
self->kind);
|
||||
PER_UNUSE(self->currentbucket);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_slice
|
||||
**
|
||||
** Creates a new BTreeItems structure representing the slice
|
||||
** between the low and high range
|
||||
**
|
||||
** Arguments: self The old BTreeItems structure
|
||||
** ilow The start index
|
||||
** ihigh The end index
|
||||
**
|
||||
** Returns: BTreeItems item
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeItems_slice(BTreeItems *self, Py_ssize_t ilow, Py_ssize_t ihigh)
|
||||
{
|
||||
Bucket *lowbucket;
|
||||
Bucket *highbucket;
|
||||
int lowoffset;
|
||||
int highoffset;
|
||||
Py_ssize_t length = -1; /* len(self), but computed only if needed */
|
||||
|
||||
/* Complications:
|
||||
* A Python slice never raises IndexError, but BTreeItems_seek does.
|
||||
* Python did only part of index normalization before calling this:
|
||||
* ilow may be < 0 now, and ihigh may be arbitrarily large. It's
|
||||
* our responsibility to clip them.
|
||||
* A Python slice is exclusive of the high index, but a BTreeItems
|
||||
* struct is inclusive on both ends.
|
||||
*/
|
||||
|
||||
/* First adjust ilow and ihigh to be legit endpoints in the Python
|
||||
* sense (ilow inclusive, ihigh exclusive). This block duplicates the
|
||||
* logic from Python's list_slice function (slicing for builtin lists).
|
||||
*/
|
||||
if (ilow < 0)
|
||||
ilow = 0;
|
||||
else
|
||||
{
|
||||
if (length < 0)
|
||||
length = BTreeItems_length(self);
|
||||
if (ilow > length)
|
||||
ilow = length;
|
||||
}
|
||||
|
||||
if (ihigh < ilow)
|
||||
ihigh = ilow;
|
||||
else
|
||||
{
|
||||
if (length < 0)
|
||||
length = BTreeItems_length(self);
|
||||
if (ihigh > length)
|
||||
ihigh = length;
|
||||
}
|
||||
assert(0 <= ilow && ilow <= ihigh);
|
||||
assert(length < 0 || ihigh <= length);
|
||||
|
||||
/* Now adjust for that our struct is inclusive on both ends. This is
|
||||
* easy *except* when the slice is empty: there's no good way to spell
|
||||
* that in an inclusive-on-both-ends scheme. For example, if the
|
||||
* slice is btree.items([:0]), ilow == ihigh == 0 at this point, and if
|
||||
* we were to subtract 1 from ihigh that would get interpreted by
|
||||
* BTreeItems_seek as meaning the *entire* set of items. Setting ilow==1
|
||||
* and ihigh==0 doesn't work either, as BTreeItems_seek raises IndexError
|
||||
* if we attempt to seek to ilow==1 when the underlying sequence is empty.
|
||||
* It seems simplest to deal with empty slices as a special case here.
|
||||
*/
|
||||
if (ilow == ihigh) /* empty slice */
|
||||
{
|
||||
lowbucket = highbucket = NULL;
|
||||
lowoffset = 1;
|
||||
highoffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ilow < ihigh);
|
||||
--ihigh; /* exclusive -> inclusive */
|
||||
|
||||
if (BTreeItems_seek(self, ilow) < 0)
|
||||
return NULL;
|
||||
lowbucket = self->currentbucket;
|
||||
lowoffset = self->currentoffset;
|
||||
|
||||
if (BTreeItems_seek(self, ihigh) < 0)
|
||||
return NULL;
|
||||
|
||||
highbucket = self->currentbucket;
|
||||
highoffset = self->currentoffset;
|
||||
}
|
||||
return newBTreeItems(self->kind,
|
||||
lowbucket, lowoffset, highbucket, highoffset);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
BTreeItems_subscript(BTreeItems *self, PyObject* subscript)
|
||||
{
|
||||
Py_ssize_t len = BTreeItems_length_or_nonzero(self, 0);
|
||||
|
||||
if (PyIndex_Check(subscript))
|
||||
{
|
||||
Py_ssize_t i = PyNumber_AsSsize_t(subscript, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
if (i < 0)
|
||||
i += len;
|
||||
return BTreeItems_item(self, i);
|
||||
}
|
||||
if (PySlice_Check(subscript))
|
||||
{
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
#ifdef PY3K
|
||||
#define SLICEOBJ(x) (x)
|
||||
#else
|
||||
#define SLICEOBJ(x) (PySliceObject*)(x)
|
||||
#endif
|
||||
|
||||
if (PySlice_GetIndicesEx(SLICEOBJ(subscript), len,
|
||||
&start, &stop, &step, &slicelength) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (step != 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"slices must have step size of 1");
|
||||
return NULL;
|
||||
}
|
||||
return BTreeItems_slice(self, start, stop);
|
||||
}
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"Unknown index type: must be int or slice");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Py3K doesn't honor sequence slicing, so implement via mapping */
|
||||
static PyMappingMethods BTreeItems_as_mapping = {
|
||||
(lenfunc)BTreeItems_length, /* mp_length */
|
||||
(binaryfunc)BTreeItems_subscript, /* mp_subscript */
|
||||
};
|
||||
|
||||
static PySequenceMethods BTreeItems_as_sequence =
|
||||
{
|
||||
(lenfunc) BTreeItems_length, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc) BTreeItems_item, /* sq_item */
|
||||
#ifndef PY3K
|
||||
/* Py3K doesn't honor this slot */
|
||||
(ssizessizeargfunc) BTreeItems_slice, /* sq_slice */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Number Method items (just for nb_nonzero!) */
|
||||
|
||||
static int
|
||||
BTreeItems_nonzero(BTreeItems *self)
|
||||
{
|
||||
return BTreeItems_length_or_nonzero(self, 1);
|
||||
}
|
||||
|
||||
static PyNumberMethods BTreeItems_as_number_for_nonzero = {
|
||||
0, /* nb_add */
|
||||
0, /* nb_subtract */
|
||||
0, /* nb_multiply */
|
||||
#ifndef PY3K
|
||||
0, /* nb_divide */
|
||||
#endif
|
||||
0, /* nb_remainder */
|
||||
0, /* nb_divmod */
|
||||
0, /* nb_power */
|
||||
0, /* nb_negative */
|
||||
0, /* nb_positive */
|
||||
0, /* nb_absolute */
|
||||
(inquiry)BTreeItems_nonzero /* nb_nonzero */
|
||||
};
|
||||
|
||||
static PyTypeObject BTreeItemsType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MOD_NAME_PREFIX "BTreeItems", /* tp_name */
|
||||
sizeof(BTreeItems), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
/* methods */
|
||||
(destructor) BTreeItems_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* obsolete tp_getattr */
|
||||
0, /* obsolete tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
&BTreeItems_as_number_for_nonzero, /* tp_as_number */
|
||||
&BTreeItems_as_sequence, /* tp_as_sequence */
|
||||
&BTreeItems_as_mapping, /* tp_as_mapping */
|
||||
(hashfunc)0, /* tp_hash */
|
||||
(ternaryfunc)0, /* tp_call */
|
||||
(reprfunc)0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
/* Space for future expansion */
|
||||
0L,0L,
|
||||
"Sequence type used to iterate over BTree items." /* Documentation string */
|
||||
};
|
||||
|
||||
/* Returns a new BTreeItems object representing the contiguous slice from
|
||||
* offset lowoffset in bucket lowbucket through offset highoffset in bucket
|
||||
* highbucket, inclusive. Pass lowbucket == NULL for an empty slice.
|
||||
* The currentbucket is set to lowbucket, currentoffset ot lowoffset, and
|
||||
* pseudoindex to 0. kind is 'k', 'v' or 'i' (see BTreeItems struct docs).
|
||||
*/
|
||||
static PyObject *
|
||||
newBTreeItems(char kind,
|
||||
Bucket *lowbucket, int lowoffset,
|
||||
Bucket *highbucket, int highoffset)
|
||||
{
|
||||
BTreeItems *self;
|
||||
|
||||
UNLESS (self = PyObject_NEW(BTreeItems, &BTreeItemsType))
|
||||
return NULL;
|
||||
self->kind=kind;
|
||||
|
||||
self->first=lowoffset;
|
||||
self->last=highoffset;
|
||||
|
||||
if (! lowbucket || ! highbucket
|
||||
|| (lowbucket == highbucket && lowoffset > highoffset))
|
||||
{
|
||||
self->firstbucket = 0;
|
||||
self->lastbucket = 0;
|
||||
self->currentbucket = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_INCREF(lowbucket);
|
||||
self->firstbucket = lowbucket;
|
||||
Py_INCREF(highbucket);
|
||||
self->lastbucket = highbucket;
|
||||
Py_INCREF(lowbucket);
|
||||
self->currentbucket = lowbucket;
|
||||
}
|
||||
|
||||
self->currentoffset = lowoffset;
|
||||
self->pseudoindex = 0;
|
||||
|
||||
return OBJECT(self);
|
||||
}
|
||||
|
||||
static int
|
||||
nextBTreeItems(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0)
|
||||
{
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
DECREF_VALUE(i->value);
|
||||
}
|
||||
|
||||
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
|
||||
{
|
||||
Bucket *currentbucket;
|
||||
|
||||
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
|
||||
UNLESS(PER_USE(currentbucket))
|
||||
{
|
||||
/* Mark iteration terminated, so that finiSetIteration doesn't
|
||||
* try to redundantly decref the key and value
|
||||
*/
|
||||
i->position = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_KEY(i->key);
|
||||
|
||||
COPY_VALUE(i->value,
|
||||
currentbucket->values[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_VALUE(i->value);
|
||||
|
||||
i->position ++;
|
||||
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nextTreeSetItems(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0)
|
||||
{
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
}
|
||||
|
||||
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
|
||||
{
|
||||
Bucket *currentbucket;
|
||||
|
||||
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
|
||||
UNLESS(PER_USE(currentbucket))
|
||||
{
|
||||
/* Mark iteration terminated, so that finiSetIteration doesn't
|
||||
* try to redundantly decref the key and value
|
||||
*/
|
||||
i->position = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_KEY(i->key);
|
||||
|
||||
i->position ++;
|
||||
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Support for the iteration protocol new in Python 2.2. */
|
||||
|
||||
static PyTypeObject BTreeIter_Type;
|
||||
|
||||
/* The type of iterator objects, returned by e.g. iter(IIBTree()). */
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
/* We use a BTreeItems object because it's convenient and flexible.
|
||||
* We abuse it two ways:
|
||||
* 1. We set currentbucket to NULL when the iteration is finished.
|
||||
* 2. We don't bother keeping pseudoindex in synch.
|
||||
*/
|
||||
BTreeItems *pitems;
|
||||
} BTreeIter;
|
||||
|
||||
/* Return a new iterator object, to traverse the keys and/or values
|
||||
* represented by pitems. pitems must not be NULL. Returns NULL if error.
|
||||
*/
|
||||
static BTreeIter *
|
||||
BTreeIter_new(BTreeItems *pitems)
|
||||
{
|
||||
BTreeIter *result;
|
||||
|
||||
assert(pitems != NULL);
|
||||
result = PyObject_New(BTreeIter, &BTreeIter_Type);
|
||||
if (result)
|
||||
{
|
||||
Py_INCREF(pitems);
|
||||
result->pitems = pitems;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The iterator's tp_dealloc slot. */
|
||||
static void
|
||||
BTreeIter_dealloc(BTreeIter *bi)
|
||||
{
|
||||
Py_DECREF(bi->pitems);
|
||||
PyObject_Del(bi);
|
||||
}
|
||||
|
||||
/* The implementation of the iterator's tp_iternext slot. Returns "the next"
|
||||
* item; returns NULL if error; returns NULL without setting an error if the
|
||||
* iteration is exhausted (that's the way to terminate the iteration protocol).
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeIter_next(BTreeIter *bi, PyObject *args)
|
||||
{
|
||||
PyObject *result = NULL; /* until proven innocent */
|
||||
BTreeItems *items = bi->pitems;
|
||||
int i = items->currentoffset;
|
||||
Bucket *bucket = items->currentbucket;
|
||||
|
||||
if (bucket == NULL) /* iteration termination is sticky */
|
||||
return NULL;
|
||||
|
||||
PER_USE_OR_RETURN(bucket, NULL);
|
||||
if (i >= bucket->len)
|
||||
{
|
||||
/* We never leave this routine normally with i >= len: somebody
|
||||
* else mutated the current bucket.
|
||||
*/
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"the bucket being iterated changed size");
|
||||
/* Arrange for that this error is sticky too. */
|
||||
items->currentoffset = INT_MAX;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Build the result object, from bucket at offset i. */
|
||||
result = getBucketEntry(bucket, i, items->kind);
|
||||
|
||||
/* Advance position for next call. */
|
||||
if (bucket == items->lastbucket && i >= items->last)
|
||||
{
|
||||
/* Next call should terminate the iteration. */
|
||||
Py_DECREF(items->currentbucket);
|
||||
items->currentbucket = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
if (i >= bucket->len)
|
||||
{
|
||||
Py_XINCREF(bucket->next);
|
||||
items->currentbucket = bucket->next;
|
||||
Py_DECREF(bucket);
|
||||
i = 0;
|
||||
}
|
||||
items->currentoffset = i;
|
||||
}
|
||||
|
||||
Done:
|
||||
PER_UNUSE(bucket);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
BTreeIter_getiter(PyObject *it)
|
||||
{
|
||||
Py_INCREF(it);
|
||||
return it;
|
||||
}
|
||||
|
||||
static PyTypeObject BTreeIter_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MODULE_NAME MOD_NAME_PREFIX "TreeIterator", /* tp_name */
|
||||
sizeof(BTreeIter), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
/* methods */
|
||||
(destructor)BTreeIter_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /*PyObject_GenericGetAttr,*/ /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)BTreeIter_getiter, /* tp_iter */
|
||||
(iternextfunc)BTreeIter_next, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
};
|
||||
@@ -0,0 +1,671 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "Python.h"
|
||||
/* include structmember.h for offsetof */
|
||||
#include "structmember.h"
|
||||
#include "bytesobject.h"
|
||||
|
||||
#ifdef PERSISTENT
|
||||
#include "persistent/cPersistence.h"
|
||||
#else
|
||||
#define PER_USE_OR_RETURN(self, NULL)
|
||||
#define PER_ALLOW_DEACTIVATION(self)
|
||||
#define PER_PREVENT_DEACTIVATION(self)
|
||||
#define PER_DEL(self)
|
||||
#define PER_USE(O) 1
|
||||
#define PER_ACCESSED(O) 1
|
||||
#endif
|
||||
|
||||
#include "_compat.h"
|
||||
|
||||
/* So sue me. This pair gets used all over the place, so much so that it
|
||||
* interferes with understanding non-persistence parts of algorithms.
|
||||
* PER_UNUSE can be used after a successul PER_USE or PER_USE_OR_RETURN.
|
||||
* It allows the object to become ghostified, and tells the persistence
|
||||
* machinery that the object's fields were used recently.
|
||||
*/
|
||||
#define PER_UNUSE(OBJ) do { \
|
||||
PER_ALLOW_DEACTIVATION(OBJ); \
|
||||
PER_ACCESSED(OBJ); \
|
||||
} while (0)
|
||||
|
||||
/* The tp_name slots of the various BTree types contain the fully
|
||||
* qualified names of the types, e.g. zodb.btrees.OOBTree.OOBTree.
|
||||
* The full name is usd to support pickling and because it is not
|
||||
* possible to modify the __module__ slot of a type dynamically. (This
|
||||
* may be a bug in Python 2.2).
|
||||
*
|
||||
* The MODULE_NAME here used to be "BTrees._". We actually want the module
|
||||
* name to point to the Python module rather than the C, so the underline
|
||||
* is now removed.
|
||||
*/
|
||||
#define MODULE_NAME "BTrees." MOD_NAME_PREFIX "BTree."
|
||||
|
||||
static PyObject *sort_str, *reverse_str, *__setstate___str;
|
||||
static PyObject *_bucket_type_str, *max_internal_size_str, *max_leaf_size_str;
|
||||
static PyObject *ConflictError = NULL;
|
||||
|
||||
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
|
||||
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
|
||||
#define UNLESS(E) if (!(E))
|
||||
#define OBJECT(O) ((PyObject*)(O))
|
||||
|
||||
#define MIN_BUCKET_ALLOC 16
|
||||
|
||||
#define SameType_Check(O1, O2) (Py_TYPE((O1))==Py_TYPE((O2)))
|
||||
|
||||
#define ASSERT(C, S, R) if (! (C)) { \
|
||||
PyErr_SetString(PyExc_AssertionError, (S)); return (R); }
|
||||
|
||||
|
||||
#ifdef NEED_LONG_LONG_SUPPORT
|
||||
/* Helper code used to support long long instead of int. */
|
||||
|
||||
#ifndef PY_LONG_LONG
|
||||
#error "PY_LONG_LONG required but not defined"
|
||||
#endif
|
||||
|
||||
#ifdef NEED_LONG_LONG_KEYS
|
||||
static int
|
||||
longlong_check(PyObject *ob)
|
||||
{
|
||||
if (INT_CHECK(ob))
|
||||
return 1;
|
||||
|
||||
if (PyLong_Check(ob)) {
|
||||
int overflow;
|
||||
(void)PyLong_AsLongLongAndOverflow(ob, &overflow);
|
||||
if (overflow)
|
||||
goto overflow;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"longlong_check: long integer out of range");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
longlong_as_object(PY_LONG_LONG val)
|
||||
{
|
||||
if ((val > LONG_MAX) || (val < LONG_MIN))
|
||||
return PyLong_FromLongLong(val);
|
||||
return INT_FROM_LONG((long)val);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
longlong_convert(PyObject *ob, PY_LONG_LONG *value)
|
||||
{
|
||||
#ifndef PY3K
|
||||
if (PyInt_Check(ob))
|
||||
{
|
||||
(*value) = (PY_LONG_LONG)PyInt_AS_LONG(ob);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!PyLong_Check(ob))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
PY_LONG_LONG val;
|
||||
int overflow;
|
||||
val = PyLong_AsLongLongAndOverflow(ob, &overflow);
|
||||
if (overflow)
|
||||
goto overflow;
|
||||
(*value) = val;
|
||||
return 1;
|
||||
}
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_ValueError, "long integer out of range");
|
||||
return 0;
|
||||
}
|
||||
#endif /* NEED_LONG_LONG_SUPPORT */
|
||||
|
||||
|
||||
/* Various kinds of BTree and Bucket structs are instances of
|
||||
* "sized containers", and have a common initial layout:
|
||||
* The stuff needed for all Python objects, or all Persistent objects.
|
||||
* int size: The maximum number of things that could be contained
|
||||
* without growing the container.
|
||||
* int len: The number of things currently contained.
|
||||
*
|
||||
* Invariant: 0 <= len <= size.
|
||||
*
|
||||
* A sized container typically goes on to declare one or more pointers
|
||||
* to contiguous arrays with 'size' elements each, the initial 'len' of
|
||||
* which are currently in use.
|
||||
*/
|
||||
#ifdef PERSISTENT
|
||||
#define sizedcontainer_HEAD \
|
||||
cPersistent_HEAD \
|
||||
int size; \
|
||||
int len;
|
||||
#else
|
||||
#define sizedcontainer_HEAD \
|
||||
PyObject_HEAD \
|
||||
int size; \
|
||||
int len;
|
||||
#endif
|
||||
|
||||
/* Nothing is actually of type Sized, but (pointers to) BTree nodes and
|
||||
* Buckets can be cast to Sized* in contexts that only need to examine
|
||||
* the members common to all sized containers.
|
||||
*/
|
||||
typedef struct Sized_s {
|
||||
sizedcontainer_HEAD
|
||||
} Sized;
|
||||
|
||||
#define SIZED(O) ((Sized*)(O))
|
||||
|
||||
/* A Bucket wraps contiguous vectors of keys and values. Keys are unique,
|
||||
* and stored in sorted order. The 'values' pointer may be NULL if the
|
||||
* Bucket is used to implement a set. Buckets serving as leafs of BTrees
|
||||
* are chained together via 'next', so that the entire BTree contents
|
||||
* can be traversed in sorted order quickly and easily.
|
||||
*/
|
||||
typedef struct Bucket_s {
|
||||
sizedcontainer_HEAD
|
||||
struct Bucket_s *next; /* the bucket with the next-larger keys */
|
||||
KEY_TYPE *keys; /* 'len' keys, in increasing order */
|
||||
VALUE_TYPE *values; /* 'len' corresponding values; NULL if a set */
|
||||
} Bucket;
|
||||
|
||||
#define BUCKET(O) ((Bucket*)(O))
|
||||
|
||||
/* A BTree is complicated. See Maintainer.txt.
|
||||
*/
|
||||
|
||||
typedef struct BTreeItem_s {
|
||||
KEY_TYPE key;
|
||||
Sized *child; /* points to another BTree, or to a Bucket of some sort */
|
||||
} BTreeItem;
|
||||
|
||||
typedef struct BTree_s {
|
||||
sizedcontainer_HEAD
|
||||
|
||||
/* firstbucket points to the bucket containing the smallest key in
|
||||
* the BTree. This is found by traversing leftmost child pointers
|
||||
* (data[0].child) until reaching a Bucket.
|
||||
*/
|
||||
Bucket *firstbucket;
|
||||
|
||||
/* The BTree points to 'len' children, via the "child" fields of the data
|
||||
* array. There are len-1 keys in the 'key' fields, stored in increasing
|
||||
* order. data[0].key is unused. For i in 0 .. len-1, all keys reachable
|
||||
* from data[i].child are >= data[i].key and < data[i+1].key, at the
|
||||
* endpoints pretending that data[0].key is minus infinity and
|
||||
* data[len].key is positive infinity.
|
||||
*/
|
||||
BTreeItem *data;
|
||||
long max_internal_size;
|
||||
long max_leaf_size;
|
||||
} BTree;
|
||||
|
||||
static PyTypeObject BTreeType;
|
||||
static PyTypeObject BucketType;
|
||||
|
||||
#define BTREE(O) ((BTree*)(O))
|
||||
|
||||
/* Use BTREE_SEARCH to find which child pointer to follow.
|
||||
* RESULT An int lvalue to hold the index i such that SELF->data[i].child
|
||||
* is the correct node to search next.
|
||||
* SELF A pointer to a BTree node.
|
||||
* KEY The key you're looking for, of type KEY_TYPE.
|
||||
* ONERROR What to do if key comparison raises an exception; for example,
|
||||
* perhaps 'return NULL'.
|
||||
*
|
||||
* See Maintainer.txt for discussion: this is optimized in subtle ways.
|
||||
* It's recommended that you call this at the start of a routine, waiting
|
||||
* to check for self->len == 0 after.
|
||||
*/
|
||||
#define BTREE_SEARCH(RESULT, SELF, KEY, ONERROR) { \
|
||||
int _lo = 0; \
|
||||
int _hi = (SELF)->len; \
|
||||
int _i, _cmp; \
|
||||
for (_i = _hi >> 1; _i > _lo; _i = (_lo + _hi) >> 1) { \
|
||||
TEST_KEY_SET_OR(_cmp, (SELF)->data[_i].key, (KEY)) \
|
||||
ONERROR; \
|
||||
if (_cmp < 0) _lo = _i; \
|
||||
else if (_cmp > 0) _hi = _i; \
|
||||
else /* equal */ break; \
|
||||
} \
|
||||
(RESULT) = _i; \
|
||||
}
|
||||
|
||||
/* SetIteration structs are used in the internal set iteration protocol.
|
||||
* When you want to iterate over a set or bucket or BTree (even an
|
||||
* individual key!),
|
||||
* 1. Declare a new iterator:
|
||||
* SetIteration si = {0,0,0};
|
||||
* Using "{0,0,0}" or "{0,0}" appear most common. Only one {0} is
|
||||
* necssary. At least one must be given so that finiSetIteration() works
|
||||
* correctly even if you don't get around to calling initSetIteration().
|
||||
* 2. Initialize it via
|
||||
* initSetIteration(&si, PyObject *s, useValues)
|
||||
* It's an error if that returns an int < 0. In case of error on the
|
||||
* init call, calling finiSetIteration(&si) is optional. But if the
|
||||
* init call succeeds, you must eventually call finiSetIteration(),
|
||||
* and whether or not subsequent calls to si.next() fail.
|
||||
* 3. Get the first element:
|
||||
* if (si.next(&si) < 0) { there was an error }
|
||||
* If the set isn't empty, this sets si.position to an int >= 0,
|
||||
* si.key to the element's key (of type KEY_TYPE), and maybe si.value to
|
||||
* the element's value (of type VALUE_TYPE). si.value is defined
|
||||
* iff si.usesValue is true.
|
||||
* 4. Process all the elements:
|
||||
* while (si.position >= 0) {
|
||||
* do something with si.key and/or si.value;
|
||||
* if (si.next(&si) < 0) { there was an error; }
|
||||
* }
|
||||
* 5. Finalize the SetIterator:
|
||||
* finiSetIteration(&si);
|
||||
* This is mandatory! si may contain references to iterator objects,
|
||||
* keys and values, and they must be cleaned up else they'll leak. If
|
||||
* this were C++ we'd hide that in the destructor, but in C you have to
|
||||
* do it by hand.
|
||||
*/
|
||||
typedef struct SetIteration_s
|
||||
{
|
||||
PyObject *set; /* the set, bucket, BTree, ..., being iterated */
|
||||
int position; /* initialized to 0; set to -1 by next() when done */
|
||||
int usesValue; /* true iff 'set' has values & we iterate them */
|
||||
KEY_TYPE key; /* next() sets to next key */
|
||||
VALUE_TYPE value; /* next() may set to next value */
|
||||
int (*next)(struct SetIteration_s*); /* function to get next key+value */
|
||||
} SetIteration;
|
||||
|
||||
/* Finish the set iteration protocol. This MUST be called by everyone
|
||||
* who starts a set iteration, unless the initial call to initSetIteration
|
||||
* failed; in that case, and only that case, calling finiSetIteration is
|
||||
* optional.
|
||||
*/
|
||||
static void
|
||||
finiSetIteration(SetIteration *i)
|
||||
{
|
||||
assert(i != NULL);
|
||||
if (i->set == NULL)
|
||||
return;
|
||||
Py_DECREF(i->set);
|
||||
i->set = NULL; /* so it doesn't hurt to call this again */
|
||||
|
||||
if (i->position > 0) {
|
||||
/* next() was called at least once, but didn't finish iterating
|
||||
* (else position would be negative). So the cached key and
|
||||
* value need to be cleaned up.
|
||||
*/
|
||||
DECREF_KEY(i->key);
|
||||
if (i->usesValue) {
|
||||
DECREF_VALUE(i->value);
|
||||
}
|
||||
}
|
||||
i->position = -1; /* stop any stray next calls from doing harm */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
IndexError(int i)
|
||||
{
|
||||
PyObject *v;
|
||||
|
||||
v = INT_FROM_LONG(i);
|
||||
if (!v) {
|
||||
v = Py_None;
|
||||
Py_INCREF(v);
|
||||
}
|
||||
PyErr_SetObject(PyExc_IndexError, v);
|
||||
Py_DECREF(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for the bucket immediately preceding *current, in the bucket chain
|
||||
* starting at first. current, *current and first must not be NULL.
|
||||
*
|
||||
* Return:
|
||||
* 1 *current holds the correct bucket; this is a borrowed reference
|
||||
* 0 no such bucket exists; *current unaltered
|
||||
* -1 error; *current unaltered
|
||||
*/
|
||||
static int
|
||||
PreviousBucket(Bucket **current, Bucket *first)
|
||||
{
|
||||
Bucket *trailing = NULL; /* first travels; trailing follows it */
|
||||
int result = 0;
|
||||
|
||||
assert(current && *current && first);
|
||||
if (first == *current)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
trailing = first;
|
||||
PER_USE_OR_RETURN(first, -1);
|
||||
first = first->next;
|
||||
|
||||
((trailing)->state==cPersistent_STICKY_STATE
|
||||
&&
|
||||
((trailing)->state=cPersistent_UPTODATE_STATE));
|
||||
|
||||
PER_ACCESSED(trailing);
|
||||
|
||||
if (first == *current) {
|
||||
*current = trailing;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
} while (first);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void *
|
||||
BTree_Malloc(size_t sz)
|
||||
{
|
||||
void *r;
|
||||
|
||||
ASSERT(sz > 0, "non-positive size malloc", NULL);
|
||||
|
||||
r = malloc(sz);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
BTree_Realloc(void *p, size_t sz)
|
||||
{
|
||||
void *r;
|
||||
|
||||
ASSERT(sz > 0, "non-positive size realloc", NULL);
|
||||
|
||||
if (p)
|
||||
r = realloc(p, sz);
|
||||
else
|
||||
r = malloc(sz);
|
||||
|
||||
UNLESS (r)
|
||||
PyErr_NoMemory();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Shared keyword-argument list for BTree/Bucket
|
||||
* (iter)?(keys|values|items)
|
||||
*/
|
||||
static char *search_keywords[] = {"min", "max",
|
||||
"excludemin", "excludemax",
|
||||
0};
|
||||
|
||||
#include "BTreeItemsTemplate.c"
|
||||
#include "BucketTemplate.c"
|
||||
#include "SetTemplate.c"
|
||||
#include "BTreeTemplate.c"
|
||||
#include "TreeSetTemplate.c"
|
||||
#include "SetOpTemplate.c"
|
||||
#include "MergeTemplate.c"
|
||||
|
||||
static struct PyMethodDef module_methods[] = {
|
||||
{"difference", (PyCFunction) difference_m, METH_VARARGS,
|
||||
"difference(o1, o2) -- "
|
||||
"compute the difference between o1 and o2"
|
||||
},
|
||||
{"union", (PyCFunction) union_m, METH_VARARGS,
|
||||
"union(o1, o2) -- compute the union of o1 and o2\n"
|
||||
},
|
||||
{"intersection", (PyCFunction) intersection_m, METH_VARARGS,
|
||||
"intersection(o1, o2) -- "
|
||||
"compute the intersection of o1 and o2"
|
||||
},
|
||||
#ifdef MERGE
|
||||
{"weightedUnion", (PyCFunction) wunion_m, METH_VARARGS,
|
||||
"weightedUnion(o1, o2 [, w1, w2]) -- compute the union of o1 and o2\n"
|
||||
"\nw1 and w2 are weights."
|
||||
},
|
||||
{"weightedIntersection", (PyCFunction) wintersection_m, METH_VARARGS,
|
||||
"weightedIntersection(o1, o2 [, w1, w2]) -- "
|
||||
"compute the intersection of o1 and o2\n"
|
||||
"\nw1 and w2 are weights."
|
||||
},
|
||||
#endif
|
||||
#ifdef MULTI_INT_UNION
|
||||
{"multiunion", (PyCFunction) multiunion_m, METH_VARARGS,
|
||||
"multiunion(seq) -- compute union of a sequence of integer sets.\n"
|
||||
"\n"
|
||||
"Each element of seq must be an integer set, or convertible to one\n"
|
||||
"via the set iteration protocol. The union returned is an IISet."
|
||||
},
|
||||
#endif
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static char BTree_module_documentation[] =
|
||||
"\n"
|
||||
MASTER_ID
|
||||
BTREEITEMSTEMPLATE_C
|
||||
"$Id$\n"
|
||||
BTREETEMPLATE_C
|
||||
BUCKETTEMPLATE_C
|
||||
KEYMACROS_H
|
||||
MERGETEMPLATE_C
|
||||
SETOPTEMPLATE_C
|
||||
SETTEMPLATE_C
|
||||
TREESETTEMPLATE_C
|
||||
VALUEMACROS_H
|
||||
BTREEITEMSTEMPLATE_C
|
||||
;
|
||||
|
||||
int
|
||||
init_persist_type(PyTypeObject *type)
|
||||
{
|
||||
#ifdef PY3K
|
||||
((PyObject*)type)->ob_type = &PyType_Type;
|
||||
#else
|
||||
type->ob_type = &PyType_Type;
|
||||
#endif
|
||||
type->tp_base = cPersistenceCAPI->pertype;
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_" MOD_NAME_PREFIX "BTree", /* m_name */
|
||||
BTree_module_documentation, /* m_doc */
|
||||
-1, /* m_size */
|
||||
module_methods, /* m_methods */
|
||||
NULL, /* m_reload */
|
||||
NULL, /* m_traverse */
|
||||
NULL, /* m_clear */
|
||||
NULL, /* m_free */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static PyObject*
|
||||
module_init(void)
|
||||
{
|
||||
PyObject *module, *mod_dict, *interfaces, *conflicterr;
|
||||
|
||||
#ifdef KEY_TYPE_IS_PYOBJECT
|
||||
object_ = PyTuple_GetItem(Py_TYPE(Py_None)->tp_bases, 0);
|
||||
if (object_ == NULL)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
sort_str = INTERN("sort");
|
||||
if (!sort_str)
|
||||
return NULL;
|
||||
reverse_str = INTERN("reverse");
|
||||
if (!reverse_str)
|
||||
return NULL;
|
||||
__setstate___str = INTERN("__setstate__");
|
||||
if (!__setstate___str)
|
||||
return NULL;
|
||||
_bucket_type_str = INTERN("_bucket_type");
|
||||
if (!_bucket_type_str)
|
||||
return NULL;
|
||||
|
||||
max_internal_size_str = INTERN("max_internal_size");
|
||||
if (! max_internal_size_str)
|
||||
return NULL;
|
||||
max_leaf_size_str = INTERN("max_leaf_size");
|
||||
if (! max_leaf_size_str)
|
||||
return NULL;
|
||||
|
||||
/* Grab the ConflictError class */
|
||||
interfaces = PyImport_ImportModule("BTrees.Interfaces");
|
||||
if (interfaces != NULL)
|
||||
{
|
||||
conflicterr = PyObject_GetAttrString(interfaces, "BTreesConflictError");
|
||||
if (conflicterr != NULL)
|
||||
ConflictError = conflicterr;
|
||||
Py_DECREF(interfaces);
|
||||
}
|
||||
|
||||
if (ConflictError == NULL)
|
||||
{
|
||||
Py_INCREF(PyExc_ValueError);
|
||||
ConflictError=PyExc_ValueError;
|
||||
}
|
||||
|
||||
/* Initialize the PyPersist_C_API and the type objects. */
|
||||
#ifdef PY3K
|
||||
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import(
|
||||
"persistent.cPersistence.CAPI", 0);
|
||||
#else
|
||||
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCObject_Import(
|
||||
"persistent.cPersistence", "CAPI");
|
||||
#endif
|
||||
if (cPersistenceCAPI == NULL) {
|
||||
/* The Capsule API attempts to import 'persistent' and then
|
||||
* walk down to the specified attribute using getattr. If the C
|
||||
* extensions aren't available, this can result in an
|
||||
* AttributeError being raised. Let that percolate up as an
|
||||
* ImportError so it can be caught in the expected way.
|
||||
*/
|
||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_ImportError)) {
|
||||
PyErr_SetString(PyExc_ImportError, "persistent C extension unavailable");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
#define _SET_TYPE(typ) ((PyObject*)(&typ))->ob_type = &PyType_Type
|
||||
#else
|
||||
#define _SET_TYPE(typ) (typ).ob_type = &PyType_Type
|
||||
#endif
|
||||
_SET_TYPE(BTreeItemsType);
|
||||
_SET_TYPE(BTreeIter_Type);
|
||||
BTreeIter_Type.tp_getattro = PyObject_GenericGetAttr;
|
||||
BucketType.tp_new = PyType_GenericNew;
|
||||
SetType.tp_new = PyType_GenericNew;
|
||||
BTreeType.tp_new = PyType_GenericNew;
|
||||
TreeSetType.tp_new = PyType_GenericNew;
|
||||
if (!init_persist_type(&BucketType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&BTreeType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&SetType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&TreeSetType))
|
||||
return NULL;
|
||||
|
||||
if (PyDict_SetItem(BTreeType.tp_dict, _bucket_type_str,
|
||||
(PyObject *)&BucketType) < 0)
|
||||
{
|
||||
fprintf(stderr, "btree failed\n");
|
||||
return NULL;
|
||||
}
|
||||
if (PyDict_SetItem(TreeSetType.tp_dict, _bucket_type_str,
|
||||
(PyObject *)&SetType) < 0)
|
||||
{
|
||||
fprintf(stderr, "bucket failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create the module and add the functions */
|
||||
#ifdef PY3K
|
||||
module = PyModule_Create(&moduledef);
|
||||
#else
|
||||
module = Py_InitModule4("_" MOD_NAME_PREFIX "BTree",
|
||||
module_methods, BTree_module_documentation,
|
||||
(PyObject *)NULL, PYTHON_API_VERSION);
|
||||
#endif
|
||||
|
||||
/* Add some symbolic constants to the module */
|
||||
mod_dict = PyModule_GetDict(module);
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Bucket",
|
||||
(PyObject *)&BucketType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "BTree",
|
||||
(PyObject *)&BTreeType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Set",
|
||||
(PyObject *)&SetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeSet",
|
||||
(PyObject *)&TreeSetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeIterator",
|
||||
(PyObject *)&BTreeIter_Type) < 0)
|
||||
return NULL;
|
||||
/* We also want to be able to access these constants without the prefix
|
||||
* so that code can more easily exchange modules (particularly the integer
|
||||
* and long modules, but also others). The TreeIterator is only internal,
|
||||
* so we don't bother to expose that.
|
||||
*/
|
||||
if (PyDict_SetItemString(mod_dict, "Bucket",
|
||||
(PyObject *)&BucketType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "BTree",
|
||||
(PyObject *)&BTreeType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "Set",
|
||||
(PyObject *)&SetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "TreeSet",
|
||||
(PyObject *)&TreeSetType) < 0)
|
||||
return NULL;
|
||||
#if defined(ZODB_64BIT_INTS) && defined(NEED_LONG_LONG_SUPPORT)
|
||||
if (PyDict_SetItemString(mod_dict, "using64bits", Py_True) < 0)
|
||||
return NULL;
|
||||
#else
|
||||
if (PyDict_SetItemString(mod_dict, "using64bits", Py_False) < 0)
|
||||
return NULL;
|
||||
#endif
|
||||
return module;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
PyMODINIT_FUNC INITMODULE(void)
|
||||
{
|
||||
return module_init();
|
||||
}
|
||||
#else
|
||||
PyMODINIT_FUNC INITMODULE(void)
|
||||
{
|
||||
module_init();
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
=====================
|
||||
Developer Information
|
||||
=====================
|
||||
|
||||
This document provides information for developers who maintain or extend
|
||||
`BTrees`.
|
||||
|
||||
Macros
|
||||
======
|
||||
|
||||
`BTrees` are defined using a "template", roughly akin to a C++ template. To
|
||||
create a new family of `BTrees`, create a source file that defines macros used
|
||||
to handle differences in key and value types:
|
||||
|
||||
|
||||
Configuration Macros
|
||||
--------------------
|
||||
|
||||
``MASTER_ID``
|
||||
|
||||
A string to hold an RCS/CVS Id key to be included in compiled binaries.
|
||||
|
||||
``MOD_NAME_PREFIX``
|
||||
|
||||
A string (like "IO" or "OO") that provides the prefix used for the module.
|
||||
This gets used to generate type names and the internal module name string.
|
||||
|
||||
``DEFAULT_MAX_BUCKET_SIZE``
|
||||
|
||||
An int giving the maximum bucket size (number of key/value pairs). When a
|
||||
bucket gets larger than this due to an insertion *into a BTREE*, it
|
||||
splits. Inserting into a bucket directly doesn't split, and functions
|
||||
that produce a bucket output (e.g., ``union()``) also have no bound on how
|
||||
large a bucket may get. Someday this will be tunable on `BTree`.
|
||||
instances.
|
||||
|
||||
``DEFAULT_MAX_BTREE_SIZE``
|
||||
|
||||
An ``int`` giving the maximum size (number of children) of an internal
|
||||
btree node. Someday this will be tunable on ``BTree`` instances.
|
||||
|
||||
|
||||
Macros for Keys
|
||||
---------------
|
||||
|
||||
``KEY_TYPE``
|
||||
|
||||
The C type declaration for keys (e.g., ``int`` or ``PyObject*``).
|
||||
|
||||
``KEY_TYPE_IS_PYOBJECT``
|
||||
|
||||
Define if ``KEY_TYPE`` is a ``PyObject*`, else ``undef``.
|
||||
|
||||
``KEY_CHECK(K)``
|
||||
|
||||
Tests whether the ``PyObject* K`` can be converted to the (``C``) key type
|
||||
(``KEY_TYPE``). The macro should return a boolean (zero for false,
|
||||
non-zero for true). When it returns false, its caller should probably set
|
||||
a ``TypeError`` exception.
|
||||
|
||||
``KEY_CHECK_ON_SET(K)``
|
||||
|
||||
Like ``KEY_CHECK``, but only checked during ``__setitem__``.
|
||||
|
||||
``TEST_KEY_SET_OR(V, K, T)``
|
||||
|
||||
Like Python's ``cmp()``. Compares K(ey) to T(arget), where ``K``
|
||||
and ``T`` are ``C`` values of type `KEY_TYPE`. ``V`` is assigned an `int`
|
||||
value depending on the outcome::
|
||||
|
||||
< 0 if K < T
|
||||
== 0 if K == T
|
||||
> 0 if K > T
|
||||
|
||||
This macro acts like an ``if``, where the following statement is executed
|
||||
only if a Python exception has been raised because the values could not be
|
||||
compared.
|
||||
|
||||
``DECREF_KEY(K)``
|
||||
|
||||
``K`` is a value of ``KEY_TYPE``. If ``KEY_TYPE`` is a flavor of
|
||||
``PyObject*``, write this to do ``Py_DECREF(K)``. Else (e.g.,
|
||||
``KEY_TYPE`` is ``int``) make it a nop.
|
||||
|
||||
``INCREF_KEY(K)``
|
||||
|
||||
``K`` is a value of `KEY_TYPE`. If `KEY_TYPE` is a flavor of
|
||||
``PyObject*``, write this to do ``Py_INCREF(K)``. Else (e.g., `KEY_TYPE`
|
||||
is ``int``) make it a nop.
|
||||
|
||||
``COPY_KEY(K, E)``
|
||||
|
||||
Like ``K=E``. Copy a key from ``E`` to ``K``, both of ``KEY_TYPE``. Note
|
||||
that this doesn't ``decref K`` or ``incref E`` when ``KEY_TYPE`` is a
|
||||
``PyObject*``; the caller is responsible for keeping refcounts straight.
|
||||
|
||||
``COPY_KEY_TO_OBJECT(O, K)``
|
||||
|
||||
Roughly like ``O=K``. ``O`` is a ``PyObject*``, and the macro must build
|
||||
a Python object form of ``K``, assign it to ``O``, and ensure that ``O``
|
||||
owns the reference to its new value. It may do this by creating a new
|
||||
Python object based on ``K`` (e.g., ``PyInt_FromLong(K)`` when
|
||||
``KEY_TYPE`` is ``int``), or simply by doing ``Py_INCREF(K)`` if
|
||||
``KEY_TYPE`` is a ``PyObject*``.
|
||||
|
||||
``COPY_KEY_FROM_ARG(TARGET, ARG, STATUS)``
|
||||
|
||||
Copy an argument to the target without creating a new reference to
|
||||
``ARG``. ``ARG`` is a ``PyObject*``, and ``TARGET`` is of type
|
||||
``KEY_TYPE``. If this can't be done (for example, ``KEY_CHECK(ARG)``
|
||||
returns false), set a Python error and set status to ``0``. If there is
|
||||
no error, leave status alone.
|
||||
|
||||
|
||||
Macros for Values
|
||||
-----------------
|
||||
|
||||
``VALUE_TYPE``
|
||||
|
||||
The C type declaration for values (e.g., ``int`` or ``PyObject*``).
|
||||
|
||||
``VALUE_TYPE_IS_PYOBJECT``
|
||||
|
||||
Define if ``VALUE_TYPE`` is a ``PyObject*``, else ``undef``.
|
||||
|
||||
``TEST_VALUE(X, Y)``
|
||||
|
||||
Like Python's ``cmp()``. Compares ``X`` to ``Y``, where ``X`` & ``Y`` are
|
||||
``C`` values of type ``VALUE_TYPE``. The macro returns an ``int``, with
|
||||
value::
|
||||
|
||||
< 0 if X < Y
|
||||
== 0 if X == Y
|
||||
> 0 if X > Y
|
||||
|
||||
Bug: There is no provision for determining whether the comparison attempt
|
||||
failed (set a Python exception).
|
||||
|
||||
``DECREF_VALUE(K)``
|
||||
|
||||
Like ``DECREF_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``INCREF_VALUE(K)``
|
||||
|
||||
Like ``INCREF_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE(K, E)``
|
||||
|
||||
Like ``COPY_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE_TO_OBJECT(O, K)``
|
||||
|
||||
Like ``COPY_KEY_TO_OBJECT``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS)``
|
||||
|
||||
Like ``COPY_KEY_FROM_ARG``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``NORMALIZE_VALUE(V, MIN)``
|
||||
|
||||
Normalize the value, ``V``, using the parameter ``MIN``. This is almost
|
||||
certainly a YAGNI. It is a no-op for most types. For integers, ``V`` is
|
||||
replaced by ``V/MIN`` only if ``MIN > 0``.
|
||||
|
||||
|
||||
Macros for Set Operations
|
||||
-------------------------
|
||||
|
||||
``MERGE_DEFAULT``
|
||||
|
||||
A value of ``VALUE_TYPE`` specifying the value to associate with set
|
||||
elements when sets are merged with mappings via weighed union or weighted
|
||||
intersection.
|
||||
|
||||
``MERGE(O1, w1, O2, w2)``
|
||||
|
||||
Performs a weighted merge of two values, ``O1`` and ``O2``, using weights
|
||||
``w1`` and ``w2``. The result must be of ``VALUE_TYPE``. Note that
|
||||
weighted unions and weighted intersections are not enabled if this macro
|
||||
is left undefined.
|
||||
|
||||
``MERGE_WEIGHT(O, w)``
|
||||
|
||||
Computes a weighted value for ``O``. The result must be of
|
||||
``VALUE_TYPE``. This is used for "filling out" weighted unions, i.e. to
|
||||
compute a weighted value for keys that appear in only one of the input
|
||||
mappings. If left undefined, ``MERGE_WEIGHT`` defaults to::
|
||||
|
||||
#define MERGE_WEIGHT(O, w) (O)
|
||||
|
||||
``MULTI_INT_UNION``
|
||||
|
||||
The value doesn't matter. If defined, `SetOpTemplate.c` compiles code for
|
||||
a ``multiunion()`` function (compute a union of many input sets at high
|
||||
speed). This currently makes sense only for structures with integer keys.
|
||||
|
||||
|
||||
BTree Clues
|
||||
===========
|
||||
|
||||
More or less random bits of helpful info.
|
||||
|
||||
+ In papers and textbooks, this flavor of BTree is usually called a B+-Tree,
|
||||
where "+" is a superscript.
|
||||
|
||||
+ All keys and all values live in the bucket leaf nodes. Keys in interior
|
||||
(BTree) nodes merely serve to guide a search efficiently toward the correct
|
||||
leaf.
|
||||
|
||||
+ When a key is deleted, it's physically removed from the bucket it's in, but
|
||||
this doesn't propagate back up the tree: since keys in interior nodes only
|
||||
serve to guide searches, it's OK-- and saves time --to leave "stale" keys in
|
||||
interior nodes.
|
||||
|
||||
+ No attempt is made to rebalance the tree after a deletion, unless a bucket
|
||||
thereby becomes entirely empty. "Classic BTrees" do rebalance, keeping all
|
||||
buckets at least half full (provided there are enough keys in the entire
|
||||
tree to fill half a bucket). The tradeoffs are murky. Pathological cases
|
||||
in the presence of deletion do exist. Pathologies include trees tending
|
||||
toward only one key per bucket, and buckets at differing depths (all buckets
|
||||
are at the same depth in a classic BTree).
|
||||
|
||||
+ ``DEFAULT_MAX_BUCKET_SIZE`` and ``DEFAULT_MAX_BTREE_SIZE`` are chosen mostly
|
||||
to "even out" pickle sizes in storage. That's why, e.g., an `IIBTree` has
|
||||
larger values than an `OOBTree`: pickles store ints more efficiently than
|
||||
they can store arbitrary Python objects.
|
||||
|
||||
+ In a non-empty BTree, every bucket node contains at least one key, and every
|
||||
BTree node contains at least one child and a non-NULL firstbucket pointer.
|
||||
However, a BTree node may not contain any keys.
|
||||
|
||||
+ An empty BTree consists solely of a BTree node with ``len==0`` and
|
||||
``firstbucket==NULL``.
|
||||
|
||||
+ Although a BTree can become unbalanced under a mix of inserts and deletes
|
||||
(meaning both that there's nothing stronger that can be said about buckets
|
||||
than that they're not empty, and that buckets can appear at different
|
||||
depths), a BTree node always has children of the same kind: they're all
|
||||
buckets, or they're all BTree nodes.
|
||||
|
||||
|
||||
The ``BTREE_SEARCH`` Macro
|
||||
==========================
|
||||
|
||||
For notational ease, consider a fixed BTree node ``x``, and let
|
||||
|
||||
::
|
||||
|
||||
K(i) mean x->data.key[i]
|
||||
C(i) mean all the keys reachable from x->data.child[i]
|
||||
|
||||
For each ``i`` in ``0`` to ``x->len-1`` inclusive,
|
||||
|
||||
::
|
||||
|
||||
K(i) <= C(i) < K(i+1)
|
||||
|
||||
is a BTree node invariant, where we pretend that ``K(0)`` holds a key smaller
|
||||
than any possible key, and ``K(x->len)`` holds a key larger than any possible
|
||||
key. (Note that ``K(x->len)`` doesn't actually exist, and ``K(0)`` is never
|
||||
used although space for it exists in non-empty BTree nodes.)
|
||||
|
||||
When searching for a key ``k``, then, the child pointer we want to follow is
|
||||
the one at index ``i`` such that ``K(i) <= k < K(i+1)``. There can be at most
|
||||
one such ``i``, since the ``K(i)`` are strictly increasing. And there is at
|
||||
least one such ``i`` provided the tree isn't empty (so that ``0 < len``). For
|
||||
the moment, assume the tree isn't empty (we'll get back to that later).
|
||||
|
||||
The macro's chief loop invariant is
|
||||
|
||||
::
|
||||
|
||||
K(lo) < k < K(hi)
|
||||
|
||||
This holds trivially at the start, since ``lo`` is set to ``0``, and ``hi`` to
|
||||
``x->len``, and we pretend ``K(0)`` is minus infinity and ``K(len)`` is plus
|
||||
infinity. Inside the loop, if ``K(i) < k`` we set ``lo`` to ``i``, and if
|
||||
``K(i) > k`` we set ``hi`` to ``i``. These obviously preserve the invariant.
|
||||
If ``K(i) == k``, the loop breaks and sets the result to ``i``, and since
|
||||
``K(i) == k`` in that case ``i`` is obviously the correct result.
|
||||
|
||||
Other cases depend on how ``i = floor((lo + hi)/2)`` works, exactly. Suppose
|
||||
``lo + d = hi`` for some ``d >= 0``. Then ``i = floor((lo + lo + d)/2) =
|
||||
floor(lo + d/2) = lo + floor(d/2)``. So:
|
||||
|
||||
a. ``[d == 0] (lo == i == hi)`` if and only if ``(lo == hi)``.
|
||||
b. ``[d == 1] (lo == i < hi)`` if and only if ``(lo+1 == hi)``.
|
||||
c. ``[d > 1] (lo < i < hi)`` if and only if ``(lo+1 < hi)``.
|
||||
|
||||
If the node is empty ``(x->len == 0)``, then ``lo==i==hi==0`` at the start,
|
||||
and the loop exits immediately (the first ``i > lo`` test fails), without
|
||||
entering the body.
|
||||
|
||||
Else ``lo < hi`` at the start, and the invariant ``K(lo) < k < K(hi)`` holds.
|
||||
|
||||
If ``lo+1 < hi``, we're in case (c): ``i`` is strictly between ``lo`` and
|
||||
``hi``, so the loop body is entered, and regardless of whether the body sets
|
||||
the new ``lo`` or the new ``hi`` to ``i``, the new ``lo`` is strictly less
|
||||
than the new ``hi``, and the difference between the new ``lo`` and new ``hi``
|
||||
is strictly less than the difference between the old ``lo`` and old ``hi``.
|
||||
So long as the new ``lo + 1`` remains < the new ``hi``, we stay in this case.
|
||||
We can't stay in this case forever, though: because ``hi-lo`` decreases on
|
||||
each trip but remains > ``0``, ``lo+1 == hi`` must eventually become true.
|
||||
(In fact, it becomes true quickly, in about ``log2(x->len)`` trips; the point
|
||||
is more that ``lo`` doesn't equal ``hi`` when the loop ends, it has to end
|
||||
with ``lo+1==hi`` and ``i==lo``).
|
||||
|
||||
Then we're in case (b): ``i==lo==hi-1`` then, and the loop exits. The
|
||||
invariant still holds, with ``lo==i`` and ``hi==lo+1==i+1``::
|
||||
|
||||
K(i) < k < K(i+1)
|
||||
|
||||
so ``i`` is again the correct answer.
|
||||
|
||||
|
||||
Optimization points:
|
||||
--------------------
|
||||
|
||||
+ Division by 2 is done via shift rather via "/2". These are signed ints, and
|
||||
almost all C compilers treat signed int division as truncating, and shifting
|
||||
is not the same as truncation for signed int division. The compiler has no
|
||||
way to know these values aren't negative, so has to generate longer-winded
|
||||
code for "/2". But we know these values aren't negative, and exploit it.
|
||||
|
||||
+ The order of _cmp comparisons matters. We're in an interior BTree node, and
|
||||
are looking at only a tiny fraction of all the keys that exist. So finding
|
||||
the key exactly in this node is unlikely, and checking ``_cmp == 0`` is a
|
||||
waste of time to the same extent. It doesn't matter whether we check for
|
||||
``_cmp < 0`` or ``_cmp > 0`` first, so long as we do both before worrying
|
||||
about equality.
|
||||
|
||||
+ At the start of a routine, it's better to run this macro even if ``x->len``
|
||||
is ``0`` (check for that afterwards). We just called a function and so
|
||||
probably drained the pipeline. If the first thing we do then is read up
|
||||
``self->len`` and check it against ``0``, we just sit there waiting for the
|
||||
data to get read up, and then another immediate test-and-branch, and for a
|
||||
very unlikely case (BTree nodes are rarely empty). It's better to get into
|
||||
the loop right away so the normal case makes progress ASAP.
|
||||
|
||||
|
||||
The ``BUCKET_SEARCH`` Macro
|
||||
===========================
|
||||
|
||||
This has a different job than ``BTREE_SEARCH``: the key ``0`` slot is
|
||||
legitimate in a bucket, and we want to find the index at which the key
|
||||
belongs. If the key is larger than the bucket's largest key, a new slot at
|
||||
index len is where it belongs, else it belongs at the smallest ``i`` with
|
||||
``keys[i]`` >= the key we're looking for. We also need to know whether or not
|
||||
the key is present (``BTREE_SEARCH`` didn't care; it only wanted to find the
|
||||
next node to search).
|
||||
|
||||
The mechanics of the search are quite similar, though. The primary
|
||||
loop invariant changes to (say we're searching for key ``k``)::
|
||||
|
||||
K(lo-1) < k < K(hi)
|
||||
|
||||
where ``K(i)`` means ``keys[i]``, and we pretend ``K(-1)`` is minus infinity
|
||||
and ``K(len)`` is plus infinity.
|
||||
|
||||
If the bucket is empty, ``lo=hi=i=0`` at the start, the loop body is never
|
||||
entered, and the macro sets ``INDEX`` to 0 and ``ABSENT`` to true. That's why
|
||||
``_cmp`` is initialized to 1 (``_cmp`` becomes ``ABSENT``).
|
||||
|
||||
Else the bucket is not empty, lo<hi at the start, and the loop body is
|
||||
entered. The invariant is obviously satisfied then, as ``lo=0`` and
|
||||
``hi=len``.
|
||||
|
||||
If ``K[i]<k``, ``lo`` is set to ``i+1``, preserving that ``K(lo-1) = K[i] <
|
||||
k``.
|
||||
|
||||
If ``K[i]>k``, ``hi`` is set to ``i``, preserving that ``K[hi] = K[i] > k``.
|
||||
|
||||
If the loop exits after either of those, ``_cmp != 0``, so ``ABSENT`` becomes
|
||||
true.
|
||||
|
||||
If ``K[i]=k``, the loop breaks, so that ``INDEX`` becomes ``i``, and
|
||||
``ABSENT`` becomes false (``_cmp=0`` in this case).
|
||||
|
||||
The same case analysis for ``BTREE_SEARCH`` on ``lo`` and ``hi`` holds here:
|
||||
|
||||
a. ``(lo == i == hi)`` if and only if ``(lo == hi)``.
|
||||
b. ``(lo == i < hi)`` if and only if ``(lo+1 == hi)``.
|
||||
c. ``(lo < i < hi)`` if and only if ``(lo+1 < hi)``.
|
||||
|
||||
So long as ``lo+1 < hi``, we're in case (c), and either break with equality
|
||||
(in which case the right results are obviously computed) or narrow the range.
|
||||
If equality doesn't obtain, the range eventually narrows to cases (a) or (b).
|
||||
|
||||
To go from (c) to (a), we must have ``lo+2==hi`` at the start, and
|
||||
``K[i]=K[lo+1]<k``. Then the new lo gets set to ``i+1 = lo+2 = hi``, and the
|
||||
loop exits with ``lo=hi=i`` and ``_cmp<0``. This is correct, because we know
|
||||
that ``k != K(i)`` (loop invariant! we actually know something stronger, that
|
||||
``k < K(hi)``; since ``i=hi``, this implies ``k != K(i)``).
|
||||
|
||||
Else (c) eventually falls into case (b), ``lo+1==hi`` and ``i==lo``. The
|
||||
invariant tells us ``K(lo-1) < k < K(hi) = K(lo+1)``, so if the key is present
|
||||
it must be at ``K(lo)``. ``i==lo`` in this case, so we test ``K(lo)`` against
|
||||
``k``. As always, if equality obtains we do the right thing, else case #b
|
||||
becomes case (a).
|
||||
|
||||
When (b) becomes (a), the last comparison was non-equal, so ``_cmp`` is
|
||||
non-zero, and the loop exits because ``lo==hi==i`` in case (a). The invariant
|
||||
then tells us ``K(lo-1) < k < K(lo)``, so the key is in fact not present, it's
|
||||
correct to exit with ``_cmp`` non-zero, and ``i==lo`` is again the index at
|
||||
which ``k`` belongs.
|
||||
|
||||
|
||||
Optimization points:
|
||||
--------------------
|
||||
|
||||
+ As for ``BTREE_SEARCH``, shifting of signed ints is cheaper than division.
|
||||
|
||||
+ Unlike as for ``BTREE_SEARCH``, there's nothing special about searching an
|
||||
empty bucket, and the macro computes thoroughly sensible results in that
|
||||
case.
|
||||
|
||||
+ The order of ``_cmp`` comparisons differs from ``BTREE_SEARCH``. When
|
||||
searching a bucket, it's much more likely (than when searching a BTree node)
|
||||
that the key is present, so testing ``__cmp==0`` isn't a systematic waste of
|
||||
cycles. At the extreme, if all searches are successful (key present), on
|
||||
average this saves one comparison per search, against leaving the
|
||||
determination of ``_cmp==0`` implicit (as ``BTREE_SEARCH`` does). But even
|
||||
on successful searches, ``__cmp != 0`` is a more popular outcome than
|
||||
``__cmp == 0`` across iterations (unless the bucket has only a few keys), so
|
||||
it's important to check one of the inequality cases first. It turns out
|
||||
it's better on average to check ``K(i) < key`` (than to check ``K(i) >
|
||||
key``), because when it pays it narrows the range more (we get a little
|
||||
boost from setting ``lo=i+1`` in this case; the other case sets ``hi=i``,
|
||||
which isn't as much of a narrowing).
|
||||
@@ -0,0 +1,112 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IFBucket', 'IFSet', 'IFBTree', 'IFTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerFloatBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
from ._base import to_float as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
class IFBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IFBucketPy._mapping_type = IFBucketPy._bucket_type = IFBucketPy
|
||||
IFBucketPy._set_type = IFSetPy
|
||||
|
||||
IFSetPy._mapping_type = IFBucketPy
|
||||
IFSetPy._set_type = IFSetPy._bucket_type = IFSetPy
|
||||
|
||||
IFBTreePy._mapping_type = IFBTreePy._bucket_type = IFBucketPy
|
||||
IFBTreePy._set_type = IFSetPy
|
||||
|
||||
IFTreeSetPy._mapping_type = IFBucketPy
|
||||
IFTreeSetPy._set_type = IFTreeSetPy._bucket_type = IFSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IFSetPy)
|
||||
unionPy = _set_operation(_union, IFSetPy)
|
||||
intersectionPy = _set_operation(_intersection, IFSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IFSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, IFSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, IFSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerFloatBTreeModule)
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IIBucket', 'IISet', 'IIBTree', 'IITreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
_to_value = _to_key
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
|
||||
class IIBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IISetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IIBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IITreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IITreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IIBucketPy._mapping_type = IIBucketPy._bucket_type = IIBucketPy
|
||||
IIBucketPy._set_type = IISetPy
|
||||
|
||||
IISetPy._mapping_type = IIBucketPy
|
||||
IISetPy._set_type = IISetPy._bucket_type = IISetPy
|
||||
|
||||
IIBTreePy._mapping_type = IIBTreePy._bucket_type = IIBucketPy
|
||||
IIBTreePy._set_type = IISetPy
|
||||
|
||||
IITreeSetPy._mapping_type = IIBucketPy
|
||||
IITreeSetPy._set_type = IITreeSetPy._bucket_type = IISetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IISetPy)
|
||||
unionPy = _set_operation(_union, IISetPy)
|
||||
intersectionPy = _set_operation(_intersection, IISetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IISetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, IISetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, IISetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerIntegerBTreeModule)
|
||||
@@ -0,0 +1,95 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IOBucket', 'IOSet', 'IOBTree', 'IOTreeSet',
|
||||
'union', 'intersection', 'difference', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE_WEIGHT_default
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
from ._base import to_ob as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
|
||||
class IOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class IOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class IOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class IOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
class IOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IOBucketPy._mapping_type = IOBucketPy._bucket_type = IOBucketPy
|
||||
IOBucketPy._set_type = IOSetPy
|
||||
|
||||
IOSetPy._mapping_type = IOBucketPy
|
||||
IOSetPy._set_type = IOSetPy._bucket_type = IOSetPy
|
||||
|
||||
IOBTreePy._mapping_type = IOBTreePy._bucket_type = IOBucketPy
|
||||
IOBTreePy._set_type = IOSetPy
|
||||
|
||||
IOTreeSetPy._mapping_type = IOBucketPy
|
||||
IOTreeSetPy._set_type = IOTreeSetPy._bucket_type = IOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IOSetPy)
|
||||
unionPy = _set_operation(_union, IOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, IOSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,527 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
|
||||
|
||||
class ICollection(Interface):
|
||||
|
||||
def clear():
|
||||
"""Remove all of the items from the collection."""
|
||||
|
||||
def __nonzero__():
|
||||
"""Check if the collection is non-empty.
|
||||
|
||||
Return a true value if the collection is non-empty and a
|
||||
false value otherwise.
|
||||
"""
|
||||
|
||||
|
||||
class IReadSequence(Interface):
|
||||
|
||||
def __getitem__(index):
|
||||
"""Return the value at the given index.
|
||||
|
||||
An IndexError is raised if the index cannot be found.
|
||||
"""
|
||||
|
||||
def __getslice__(index1, index2):
|
||||
"""Return a subsequence from the original sequence.
|
||||
|
||||
The subsequence includes the items from index1 up to, but not
|
||||
including, index2.
|
||||
"""
|
||||
|
||||
class IKeyed(ICollection):
|
||||
|
||||
def has_key(key):
|
||||
"""Check whether the object has an item with the given key.
|
||||
|
||||
Return a true value if the key is present, else a false value.
|
||||
"""
|
||||
|
||||
def keys(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the keys in the collection.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to keys greater
|
||||
than or equal to the given min, and, if excludemin is specified and
|
||||
true, is further constrained to keys strictly greater than min. A
|
||||
min value of None is ignored. If min is None or not specified, and
|
||||
excludemin is true, the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to keys less than
|
||||
or equal to the given max, and, if excludemax is specified and
|
||||
true, is further constrained to keys strictly less than max. A max
|
||||
value of None is ignored. If max is None or not specified, and
|
||||
excludemax is true, the largest key is excluded.
|
||||
"""
|
||||
|
||||
def maxKey(key=None):
|
||||
"""Return the maximum key.
|
||||
|
||||
If a key argument if provided and not None, return the largest key
|
||||
that is less than or equal to the argument. Raise an exception if
|
||||
no such key exists.
|
||||
"""
|
||||
|
||||
def minKey(key=None):
|
||||
"""Return the minimum key.
|
||||
|
||||
If a key argument if provided and not None, return the smallest key
|
||||
that is greater than or equal to the argument. Raise an exception
|
||||
if no such key exists.
|
||||
"""
|
||||
|
||||
|
||||
class ISetMutable(IKeyed):
|
||||
|
||||
def insert(key):
|
||||
"""Add the key (value) to the set.
|
||||
|
||||
If the key was already in the set, return 0, otherwise return 1.
|
||||
"""
|
||||
|
||||
def remove(key):
|
||||
"""Remove the key from the set.
|
||||
|
||||
Raises KeyError if key is not in the set.
|
||||
"""
|
||||
|
||||
def update(seq):
|
||||
"""Add the items from the given sequence to the set."""
|
||||
|
||||
|
||||
class ISized(Interface):
|
||||
"""An object that supports __len__."""
|
||||
|
||||
def __len__():
|
||||
"""Return the number of items in the container."""
|
||||
|
||||
|
||||
class IKeySequence(IKeyed, ISized):
|
||||
|
||||
def __getitem__(index):
|
||||
"""Return the key in the given index position.
|
||||
|
||||
This allows iteration with for loops and use in functions,
|
||||
like map and list, that read sequences.
|
||||
"""
|
||||
|
||||
|
||||
class ISet(IKeySequence, ISetMutable):
|
||||
pass
|
||||
|
||||
|
||||
class ITreeSet(IKeyed, ISetMutable):
|
||||
pass
|
||||
|
||||
class IMinimalDictionary(ISized, IKeyed):
|
||||
|
||||
def get(key, default):
|
||||
"""Get the value associated with the given key.
|
||||
|
||||
Return the default if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def __getitem__(key):
|
||||
"""Get the value associated with the given key.
|
||||
|
||||
Raise KeyError if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def __setitem__(key, value):
|
||||
"""Set the value associated with the given key."""
|
||||
|
||||
def __delitem__(key):
|
||||
"""Delete the value associated with the given key.
|
||||
|
||||
Raise KeyError if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def values(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the values in the collection.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to values whose
|
||||
keys are greater than or equal to the given min, and, if excludemin
|
||||
is specified and true, is further constrained to values whose keys
|
||||
are strictly greater than min. A min value of None is ignored. If
|
||||
min is None or not specified, and excludemin is true, the value
|
||||
corresponding to the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to values whose
|
||||
keys are less than or equal to the given max, and, if excludemax is
|
||||
specified and true, is further constrained to values whose keys are
|
||||
strictly less than max. A max value of None is ignored. If max is
|
||||
None or not specified, and excludemax is true, the value
|
||||
corresponding to the largest key is excluded.
|
||||
"""
|
||||
|
||||
def items(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the items in the collection.
|
||||
|
||||
An item is a 2-tuple, a (key, value) pair.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to items whose
|
||||
keys are greater than or equal to the given min, and, if excludemin
|
||||
is specified and true, is further constrained to items whose keys
|
||||
are strictly greater than min. A min value of None is ignored. If
|
||||
min is None or not specified, and excludemin is true, the item with
|
||||
the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to items whose
|
||||
keys are less than or equal to the given max, and, if excludemax is
|
||||
specified and true, is further constrained to items whose keys are
|
||||
strictly less than max. A max value of None is ignored. If max is
|
||||
None or not specified, and excludemax is true, the item with the
|
||||
largest key is excluded.
|
||||
"""
|
||||
|
||||
class IDictionaryIsh(IMinimalDictionary):
|
||||
|
||||
def update(collection):
|
||||
"""Add the items from the given collection object to the collection.
|
||||
|
||||
The input collection must be a sequence of (key, value) 2-tuples,
|
||||
or an object with an 'items' method that returns a sequence of
|
||||
(key, value) pairs.
|
||||
"""
|
||||
|
||||
def byValue(minValue):
|
||||
"""Return a sequence of (value, key) pairs, sorted by value.
|
||||
|
||||
Values < minValue are omitted and other values are "normalized" by
|
||||
the minimum value. This normalization may be a noop, but, for
|
||||
integer values, the normalization is division.
|
||||
"""
|
||||
|
||||
def setdefault(key, d):
|
||||
"""D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D.
|
||||
|
||||
Return the value like get() except that if key is missing, d is both
|
||||
returned and inserted into the dictionary as the value of k.
|
||||
|
||||
Note that, unlike as for Python's dict.setdefault(), d is not
|
||||
optional. Python defaults d to None, but that doesn't make sense
|
||||
for mappings that can't have None as a value (for example, an
|
||||
IIBTree can have only integers as values).
|
||||
"""
|
||||
|
||||
def pop(key, d):
|
||||
"""D.pop(k[, d]) -> v, remove key and return the corresponding value.
|
||||
|
||||
If key is not found, d is returned if given, otherwise KeyError is
|
||||
raised.
|
||||
"""
|
||||
|
||||
|
||||
class IBTree(IDictionaryIsh):
|
||||
|
||||
def insert(key, value):
|
||||
"""Insert a key and value into the collection.
|
||||
|
||||
If the key was already in the collection, then there is no
|
||||
change and 0 is returned.
|
||||
|
||||
If the key was not already in the collection, then the item is
|
||||
added and 1 is returned.
|
||||
|
||||
This method is here to allow one to generate random keys and
|
||||
to insert and test whether the key was there in one operation.
|
||||
|
||||
A standard idiom for generating new keys will be::
|
||||
|
||||
key = generate_key()
|
||||
while not t.insert(key, value):
|
||||
key=generate_key()
|
||||
"""
|
||||
|
||||
|
||||
class IMerge(Interface):
|
||||
"""Object with methods for merging sets, buckets, and trees.
|
||||
|
||||
These methods are supplied in modules that define collection
|
||||
classes with particular key and value types. The operations apply
|
||||
only to collections from the same module. For example, the
|
||||
IIBTree.union can only be used with IIBTree.IIBTree,
|
||||
IIBTree.IIBucket, IIBTree.IISet, and IIBTree.IITreeSet.
|
||||
|
||||
The implementing module has a value type. The IOBTree and OOBTree
|
||||
modules have object value type. The IIBTree and OIBTree modules
|
||||
have integer value types. Other modules may be defined in the
|
||||
future that have other value types.
|
||||
|
||||
The individual types are classified into set (Set and TreeSet) and
|
||||
mapping (Bucket and BTree) types.
|
||||
"""
|
||||
|
||||
def difference(c1, c2):
|
||||
"""Return the keys or items in c1 for which there is no key in c2.
|
||||
|
||||
If c1 is None, then None is returned. If c2 is None, then c1
|
||||
is returned.
|
||||
|
||||
If neither c1 nor c2 is None, the output is a Set if c1 is a Set or
|
||||
TreeSet, and is a Bucket if c1 is a Bucket or BTree.
|
||||
"""
|
||||
|
||||
def union(c1, c2):
|
||||
"""Compute the Union of c1 and c2.
|
||||
|
||||
If c1 is None, then c2 is returned, otherwise, if c2 is None,
|
||||
then c1 is returned.
|
||||
|
||||
The output is a Set containing keys from the input
|
||||
collections.
|
||||
"""
|
||||
|
||||
def intersection(c1, c2):
|
||||
"""Compute the intersection of c1 and c2.
|
||||
|
||||
If c1 is None, then c2 is returned, otherwise, if c2 is None,
|
||||
then c1 is returned.
|
||||
|
||||
The output is a Set containing matching keys from the input
|
||||
collections.
|
||||
"""
|
||||
|
||||
|
||||
class IBTreeModule(Interface):
|
||||
"""These are available in all modules (IOBTree, OIBTree, OOBTree, IIBTree,
|
||||
IFBTree, LFBTree, LOBTree, OLBTree, and LLBTree).
|
||||
"""
|
||||
|
||||
BTree = Attribute(
|
||||
"""The IBTree for this module.
|
||||
|
||||
Also available as [prefix]BTree, as in IOBTree.""")
|
||||
|
||||
Bucket = Attribute(
|
||||
"""The leaf-node data buckets used by the BTree.
|
||||
|
||||
(IBucket is not currently defined in this file, but is essentially
|
||||
IDictionaryIsh, with the exception of __nonzero__, as of this
|
||||
writing.)
|
||||
|
||||
Also available as [prefix]Bucket, as in IOBucket.""")
|
||||
|
||||
TreeSet = Attribute(
|
||||
"""The ITreeSet for this module.
|
||||
|
||||
Also available as [prefix]TreeSet, as in IOTreeSet.""")
|
||||
|
||||
Set = Attribute(
|
||||
"""The ISet for this module: the leaf-node data buckets used by the
|
||||
TreeSet.
|
||||
|
||||
Also available as [prefix]BTree, as in IOSet.""")
|
||||
|
||||
|
||||
class IIMerge(IMerge):
|
||||
"""Merge collections with integer value type.
|
||||
|
||||
A primary intent is to support operations with no or integer
|
||||
values, which are used as "scores" to rate indiviual keys. That
|
||||
is, in this context, a BTree or Bucket is viewed as a set with
|
||||
scored keys, using integer scores.
|
||||
"""
|
||||
|
||||
def weightedUnion(c1, c2, weight1=1, weight2=1):
|
||||
"""Compute the weighted union of c1 and c2.
|
||||
|
||||
If c1 and c2 are None, the output is (0, None).
|
||||
|
||||
If c1 is None and c2 is not None, the output is (weight2, c2).
|
||||
|
||||
If c1 is not None and c2 is None, the output is (weight1, c1).
|
||||
|
||||
Else, and hereafter, c1 is not None and c2 is not None.
|
||||
|
||||
If c1 and c2 are both sets, the output is 1 and the (unweighted)
|
||||
union of the sets.
|
||||
|
||||
Else the output is 1 and a Bucket whose keys are the union of c1 and
|
||||
c2's keys, and whose values are::
|
||||
|
||||
v1*weight1 + v2*weight2
|
||||
|
||||
where:
|
||||
|
||||
v1 is 0 if the key is not in c1
|
||||
1 if the key is in c1 and c1 is a set
|
||||
c1[key] if the key is in c1 and c1 is a mapping
|
||||
|
||||
v2 is 0 if the key is not in c2
|
||||
1 if the key is in c2 and c2 is a set
|
||||
c2[key] if the key is in c2 and c2 is a mapping
|
||||
|
||||
Note that c1 and c2 must be collections.
|
||||
"""
|
||||
|
||||
def weightedIntersection(c1, c2, weight1=1, weight2=1):
|
||||
"""Compute the weighted intersection of c1 and c2.
|
||||
|
||||
If c1 and c2 are None, the output is (0, None).
|
||||
|
||||
If c1 is None and c2 is not None, the output is (weight2, c2).
|
||||
|
||||
If c1 is not None and c2 is None, the output is (weight1, c1).
|
||||
|
||||
Else, and hereafter, c1 is not None and c2 is not None.
|
||||
|
||||
If c1 and c2 are both sets, the output is the sum of the weights
|
||||
and the (unweighted) intersection of the sets.
|
||||
|
||||
Else the output is 1 and a Bucket whose keys are the intersection of
|
||||
c1 and c2's keys, and whose values are::
|
||||
|
||||
v1*weight1 + v2*weight2
|
||||
|
||||
where:
|
||||
|
||||
v1 is 1 if c1 is a set
|
||||
c1[key] if c1 is a mapping
|
||||
|
||||
v2 is 1 if c2 is a set
|
||||
c2[key] if c2 is a mapping
|
||||
|
||||
Note that c1 and c2 must be collections.
|
||||
"""
|
||||
|
||||
|
||||
class IMergeIntegerKey(IMerge):
|
||||
"""IMerge-able objects with integer keys.
|
||||
|
||||
Concretely, this means the types in IOBTree and IIBTree.
|
||||
"""
|
||||
|
||||
def multiunion(seq):
|
||||
"""Return union of (zero or more) integer sets, as an integer set.
|
||||
|
||||
seq is a sequence of objects each convertible to an integer set.
|
||||
These objects are convertible to an integer set:
|
||||
|
||||
+ An integer, which is added to the union.
|
||||
|
||||
+ A Set or TreeSet from the same module (for example, an
|
||||
IIBTree.TreeSet for IIBTree.multiunion()). The elements of the
|
||||
set are added to the union.
|
||||
|
||||
+ A Bucket or BTree from the same module (for example, an
|
||||
IOBTree.IOBTree for IOBTree.multiunion()). The keys of the
|
||||
mapping are added to the union.
|
||||
|
||||
The union is returned as a Set from the same module (for example,
|
||||
IIBTree.multiunion() returns an IIBTree.IISet).
|
||||
|
||||
The point to this method is that it can run much faster than
|
||||
doing a sequence of two-input union() calls. Under the covers,
|
||||
all the integers in all the inputs are sorted via a single
|
||||
linear-time radix sort, then duplicates are removed in a second
|
||||
linear-time pass.
|
||||
"""
|
||||
|
||||
class IBTreeFamily(Interface):
|
||||
"""the 64-bit or 32-bit family"""
|
||||
IO = Attribute('The IIntegerObjectBTreeModule for this family')
|
||||
OI = Attribute('The IObjectIntegerBTreeModule for this family')
|
||||
II = Attribute('The IIntegerIntegerBTreeModule for this family')
|
||||
IF = Attribute('The IIntegerFloatBTreeModule for this family')
|
||||
OO = Attribute('The IObjectObjectBTreeModule for this family')
|
||||
maxint = Attribute('The maximum integer storable in this family')
|
||||
minint = Attribute('The minimum integer storable in this family')
|
||||
|
||||
|
||||
class IIntegerObjectBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are integers; values are objects.
|
||||
|
||||
describes IOBTree and LOBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IObjectIntegerBTreeModule(IBTreeModule, IIMerge):
|
||||
"""keys, or set values, are objects; values are integers.
|
||||
|
||||
Object keys (and set values) must sort reliably (for instance, *not* on
|
||||
object id)! Homogenous key types recommended.
|
||||
|
||||
describes OIBTree and LOBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IIntegerIntegerBTreeModule(IBTreeModule, IIMerge, IMergeIntegerKey):
|
||||
"""keys, or set values, are integers; values are also integers.
|
||||
|
||||
describes IIBTree and LLBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IObjectObjectBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are objects; values are also objects.
|
||||
|
||||
Object keys (and set values) must sort reliably (for instance, *not* on
|
||||
object id)! Homogenous key types recommended.
|
||||
|
||||
describes OOBTree"""
|
||||
|
||||
# Note that there's no ``family`` attribute; all families include
|
||||
# the OO flavor of BTrees.
|
||||
|
||||
|
||||
class IIntegerFloatBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are integers; values are floats.
|
||||
|
||||
describes IFBTree and LFBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
try:
|
||||
from ZODB.POSException import BTreesConflictError
|
||||
except ImportError:
|
||||
class BTreesConflictError(ValueError):
|
||||
@property
|
||||
def reason(self):
|
||||
return self.args[-1]
|
||||
|
||||
###############################################################
|
||||
# IMPORTANT NOTE
|
||||
#
|
||||
# Getting the length of a BTree, TreeSet, or output of keys,
|
||||
# values, or items of same is expensive. If you need to get the
|
||||
# length, you need to maintain this separately.
|
||||
#
|
||||
# Eventually, I need to express this through the interfaces.
|
||||
#
|
||||
################################################################
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LFBucket', 'LFSet', 'LFBTree', 'LFTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerFloatBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_float as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LFBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LFBucketPy._mapping_type = LFBucketPy._bucket_type = LFBucketPy
|
||||
LFBucketPy._set_type = LFSetPy
|
||||
|
||||
LFSetPy._mapping_type = LFBucketPy
|
||||
LFSetPy._set_type = LFSetPy._bucket_type = LFSetPy
|
||||
|
||||
LFBTreePy._mapping_type = LFBTreePy._bucket_type = LFBucketPy
|
||||
LFBTreePy._set_type = LFSetPy
|
||||
|
||||
LFTreeSetPy._mapping_type = LFBucketPy
|
||||
LFTreeSetPy._set_type = LFTreeSetPy._bucket_type = LFSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LFSetPy)
|
||||
unionPy = _set_operation(_union, LFSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LFSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LFSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, LFSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, LFSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerFloatBTreeModule)
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LLBucket', 'LLSet', 'LLBTree', 'LLTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_long as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LLBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LLBucketPy._mapping_type = LLBucketPy._bucket_type = LLBucketPy
|
||||
LLBucketPy._set_type = LLSetPy
|
||||
|
||||
LLSetPy._mapping_type = LLBucketPy
|
||||
LLSetPy._set_type = LLSetPy._bucket_type = LLSetPy
|
||||
|
||||
LLBTreePy._mapping_type = LLBTreePy._bucket_type = LLBucketPy
|
||||
LLBTreePy._set_type = LLSetPy
|
||||
|
||||
LLTreeSetPy._mapping_type = LLBucketPy
|
||||
LLTreeSetPy._set_type = LLTreeSetPy._bucket_type = LLSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LLSetPy)
|
||||
unionPy = _set_operation(_union, LLSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LLSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LLSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, LLSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, LLSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerIntegerBTreeModule)
|
||||
@@ -0,0 +1,96 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LOBucket', 'LOSet', 'LOBTree', 'LOTreeSet',
|
||||
'union', 'intersection', 'difference', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE_WEIGHT_default
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_ob as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class LOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class LOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class LOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class LOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LOBucketPy._mapping_type = LOBucketPy._bucket_type = LOBucketPy
|
||||
LOBucketPy._set_type = LOSetPy
|
||||
|
||||
LOSetPy._mapping_type = LOBucketPy
|
||||
LOSetPy._set_type = LOSetPy._bucket_type = LOSetPy
|
||||
|
||||
LOBTreePy._mapping_type = LOBTreePy._bucket_type = LOBucketPy
|
||||
LOBTreePy._set_type = LOSetPy
|
||||
|
||||
LOTreeSetPy._mapping_type = LOBucketPy
|
||||
LOTreeSetPy._set_type = LOTreeSetPy._bucket_type = LOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LOSetPy)
|
||||
unionPy = _set_operation(_union, LOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LOSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,58 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import persistent
|
||||
|
||||
class Length(persistent.Persistent):
|
||||
"""BTree lengths are often too expensive to compute.
|
||||
|
||||
Objects that use BTrees need to keep track of lengths themselves.
|
||||
This class provides an object for doing this.
|
||||
|
||||
As a bonus, the object support application-level conflict
|
||||
resolution.
|
||||
|
||||
It is tempting to to assign length objects to __len__ attributes
|
||||
to provide instance-specific __len__ methods. However, this no
|
||||
longer works as expected, because new-style classes cache
|
||||
class-defined slot methods (like __len__) in C type slots. Thus,
|
||||
instance-defined slot fillers are ignored.
|
||||
"""
|
||||
# class-level default required to keep copy.deepcopy happy -- see
|
||||
# https://bugs.launchpad.net/zodb/+bug/516653
|
||||
value = 0
|
||||
|
||||
def __init__(self, v=0):
|
||||
self.value = v
|
||||
|
||||
def __getstate__(self):
|
||||
return self.value
|
||||
|
||||
def __setstate__(self, v):
|
||||
self.value = v
|
||||
|
||||
def set(self, v):
|
||||
"Set the length value to v."
|
||||
self.value = v
|
||||
|
||||
def _p_resolveConflict(self, old, s1, s2):
|
||||
return s1 + s2 - old
|
||||
|
||||
def change(self, delta):
|
||||
"Add delta to the length value."
|
||||
self.value += delta
|
||||
|
||||
def __call__(self, *args):
|
||||
"Return the current length value."
|
||||
return self.value
|
||||
@@ -0,0 +1,349 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#define MERGETEMPLATE_C "$Id$\n"
|
||||
|
||||
/****************************************************************************
|
||||
Set operations
|
||||
****************************************************************************/
|
||||
|
||||
static int
|
||||
merge_output(Bucket *r, SetIteration *i, int mapping)
|
||||
{
|
||||
if (r->len >= r->size && Bucket_grow(r, -1, !mapping) < 0)
|
||||
return -1;
|
||||
COPY_KEY(r->keys[r->len], i->key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (mapping) {
|
||||
COPY_VALUE(r->values[r->len], i->value);
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The "reason" argument is a little integer giving "a reason" for the
|
||||
* error. In the Zope3 codebase, these are mapped to explanatory strings
|
||||
* via zodb/btrees/interfaces.py.
|
||||
*/
|
||||
static PyObject *
|
||||
merge_error(int p1, int p2, int p3, int reason)
|
||||
{
|
||||
PyObject *r;
|
||||
|
||||
UNLESS (r=Py_BuildValue("iiii", p1, p2, p3, reason)) r=Py_None;
|
||||
if (ConflictError == NULL) {
|
||||
ConflictError = PyExc_ValueError;
|
||||
Py_INCREF(ConflictError);
|
||||
}
|
||||
PyErr_SetObject(ConflictError, r);
|
||||
if (r != Py_None)
|
||||
{
|
||||
Py_DECREF(r);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* It's hard to explain "the rules" for bucket_merge, in large part because
|
||||
* any automatic conflict-resolution scheme is going to be incorrect for
|
||||
* some endcases of *some* app. The scheme here is pretty conservative,
|
||||
* and should be OK for most apps. It's easier to explain what the code
|
||||
* allows than what it forbids:
|
||||
*
|
||||
* Leaving things alone: it's OK if both s2 and s3 leave a piece of s1
|
||||
* alone (don't delete the key, and don't change the value).
|
||||
*
|
||||
* Key deletion: a transaction (s2 or s3) can delete a key (from s1), but
|
||||
* only if the other transaction (of s2 and s3) doesn't delete the same key.
|
||||
* However, it's not OK for s2 and s3 to, between them, end up deleting all
|
||||
* the keys. This is a higher-level constraint, due to that the caller of
|
||||
* bucket_merge() doesn't have enough info to unlink the resulting empty
|
||||
* bucket from its BTree correctly. It's also not OK if s2 or s3 are empty,
|
||||
* because the transaction that emptied the bucket unlinked the bucket from
|
||||
* the tree, and nothing we do here can get it linked back in again.
|
||||
*
|
||||
* Key insertion: s2 or s3 can add a new key, provided the other transaction
|
||||
* doesn't insert the same key. It's not OK even if they insert the same
|
||||
* <key, value> pair.
|
||||
*
|
||||
* Mapping value modification: s2 or s3 can modify the value associated
|
||||
* with a key in s1, provided the other transaction doesn't make a
|
||||
* modification of the same key to a different value. It's OK if s2 and s3
|
||||
* both give the same new value to the key while it's hard to be precise about
|
||||
* why, this doesn't seem consistent with that it's *not* OK for both to add
|
||||
* a new key mapping to the same value).
|
||||
*/
|
||||
static PyObject *
|
||||
bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3)
|
||||
{
|
||||
Bucket *r=0;
|
||||
PyObject *s;
|
||||
SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0};
|
||||
int cmp12, cmp13, cmp23, mapping, set;
|
||||
|
||||
/* If either "after" bucket is empty, punt. */
|
||||
if (s2->len == 0 || s3->len == 0)
|
||||
{
|
||||
merge_error(-1, -1, -1, 12);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (initSetIteration(&i1, OBJECT(s1), 1) < 0)
|
||||
goto err;
|
||||
if (initSetIteration(&i2, OBJECT(s2), 1) < 0)
|
||||
goto err;
|
||||
if (initSetIteration(&i3, OBJECT(s3), 1) < 0)
|
||||
goto err;
|
||||
|
||||
mapping = i1.usesValue | i2.usesValue | i3.usesValue;
|
||||
set = !mapping;
|
||||
|
||||
if (mapping)
|
||||
r = (Bucket *)PyObject_CallObject((PyObject *)&BucketType, NULL);
|
||||
else
|
||||
r = (Bucket *)PyObject_CallObject((PyObject *)&SetType, NULL);
|
||||
if (r == NULL)
|
||||
goto err;
|
||||
|
||||
if (i1.next(&i1) < 0)
|
||||
goto err;
|
||||
if (i2.next(&i2) < 0)
|
||||
goto err;
|
||||
if (i3.next(&i3) < 0)
|
||||
goto err;
|
||||
|
||||
/* Consult zodb/btrees/interfaces.py for the meaning of the last
|
||||
* argument passed to merge_error().
|
||||
*/
|
||||
/* TODO: This isn't passing on errors raised by value comparisons. */
|
||||
while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0)
|
||||
{
|
||||
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
|
||||
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
|
||||
if (cmp12==0)
|
||||
{
|
||||
if (cmp13==0)
|
||||
{
|
||||
if (set || (TEST_VALUE(i1.value, i2.value) == 0))
|
||||
{ /* change in i3 value or all same */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
|
||||
{ /* change in i2 value */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting value changes in i2 and i3 */
|
||||
merge_error(i1.position, i2.position, i3.position, 1);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (cmp13 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i2.value) == 0))
|
||||
{ /* deleted in i3 */
|
||||
if (i3.position == 1)
|
||||
{
|
||||
/* Deleted the first item. This will modify the
|
||||
parent node, so we don't know if merging will be
|
||||
safe
|
||||
*/
|
||||
merge_error(i1.position, i2.position, i3.position, 13);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting del in i3 and change in i2 */
|
||||
merge_error(i1.position, i2.position, i3.position, 2);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else if (cmp13 == 0)
|
||||
{
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
|
||||
{ /* deleted in i2 */
|
||||
if (i2.position == 1)
|
||||
{
|
||||
/* Deleted the first item. This will modify the
|
||||
parent node, so we don't know if merging will be
|
||||
safe
|
||||
*/
|
||||
merge_error(i1.position, i2.position, i3.position, 13);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting del in i2 and change in i3 */
|
||||
merge_error(i1.position, i2.position, i3.position, 3);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* Both keys changed */
|
||||
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
|
||||
if (cmp23==0)
|
||||
{ /* dueling inserts or deletes */
|
||||
merge_error(i1.position, i2.position, i3.position, 4);
|
||||
goto err;
|
||||
}
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (cmp23 > 0)
|
||||
{ /* insert i3 first */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* insert i2 first */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
else if (cmp13 > 0)
|
||||
{ /* Insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* 1<2 and 1<3: both deleted 1.key */
|
||||
merge_error(i1.position, i2.position, i3.position, 5);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (i2.position >= 0 && i3.position >= 0)
|
||||
{ /* New inserts */
|
||||
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
|
||||
if (cmp23==0)
|
||||
{ /* dueling inserts */
|
||||
merge_error(i1.position, i2.position, i3.position, 6);
|
||||
goto err;
|
||||
}
|
||||
if (cmp23 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
|
||||
while (i1.position >= 0 && i2.position >= 0)
|
||||
{ /* remainder of i1 deleted in i3 */
|
||||
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else if (cmp12==0 && (set || (TEST_VALUE(i1.value, i2.value) == 0)))
|
||||
{ /* delete i3 */
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* Dueling deletes or delete and change */
|
||||
merge_error(i1.position, i2.position, i3.position, 7);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
while (i1.position >= 0 && i3.position >= 0)
|
||||
{ /* remainder of i1 deleted in i2 */
|
||||
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
|
||||
if (cmp13 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (cmp13==0 && (set || (TEST_VALUE(i1.value, i3.value) == 0)))
|
||||
{ /* delete i2 */
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* Dueling deletes or delete and change */
|
||||
merge_error(i1.position, i2.position, i3.position, 8);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (i1.position >= 0)
|
||||
{ /* Dueling deletes */
|
||||
merge_error(i1.position, i2.position, i3.position, 9);
|
||||
goto err;
|
||||
}
|
||||
|
||||
while (i2.position >= 0)
|
||||
{ /* Inserting i2 at end */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
|
||||
while (i3.position >= 0)
|
||||
{ /* Inserting i3 at end */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
|
||||
/* If the output bucket is empty, conflict resolution doesn't have
|
||||
* enough info to unlink it from its containing BTree correctly.
|
||||
*/
|
||||
if (r->len == 0)
|
||||
{
|
||||
merge_error(-1, -1, -1, 10);
|
||||
goto err;
|
||||
}
|
||||
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
finiSetIteration(&i3);
|
||||
|
||||
if (s1->next)
|
||||
{
|
||||
Py_INCREF(s1->next);
|
||||
r->next = s1->next;
|
||||
}
|
||||
s = bucket_getstate(r);
|
||||
Py_DECREF(r);
|
||||
|
||||
return s;
|
||||
|
||||
err:
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
finiSetIteration(&i3);
|
||||
Py_XDECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OIBucket', 'OISet', 'OIBTree', 'OITreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
from ._base import to_int as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 250
|
||||
using64bits = True
|
||||
|
||||
class OIBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OISetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OIBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OITreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OITreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OIBucketPy._mapping_type = OIBucketPy._bucket_type = OIBucketPy
|
||||
OIBucketPy._set_type = OISetPy
|
||||
|
||||
OISetPy._mapping_type = OIBucketPy
|
||||
OISetPy._set_type = OISetPy._bucket_type = OISetPy
|
||||
|
||||
OIBTreePy._mapping_type = OIBTreePy._bucket_type = OIBucketPy
|
||||
OIBTreePy._set_type = OISetPy
|
||||
|
||||
OITreeSetPy._mapping_type = OIBucketPy
|
||||
OITreeSetPy._set_type = OITreeSetPy._bucket_type = OISetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OISetPy)
|
||||
unionPy = _set_operation(_union, OISetPy)
|
||||
intersectionPy = _set_operation(_intersection, OISetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, OISetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, OISetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectIntegerBTreeModule)
|
||||
@@ -0,0 +1,111 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OLBucket', 'OLSet', 'OLBTree', 'OLTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
from ._base import to_long as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 250
|
||||
using64bits = True
|
||||
|
||||
|
||||
class OLBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OLBucketPy._mapping_type = OLBucketPy._bucket_type = OLBucketPy
|
||||
OLBucketPy._set_type = OLSetPy
|
||||
|
||||
OLSetPy._mapping_type = OLBucketPy
|
||||
OLSetPy._set_type = OLSetPy._bucket_type = OLSetPy
|
||||
|
||||
OLBTreePy._mapping_type = OLBTreePy._bucket_type = OLBucketPy
|
||||
OLBTreePy._set_type = OLSetPy
|
||||
|
||||
OLTreeSetPy._mapping_type = OLBucketPy
|
||||
OLTreeSetPy._set_type = OLTreeSetPy._bucket_type = OLSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OLSetPy)
|
||||
unionPy = _set_operation(_union, OLSetPy)
|
||||
intersectionPy = _set_operation(_intersection, OLSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, OLSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, OLSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectIntegerBTreeModule)
|
||||
@@ -0,0 +1,91 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OOBucket', 'OOSet', 'OOBTree', 'OOTreeSet',
|
||||
'union', 'intersection','difference',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
_to_value = _to_key
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 30
|
||||
_TREE_SIZE = 250
|
||||
using64bits = False
|
||||
|
||||
|
||||
class OOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class OOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class OOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class OOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class OOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OOBucketPy._mapping_type = OOBucketPy._bucket_type = OOBucketPy
|
||||
OOBucketPy._set_type = OOSetPy
|
||||
|
||||
OOSetPy._mapping_type = OOBucketPy
|
||||
OOSetPy._set_type = OOSetPy._bucket_type = OOSetPy
|
||||
|
||||
OOBTreePy._mapping_type = OOBTreePy._bucket_type = OOBucketPy
|
||||
OOBTreePy._set_type = OOSetPy
|
||||
|
||||
OOTreeSetPy._mapping_type = OOBucketPy
|
||||
OOTreeSetPy._set_type = OOTreeSetPy._bucket_type = OOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OOSetPy)
|
||||
unionPy = _set_operation(_union, OOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, OOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectObjectBTreeModule)
|
||||
@@ -0,0 +1,557 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
Set operations
|
||||
****************************************************************************/
|
||||
|
||||
#define SETOPTEMPLATE_C "$Id$\n"
|
||||
|
||||
#ifdef KEY_CHECK
|
||||
static int
|
||||
nextKeyAsSet(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0) {
|
||||
if (i->position) {
|
||||
DECREF_KEY(i->key);
|
||||
i->position = -1;
|
||||
}
|
||||
else
|
||||
i->position = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* initSetIteration
|
||||
*
|
||||
* Start the set iteration protocol. See the comments at struct SetIteration.
|
||||
*
|
||||
* Arguments
|
||||
* i The address of a SetIteration control struct.
|
||||
* s The address of the set, bucket, BTree, ..., to be iterated.
|
||||
* useValues Boolean; if true, and s has values (is a mapping), copy
|
||||
* them into i->value each time i->next() is called; else
|
||||
* ignore s's values even if s is a mapping.
|
||||
*
|
||||
* Return
|
||||
* 0 on success; -1 and an exception set if error.
|
||||
* i.usesValue is set to 1 (true) if s has values and useValues was
|
||||
* true; else usesValue is set to 0 (false).
|
||||
* i.set gets a new reference to s, or to some other object used to
|
||||
* iterate over s.
|
||||
* i.position is set to 0.
|
||||
* i.next is set to an appropriate iteration function.
|
||||
* i.key and i.value are left alone.
|
||||
*
|
||||
* Internal
|
||||
* i.position < 0 means iteration terminated.
|
||||
* i.position = 0 means iteration hasn't yet begun (next() hasn't
|
||||
* been called yet).
|
||||
* In all other cases, i.key, and possibly i.value, own references.
|
||||
* These must be cleaned up, either by next() routines, or by
|
||||
* finiSetIteration.
|
||||
* next() routines must ensure the above. They should return without
|
||||
* doing anything when i.position < 0.
|
||||
* It's the responsibility of {init, fini}setIteration to clean up
|
||||
* the reference in i.set, and to ensure that no stale references
|
||||
* live in i.key or i.value if iteration terminates abnormally.
|
||||
* A SetIteration struct has been cleaned up iff i.set is NULL.
|
||||
*/
|
||||
static int
|
||||
initSetIteration(SetIteration *i, PyObject *s, int useValues)
|
||||
{
|
||||
i->set = NULL;
|
||||
i->position = -1; /* set to 0 only on normal return */
|
||||
i->usesValue = 0; /* assume it's a set or that values aren't iterated */
|
||||
|
||||
if (PyObject_IsInstance(s, (PyObject *)&BucketType))
|
||||
{
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
|
||||
if (useValues)
|
||||
{
|
||||
i->usesValue = 1;
|
||||
i->next = nextBucket;
|
||||
}
|
||||
else
|
||||
i->next = nextSet;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&SetType))
|
||||
{
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
i->next = nextSet;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&BTreeType))
|
||||
{
|
||||
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'i');
|
||||
UNLESS(i->set) return -1;
|
||||
|
||||
if (useValues)
|
||||
{
|
||||
i->usesValue = 1;
|
||||
i->next = nextBTreeItems;
|
||||
}
|
||||
else
|
||||
i->next = nextTreeSetItems;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&TreeSetType))
|
||||
{
|
||||
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'k');
|
||||
UNLESS(i->set) return -1;
|
||||
i->next = nextTreeSetItems;
|
||||
}
|
||||
#ifdef KEY_CHECK
|
||||
else if (KEY_CHECK(s))
|
||||
{
|
||||
int copied = 1;
|
||||
COPY_KEY_FROM_ARG(i->key, s, copied);
|
||||
UNLESS (copied) return -1;
|
||||
|
||||
INCREF_KEY(i->key);
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
i->next = nextKeyAsSet;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "invalid argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i->position = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef MERGE_WEIGHT
|
||||
#define MERGE_WEIGHT(O, w) (O)
|
||||
#endif
|
||||
|
||||
static int
|
||||
copyRemaining(Bucket *r, SetIteration *i, int merge,
|
||||
|
||||
/* See comment # 42 */
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE w)
|
||||
#else
|
||||
int w)
|
||||
#endif
|
||||
{
|
||||
while (i->position >= 0)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) return -1;
|
||||
COPY_KEY(r->keys[r->len], i->key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i->value, w));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
if (i->next(i) < 0) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the workhorse for all set merge operations: the weighted and
|
||||
* unweighted flavors of union and intersection, and set difference. The
|
||||
* algorithm is conceptually simple but the code is complicated due to all
|
||||
* the options.
|
||||
*
|
||||
* s1, s2
|
||||
* The input collections to be merged.
|
||||
*
|
||||
* usevalues1, usevalues2
|
||||
* Booleans. In the output, should values from s1 (or s2) be used? This
|
||||
* only makes sense when an operation intends to support mapping outputs;
|
||||
* these should both be false for operations that want pure set outputs.
|
||||
*
|
||||
* w1, w2
|
||||
* If usevalues1(2) are true, these are the weights to apply to the
|
||||
* input values.
|
||||
*
|
||||
* c1
|
||||
* Boolean. Should keys that appear in c1 but not c2 appear in the output?
|
||||
* c12
|
||||
* Boolean. Should keys that appear in both inputs appear in the output?
|
||||
* c2
|
||||
* Boolean. Should keys that appear in c2 but not c1 appear in the output?
|
||||
*
|
||||
* Returns NULL if error, else a Set or Bucket, depending on whether a set or
|
||||
* mapping was requested.
|
||||
*/
|
||||
static PyObject *
|
||||
set_operation(PyObject *s1, PyObject *s2,
|
||||
int usevalues1, int usevalues2,
|
||||
|
||||
/* Comment # 42
|
||||
|
||||
The following ifdef works around a template/type problem
|
||||
|
||||
Weights are passed as integers. In particular, the weight passed by
|
||||
difference is one. This works fine in the int value and float value
|
||||
cases but makes no sense in the object value case. In the object
|
||||
value case, we don't do merging, so we don't use the weights, so it
|
||||
doesn't matter what they are.
|
||||
*/
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE w1, VALUE_TYPE w2,
|
||||
#else
|
||||
int w1, int w2,
|
||||
#endif
|
||||
int c1, int c12, int c2)
|
||||
|
||||
|
||||
{
|
||||
Bucket *r=0;
|
||||
SetIteration i1 = {0,0,0}, i2 = {0,0,0};
|
||||
int cmp, merge;
|
||||
|
||||
if (initSetIteration(&i1, s1, usevalues1) < 0) goto err;
|
||||
if (initSetIteration(&i2, s2, usevalues2) < 0) goto err;
|
||||
merge = i1.usesValue | i2.usesValue;
|
||||
|
||||
if (merge)
|
||||
{
|
||||
#ifndef MERGE
|
||||
if (c12 && i1.usesValue && i2.usesValue) goto invalid_set_operation;
|
||||
#endif
|
||||
if (! i1.usesValue&& i2.usesValue)
|
||||
{
|
||||
SetIteration t;
|
||||
int i;
|
||||
|
||||
/* See comment # 42 above */
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE v;
|
||||
#else
|
||||
int v;
|
||||
#endif
|
||||
|
||||
t=i1; i1=i2; i2=t;
|
||||
i=c1; c1=c2; c2=i;
|
||||
v=w1; w1=w2; w2=v;
|
||||
}
|
||||
#ifdef MERGE_DEFAULT
|
||||
i1.value=MERGE_DEFAULT;
|
||||
i2.value=MERGE_DEFAULT;
|
||||
#else
|
||||
if (i1.usesValue)
|
||||
{
|
||||
if (! i2.usesValue && c2) goto invalid_set_operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c1 || c12) goto invalid_set_operation;
|
||||
}
|
||||
#endif
|
||||
|
||||
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&BucketType), NULL)))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL)))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
|
||||
while (i1.position >= 0 && i2.position >= 0)
|
||||
{
|
||||
TEST_KEY_SET_OR(cmp, i1.key, i2.key) goto err;
|
||||
if(cmp < 0)
|
||||
{
|
||||
if(c1)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i1.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i1.value, w1));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
}
|
||||
else if(cmp==0)
|
||||
{
|
||||
if(c12)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i1.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
#ifdef MERGE
|
||||
r->values[r->len] = MERGE(i1.value, w1, i2.value, w2);
|
||||
#else
|
||||
COPY_VALUE(r->values[r->len], i1.value);
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
#endif
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c2)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i2.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i2.value, w2));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
if(c1 && copyRemaining(r, &i1, merge, w1) < 0) goto err;
|
||||
if(c2 && copyRemaining(r, &i2, merge, w2) < 0) goto err;
|
||||
|
||||
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
|
||||
return OBJECT(r);
|
||||
|
||||
#ifndef MERGE_DEFAULT
|
||||
invalid_set_operation:
|
||||
PyErr_SetString(PyExc_TypeError, "invalid set operation");
|
||||
#endif
|
||||
|
||||
err:
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
Py_XDECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
difference_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
|
||||
if (o1 == Py_None || o2 == Py_None)
|
||||
{
|
||||
/* difference(None, X) -> None; difference(X, None) -> X */
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 1, 0, /* preserve values from o1, ignore o2's */
|
||||
1, 0, /* o1's values multiplied by 1 */
|
||||
1, 0, 0); /* take only keys unique to o1 */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
union_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
{
|
||||
Py_INCREF(o2);
|
||||
return o2;
|
||||
}
|
||||
else if (o2 == Py_None)
|
||||
{
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 0, 0, /* ignore values in both */
|
||||
1, 1, /* the weights are irrelevant */
|
||||
1, 1, 1); /* take all keys */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
intersection_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
{
|
||||
Py_INCREF(o2);
|
||||
return o2;
|
||||
}
|
||||
else if (o2 == Py_None)
|
||||
{
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 0, 0, /* ignore values in both */
|
||||
1, 1, /* the weights are irrelevant */
|
||||
0, 1, 0); /* take only keys common to both */
|
||||
}
|
||||
|
||||
#ifdef MERGE
|
||||
|
||||
static PyObject *
|
||||
wunion_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
VALUE_TYPE w1 = 1, w2 = 1;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
|
||||
&o1, &o2, &w1, &w2)
|
||||
) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
|
||||
else if (o2 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
|
||||
|
||||
o1 = set_operation(o1, o2, 1, 1, w1, w2, 1, 1, 1);
|
||||
if (o1)
|
||||
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O", (VALUE_TYPE)1, o1));
|
||||
|
||||
return o1;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
wintersection_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
VALUE_TYPE w1 = 1, w2 = 1;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
|
||||
&o1, &o2, &w1, &w2)
|
||||
) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
|
||||
else if (o2 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
|
||||
|
||||
o1 = set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0);
|
||||
if (o1)
|
||||
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O",
|
||||
((o1->ob_type == (PyTypeObject*)(&SetType)) ? w2+w1 : 1),
|
||||
o1));
|
||||
|
||||
return o1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MULTI_INT_UNION
|
||||
#include "sorters.c"
|
||||
|
||||
/* Input is a sequence of integer sets (or convertible to sets by the
|
||||
set iteration protocol). Output is the union of the sets. The point
|
||||
is to run much faster than doing pairs of unions.
|
||||
*/
|
||||
static PyObject *
|
||||
multiunion_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *seq; /* input sequence */
|
||||
int n; /* length of input sequence */
|
||||
PyObject *set = NULL; /* an element of the input sequence */
|
||||
Bucket *result; /* result set */
|
||||
SetIteration setiter = {0};
|
||||
int i;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "O", &seq))
|
||||
return NULL;
|
||||
|
||||
n = PyObject_Length(seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
/* Construct an empty result set. */
|
||||
result = BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL));
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
/* For each set in the input sequence, append its elements to the result
|
||||
set. At this point, we ignore the possibility of duplicates. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
set = PySequence_GetItem(seq, i);
|
||||
if (set == NULL)
|
||||
goto Error;
|
||||
|
||||
/* If set is a bucket, do a straight resize + memcpy. */
|
||||
if (set->ob_type == (PyTypeObject*)&SetType ||
|
||||
set->ob_type == (PyTypeObject*)&BucketType)
|
||||
{
|
||||
Bucket *b = BUCKET(set);
|
||||
int status = 0;
|
||||
|
||||
UNLESS (PER_USE(b)) goto Error;
|
||||
if (b->len)
|
||||
status = bucket_append(result, b, 0, b->len, 0, i < n-1);
|
||||
PER_UNUSE(b);
|
||||
if (status < 0) goto Error;
|
||||
}
|
||||
else {
|
||||
/* No cheap way: iterate over set's elements one at a time. */
|
||||
if (initSetIteration(&setiter, set, 0) < 0) goto Error;
|
||||
if (setiter.next(&setiter) < 0) goto Error;
|
||||
while (setiter.position >= 0) {
|
||||
if (result->len >= result->size && Bucket_grow(result, -1, 1) < 0)
|
||||
goto Error;
|
||||
COPY_KEY(result->keys[result->len], setiter.key);
|
||||
++result->len;
|
||||
/* We know the key is an int, so no need to incref it. */
|
||||
if (setiter.next(&setiter) < 0) goto Error;
|
||||
}
|
||||
finiSetIteration(&setiter);
|
||||
}
|
||||
Py_DECREF(set);
|
||||
set = NULL;
|
||||
}
|
||||
|
||||
/* Combine, sort, remove duplicates, and reset the result's len.
|
||||
If the set shrinks (which happens if and only if there are
|
||||
duplicates), no point to realloc'ing the set smaller, as we
|
||||
expect the result set to be short-lived.
|
||||
*/
|
||||
if (result->len > 0) {
|
||||
size_t newlen; /* number of elements in final result set */
|
||||
newlen = sort_int_nodups(result->keys, (size_t)result->len);
|
||||
result->len = (int)newlen;
|
||||
}
|
||||
return (PyObject *)result;
|
||||
|
||||
Error:
|
||||
Py_DECREF(result);
|
||||
Py_XDECREF(set);
|
||||
finiSetIteration(&setiter);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
#include "_compat.h"
|
||||
|
||||
#define SETTEMPLATE_C "$Id$\n"
|
||||
|
||||
static PyObject *
|
||||
Set_insert(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
int i;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if ( (i=_bucket_set(self, key, Py_None, 1, 1, 0)) < 0)
|
||||
return NULL;
|
||||
return INT_FROM_LONG(i);
|
||||
}
|
||||
|
||||
/* _Set_update and _TreeSet_update are identical except for the
|
||||
function they call to add the element to the set.
|
||||
*/
|
||||
|
||||
static int
|
||||
_Set_update(Bucket *self, PyObject *seq)
|
||||
{
|
||||
int n=0, ind=0;
|
||||
PyObject *iter, *v;
|
||||
|
||||
iter = PyObject_GetIter(seq);
|
||||
if (iter == NULL)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
v = PyIter_Next(iter);
|
||||
if (v == NULL) {
|
||||
if (PyErr_Occurred())
|
||||
goto err;
|
||||
else
|
||||
break;
|
||||
}
|
||||
ind = _bucket_set(self, v, Py_None, 1, 1, 0);
|
||||
Py_DECREF(v);
|
||||
if (ind < 0)
|
||||
goto err;
|
||||
else
|
||||
n += ind;
|
||||
}
|
||||
|
||||
err:
|
||||
Py_DECREF(iter);
|
||||
if (ind < 0)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Set_update(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *seq = NULL;
|
||||
int n = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:update", &seq))
|
||||
return NULL;
|
||||
|
||||
if (seq) {
|
||||
n = _Set_update(self, seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return INT_FROM_LONG(n);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Set_remove(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if (_bucket_set(self, key, NULL, 0, 1, 0) < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static int
|
||||
_set_setstate(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *k, *items;
|
||||
Bucket *next=0;
|
||||
int i, l, copied=1;
|
||||
KEY_TYPE *keys;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O|O", &items, &next))
|
||||
return -1;
|
||||
|
||||
if (!PyTuple_Check(items)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"tuple required for first state element");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((l=PyTuple_Size(items)) < 0)
|
||||
return -1;
|
||||
|
||||
for (i=self->len; --i >= 0; )
|
||||
{
|
||||
DECREF_KEY(self->keys[i]);
|
||||
}
|
||||
self->len=0;
|
||||
|
||||
if (self->next)
|
||||
{
|
||||
Py_DECREF(self->next);
|
||||
self->next=0;
|
||||
}
|
||||
|
||||
if (l > self->size)
|
||||
{
|
||||
UNLESS (keys=BTree_Realloc(self->keys, sizeof(KEY_TYPE)*l))
|
||||
return -1;
|
||||
self->keys=keys;
|
||||
self->size=l;
|
||||
}
|
||||
|
||||
for (i=0; i<l; i++)
|
||||
{
|
||||
k=PyTuple_GET_ITEM(items, i);
|
||||
COPY_KEY_FROM_ARG(self->keys[i], k, copied);
|
||||
UNLESS (copied)
|
||||
return -1;
|
||||
INCREF_KEY(self->keys[i]);
|
||||
}
|
||||
|
||||
self->len=l;
|
||||
|
||||
if (next)
|
||||
{
|
||||
self->next=next;
|
||||
Py_INCREF(next);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
set_setstate(Bucket *self, PyObject *args)
|
||||
{
|
||||
int r;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &args))
|
||||
return NULL;
|
||||
|
||||
PER_PREVENT_DEACTIVATION(self);
|
||||
r=_set_setstate(self, args);
|
||||
PER_UNUSE(self);
|
||||
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static struct PyMethodDef Set_methods[] = {
|
||||
{"__getstate__", (PyCFunction) bucket_getstate, METH_VARARGS,
|
||||
"__getstate__() -- Return the picklable state of the object"},
|
||||
|
||||
{"__setstate__", (PyCFunction) set_setstate, METH_VARARGS,
|
||||
"__setstate__() -- Set the state of the object"},
|
||||
|
||||
{"keys", (PyCFunction) bucket_keys, METH_VARARGS | METH_KEYWORDS,
|
||||
"keys() -- Return the keys"},
|
||||
|
||||
{"has_key", (PyCFunction) bucket_has_key, METH_O,
|
||||
"has_key(key) -- Test whether the bucket contains the given key"},
|
||||
|
||||
{"clear", (PyCFunction) bucket_clear, METH_VARARGS,
|
||||
"clear() -- Remove all of the items from the bucket"},
|
||||
|
||||
{"maxKey", (PyCFunction) Bucket_maxKey, METH_VARARGS,
|
||||
"maxKey([key]) -- Find the maximum key\n\n"
|
||||
"If an argument is given, find the maximum <= the argument"},
|
||||
|
||||
{"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS,
|
||||
"minKey([key]) -- Find the minimum key\n\n"
|
||||
"If an argument is given, find the minimum >= the argument"},
|
||||
|
||||
#ifdef PERSISTENT
|
||||
{"_p_resolveConflict",
|
||||
(PyCFunction) bucket__p_resolveConflict, METH_VARARGS,
|
||||
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
|
||||
|
||||
{"_p_deactivate",
|
||||
(PyCFunction) bucket__p_deactivate, METH_VARARGS | METH_KEYWORDS,
|
||||
"_p_deactivate() -- Reinitialize from a newly created copy"},
|
||||
#endif
|
||||
|
||||
{"add", (PyCFunction)Set_insert, METH_VARARGS,
|
||||
"add(id) -- Add a key to the set"},
|
||||
|
||||
{"insert", (PyCFunction)Set_insert, METH_VARARGS,
|
||||
"insert(id) -- Add a key to the set"},
|
||||
|
||||
{"update", (PyCFunction)Set_update, METH_VARARGS,
|
||||
"update(seq) -- Add the items from the given sequence to the set"},
|
||||
|
||||
{"remove", (PyCFunction)Set_remove, METH_VARARGS,
|
||||
"remove(id) -- Remove an id from the set"},
|
||||
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int
|
||||
Set_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *v = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "Set", &v))
|
||||
return -1;
|
||||
|
||||
if (v)
|
||||
return _Set_update((Bucket *)self, v);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyObject *
|
||||
set_repr(Bucket *self)
|
||||
{
|
||||
static PyObject *format;
|
||||
PyObject *r, *t;
|
||||
|
||||
if (!format)
|
||||
format = TEXT_FROM_STRING(MOD_NAME_PREFIX "Set(%s)");
|
||||
UNLESS (t = PyTuple_New(1))
|
||||
return NULL;
|
||||
UNLESS (r = bucket_keys(self, NULL, NULL))
|
||||
goto err;
|
||||
PyTuple_SET_ITEM(t, 0, r);
|
||||
r = t;
|
||||
ASSIGN(r, TEXT_FORMAT(format, r));
|
||||
return r;
|
||||
err:
|
||||
Py_DECREF(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
set_length(Bucket *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
PER_USE_OR_RETURN(self, -1);
|
||||
r = self->len;
|
||||
PER_UNUSE(self);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
set_item(Bucket *self, Py_ssize_t index)
|
||||
{
|
||||
PyObject *r=0;
|
||||
|
||||
PER_USE_OR_RETURN(self, NULL);
|
||||
if (index >= 0 && index < self->len)
|
||||
{
|
||||
COPY_KEY_TO_OBJECT(r, self->keys[index]);
|
||||
}
|
||||
else
|
||||
IndexError(index);
|
||||
|
||||
PER_UNUSE(self);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static PySequenceMethods set_as_sequence = {
|
||||
(lenfunc)set_length, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc)set_item, /* sq_item */
|
||||
(ssizessizeargfunc)0, /* sq_slice */
|
||||
(ssizeobjargproc)0, /* sq_ass_item */
|
||||
(ssizessizeobjargproc)0, /* sq_ass_slice */
|
||||
(objobjproc)bucket_contains, /* sq_contains */
|
||||
0, /* sq_inplace_concat */
|
||||
0, /* sq_inplace_repeat */
|
||||
};
|
||||
|
||||
static PyTypeObject SetType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0) /* PyPersist_Type */
|
||||
MODULE_NAME MOD_NAME_PREFIX "Set", /* tp_name */
|
||||
sizeof(Bucket), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)bucket_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
(reprfunc)set_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
&set_as_sequence, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT |
|
||||
Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
(traverseproc)bucket_traverse, /* tp_traverse */
|
||||
(inquiry)bucket_tp_clear, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)Bucket_getiter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
Set_methods, /* tp_methods */
|
||||
Bucket_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
Set_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /*PyType_GenericNew,*/ /* tp_new */
|
||||
};
|
||||
|
||||
static int
|
||||
nextSet(SetIteration *i)
|
||||
{
|
||||
|
||||
if (i->position >= 0)
|
||||
{
|
||||
UNLESS(PER_USE(BUCKET(i->set)))
|
||||
return -1;
|
||||
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
}
|
||||
|
||||
if (i->position < BUCKET(i->set)->len)
|
||||
{
|
||||
COPY_KEY(i->key, BUCKET(i->set)->keys[i->position]);
|
||||
INCREF_KEY(i->key);
|
||||
i->position ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PER_ACCESSED(BUCKET(i->set));
|
||||
}
|
||||
|
||||
PER_ALLOW_DEACTIVATION(BUCKET(i->set));
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
#include "_compat.h"
|
||||
|
||||
#define TREESETTEMPLATE_C "$Id$\n"
|
||||
|
||||
static PyObject *
|
||||
TreeSet_insert(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
int i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O:insert", &key))
|
||||
return NULL;
|
||||
i = _BTree_set(self, key, Py_None, 1, 1);
|
||||
if (i < 0)
|
||||
return NULL;
|
||||
return INT_FROM_LONG(i);
|
||||
}
|
||||
|
||||
/* _Set_update and _TreeSet_update are identical except for the
|
||||
function they call to add the element to the set.
|
||||
*/
|
||||
|
||||
static int
|
||||
_TreeSet_update(BTree *self, PyObject *seq)
|
||||
{
|
||||
int n=0, ind=0;
|
||||
PyObject *iter, *v;
|
||||
|
||||
iter = PyObject_GetIter(seq);
|
||||
if (iter == NULL)
|
||||
return -1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
v = PyIter_Next(iter);
|
||||
if (v == NULL)
|
||||
{
|
||||
if (PyErr_Occurred())
|
||||
goto err;
|
||||
else
|
||||
break;
|
||||
}
|
||||
ind = _BTree_set(self, v, Py_None, 1, 1);
|
||||
Py_DECREF(v);
|
||||
if (ind < 0)
|
||||
goto err;
|
||||
else
|
||||
n += ind;
|
||||
}
|
||||
|
||||
err:
|
||||
Py_DECREF(iter);
|
||||
if (ind < 0)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
TreeSet_update(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *seq = NULL;
|
||||
int n = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:update", &seq))
|
||||
return NULL;
|
||||
|
||||
if (seq)
|
||||
{
|
||||
n = _TreeSet_update(self, seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return INT_FROM_LONG(n);
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
TreeSet_remove(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if (_BTree_set(self, key, NULL, 0, 1) < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
TreeSet_setstate(BTree *self, PyObject *args)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"O",&args))
|
||||
return NULL;
|
||||
|
||||
PER_PREVENT_DEACTIVATION(self);
|
||||
r=_BTree_setstate(self, args, 1);
|
||||
PER_UNUSE(self);
|
||||
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static struct PyMethodDef TreeSet_methods[] =
|
||||
{
|
||||
{"__getstate__", (PyCFunction) BTree_getstate, METH_NOARGS,
|
||||
"__getstate__() -> state\n\n"
|
||||
"Return the picklable state of the TreeSet."},
|
||||
|
||||
{"__setstate__", (PyCFunction) TreeSet_setstate, METH_VARARGS,
|
||||
"__setstate__(state)\n\n"
|
||||
"Set the state of the TreeSet."},
|
||||
|
||||
{"has_key", (PyCFunction) BTree_has_key, METH_O,
|
||||
"has_key(key)\n\n"
|
||||
"Return true if the TreeSet contains the given key."},
|
||||
|
||||
{"keys", (PyCFunction) BTree_keys, METH_VARARGS | METH_KEYWORDS,
|
||||
"keys([min, max]) -> list of keys\n\n"
|
||||
"Returns the keys of the TreeSet. If min and max are supplied, only\n"
|
||||
"keys greater than min and less than max are returned."},
|
||||
|
||||
{"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS,
|
||||
"maxKey([max]) -> key\n\n"
|
||||
"Return the largest key in the BTree. If max is specified, return\n"
|
||||
"the largest key <= max."},
|
||||
|
||||
{"minKey", (PyCFunction) BTree_minKey, METH_VARARGS,
|
||||
"minKey([mi]) -> key\n\n"
|
||||
"Return the smallest key in the BTree. If min is specified, return\n"
|
||||
"the smallest key >= min."},
|
||||
|
||||
{"clear", (PyCFunction) BTree_clear, METH_NOARGS,
|
||||
"clear()\n\nRemove all of the items from the BTree."},
|
||||
|
||||
{"add", (PyCFunction)TreeSet_insert, METH_VARARGS,
|
||||
"add(id) -- Add an item to the set"},
|
||||
|
||||
{"insert", (PyCFunction)TreeSet_insert, METH_VARARGS,
|
||||
"insert(id) -- Add an item to the set"},
|
||||
|
||||
{"update", (PyCFunction)TreeSet_update, METH_VARARGS,
|
||||
"update(collection)\n\n Add the items from the given collection."},
|
||||
|
||||
{"remove", (PyCFunction)TreeSet_remove, METH_VARARGS,
|
||||
"remove(id) -- Remove a key from the set"},
|
||||
|
||||
{"_check", (PyCFunction) BTree_check, METH_NOARGS,
|
||||
"Perform sanity check on TreeSet, and raise exception if flawed."},
|
||||
|
||||
#ifdef PERSISTENT
|
||||
{"_p_resolveConflict",
|
||||
(PyCFunction) BTree__p_resolveConflict, METH_VARARGS,
|
||||
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
|
||||
|
||||
{"_p_deactivate",
|
||||
(PyCFunction) BTree__p_deactivate, METH_VARARGS | METH_KEYWORDS,
|
||||
"_p_deactivate()\n\nReinitialize from a newly created copy."},
|
||||
#endif
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyMappingMethods TreeSet_as_mapping = {
|
||||
(lenfunc)BTree_length, /*mp_length*/
|
||||
};
|
||||
|
||||
static PySequenceMethods TreeSet_as_sequence = {
|
||||
(lenfunc)0, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc)0, /* sq_item */
|
||||
(ssizessizeargfunc)0, /* sq_slice */
|
||||
(ssizeobjargproc)0, /* sq_ass_item */
|
||||
(ssizessizeobjargproc)0, /* sq_ass_slice */
|
||||
(objobjproc)BTree_contains, /* sq_contains */
|
||||
0, /* sq_inplace_concat */
|
||||
0, /* sq_inplace_repeat */
|
||||
};
|
||||
|
||||
static int
|
||||
TreeSet_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *v = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "TreeSet", &v))
|
||||
return -1;
|
||||
|
||||
if (v)
|
||||
return _TreeSet_update((BTree *)self, v);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyTypeObject TreeSetType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MODULE_NAME MOD_NAME_PREFIX "TreeSet", /* tp_name */
|
||||
sizeof(BTree), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)BTree_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
&BTree_as_number_for_nonzero, /* tp_as_number */
|
||||
&TreeSet_as_sequence, /* tp_as_sequence */
|
||||
&TreeSet_as_mapping, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT |
|
||||
Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
(traverseproc)BTree_traverse, /* tp_traverse */
|
||||
(inquiry)BTree_tp_clear, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)BTree_getiter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
TreeSet_methods, /* tp_methods */
|
||||
BTree_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
TreeSet_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /*PyType_GenericNew,*/ /* tp_new */
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IFBTree - int key, float value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and float type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "IF"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "floatvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IFBTree
|
||||
#else
|
||||
#define INITMODULE init_IFBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IIBTree - int key, int value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "II"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IIBTree
|
||||
#else
|
||||
#define INITMODULE init_IIBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IOBTree - int key, object value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "IO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IOBTree
|
||||
#else
|
||||
#define INITMODULE init_IOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IFBTree.c 67074 2006-04-17 19:13:39Z fdrake $\n"
|
||||
|
||||
/* IFBTree - int key, float value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and float type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LF"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "floatvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LFBTree
|
||||
#else
|
||||
#define INITMODULE init_LFBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* IIBTree - int key, int value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LL"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LLBTree
|
||||
#else
|
||||
#define INITMODULE init_LLBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IOBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* IOBTree - int key, object value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LOBTree
|
||||
#else
|
||||
#define INITMODULE init_LOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* OIBTree - object key, int value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OI"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OIBTree
|
||||
#else
|
||||
#define INITMODULE init_OIBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _OIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* OIBTree - object key, int value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OL"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OLBTree
|
||||
#else
|
||||
#define INITMODULE init_OLBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* OOBTree - object key, object value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 30
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OOBTree
|
||||
#else
|
||||
#define INITMODULE init_OOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
#############################################################################
|
||||
#
|
||||
# Copyright (c) 2007 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
import zope.interface
|
||||
import BTrees.Interfaces
|
||||
|
||||
|
||||
@zope.interface.implementer(BTrees.Interfaces.IBTreeFamily)
|
||||
class _Family(object):
|
||||
|
||||
from BTrees import OOBTree as OO
|
||||
|
||||
class _Family32(_Family):
|
||||
from BTrees import OIBTree as OI
|
||||
from BTrees import IIBTree as II
|
||||
from BTrees import IOBTree as IO
|
||||
from BTrees import IFBTree as IF
|
||||
|
||||
maxint = int(2**31-1)
|
||||
minint = -maxint - 1
|
||||
|
||||
def __reduce__(self):
|
||||
return _family32, ()
|
||||
|
||||
class _Family64(_Family):
|
||||
from BTrees import OLBTree as OI
|
||||
from BTrees import LLBTree as II
|
||||
from BTrees import LOBTree as IO
|
||||
from BTrees import LFBTree as IF
|
||||
|
||||
maxint = 2**63-1
|
||||
minint = -maxint - 1
|
||||
|
||||
def __reduce__(self):
|
||||
return _family64, ()
|
||||
|
||||
def _family32():
|
||||
return family32
|
||||
_family32.__safe_for_unpickling__ = True
|
||||
|
||||
def _family64():
|
||||
return family64
|
||||
_family64.__safe_for_unpickling__ = True
|
||||
|
||||
|
||||
family32 = _Family32()
|
||||
family64 = _Family64()
|
||||
|
||||
|
||||
BTrees.family64.IO.family = family64
|
||||
BTrees.family64.OI.family = family64
|
||||
BTrees.family64.IF.family = family64
|
||||
BTrees.family64.II.family = family64
|
||||
|
||||
BTrees.family32.IO.family = family32
|
||||
BTrees.family32.OI.family = family32
|
||||
BTrees.family32.IF.family = family32
|
||||
BTrees.family32.II.family = family32
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
/* Straddle Python 2 / 3 */
|
||||
#ifndef BTREES__COMPAT_H
|
||||
#define BTREES__COMPAT_H
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
#ifdef INTERN
|
||||
#undef INTERN
|
||||
#endif
|
||||
|
||||
#ifdef INT_FROM_LONG
|
||||
#undef INT_FROM_LONG
|
||||
#endif
|
||||
|
||||
#ifdef INT_CHECK
|
||||
#undef INT_CHECK
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
#define PY3K
|
||||
|
||||
#define INTERN PyUnicode_InternFromString
|
||||
#define INT_FROM_LONG(x) PyLong_FromLong(x)
|
||||
#define INT_CHECK(x) PyLong_Check(x)
|
||||
#define INT_AS_LONG(x) PyLong_AS_LONG(x)
|
||||
#define TEXT_FROM_STRING PyUnicode_FromString
|
||||
#define TEXT_FORMAT PyUnicode_Format
|
||||
|
||||
/* Note that the second comparison is skipped if the first comparison returns:
|
||||
|
||||
1 -> There was no error and the answer is -1
|
||||
-1 -> There was an error, which the caller will detect with PyError_Occurred.
|
||||
*/
|
||||
#define COMPARE(lhs, rhs) \
|
||||
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
|
||||
(PyObject_RichCompareBool((lhs), (rhs), Py_LT) != 0 ? -1 : \
|
||||
(PyObject_RichCompareBool((lhs), (rhs), Py_EQ) > 0 ? 0 : 1))))
|
||||
|
||||
#else
|
||||
|
||||
#define INTERN PyString_InternFromString
|
||||
#define INT_FROM_LONG(x) PyInt_FromLong(x)
|
||||
#define INT_CHECK(x) PyInt_Check(x)
|
||||
#define INT_AS_LONG(x) PyInt_AS_LONG(x)
|
||||
#define TEXT_FROM_STRING PyString_FromString
|
||||
#define TEXT_FORMAT PyString_Format
|
||||
|
||||
#define COMPARE(lhs, rhs) \
|
||||
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
|
||||
PyObject_Compare((lhs), (rhs))))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* BTREES__COMPAT_H */
|
||||
@@ -0,0 +1,100 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import os
|
||||
import sys
|
||||
|
||||
PYPY = hasattr(sys, 'pypy_version_info')
|
||||
# We can and do build the C extensions on PyPy, but
|
||||
# as of Persistent 4.2.5 the persistent C extension is not
|
||||
# built on PyPy, so importing our C extension will fail anyway.
|
||||
PURE_PYTHON = os.environ.get('PURE_PYTHON', PYPY)
|
||||
|
||||
|
||||
if sys.version_info[0] < 3: #pragma NO COVER Python2
|
||||
|
||||
PY2 = True
|
||||
PY3 = False
|
||||
|
||||
int_types = int, long
|
||||
xrange = xrange
|
||||
def compare(x, y):
|
||||
if x is None:
|
||||
if y is None:
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
elif y is None:
|
||||
return 1
|
||||
else:
|
||||
return cmp(x, y)
|
||||
|
||||
_bytes = str
|
||||
def _ascii(x):
|
||||
return bytes(x)
|
||||
|
||||
else: #pragma NO COVER Python3
|
||||
|
||||
PY2 = False
|
||||
PY3 = True
|
||||
|
||||
int_types = int,
|
||||
xrange = range
|
||||
|
||||
def compare(x, y):
|
||||
if x is None:
|
||||
if y is None:
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
elif y is None:
|
||||
return 1
|
||||
else:
|
||||
return (x > y) - (y > x)
|
||||
|
||||
_bytes = bytes
|
||||
def _ascii(x):
|
||||
return bytes(x, 'ascii')
|
||||
|
||||
|
||||
def import_c_extension(mod_globals):
|
||||
import importlib
|
||||
c_module = None
|
||||
module_name = mod_globals['__name__']
|
||||
assert module_name.startswith('BTrees.')
|
||||
module_name = module_name.split('.')[1]
|
||||
if not PURE_PYTHON:
|
||||
try:
|
||||
c_module = importlib.import_module('BTrees._' + module_name)
|
||||
except ImportError:
|
||||
pass
|
||||
if c_module is not None:
|
||||
new_values = dict(c_module.__dict__)
|
||||
new_values.pop("__name__", None)
|
||||
new_values.pop('__file__', None)
|
||||
new_values.pop('__doc__', None)
|
||||
mod_globals.update(new_values)
|
||||
else:
|
||||
# No C extension, make the Py versions available without that
|
||||
# extension. The list comprehension both filters and prevents
|
||||
# concurrent modification errors.
|
||||
for py in [k for k in mod_globals if k.endswith('Py')]:
|
||||
mod_globals[py[:-2]] = mod_globals[py]
|
||||
|
||||
# Assign the global aliases
|
||||
prefix = module_name[:2]
|
||||
for name in ('Bucket', 'Set', 'BTree', 'TreeSet'):
|
||||
mod_globals[name] = mod_globals[prefix + name]
|
||||
|
||||
# Cleanup
|
||||
del mod_globals['import_c_extension']
|
||||
@@ -0,0 +1,164 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* fsBTree - FileStorage index BTree
|
||||
|
||||
This BTree implements a mapping from 2-character strings
|
||||
to six-character strings. This allows us to efficiently store
|
||||
a FileStorage index as a nested mapping of 6-character oid prefix
|
||||
to mapping of 2-character oid suffix to 6-character (byte) file
|
||||
positions.
|
||||
*/
|
||||
|
||||
typedef unsigned char char2[2];
|
||||
typedef unsigned char char6[6];
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "fs"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 500
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
/*#include "intkeymacros.h"*/
|
||||
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
#define KEY_TYPE char2
|
||||
#undef KEY_TYPE_IS_PYOBJECT
|
||||
#define KEY_CHECK(K) (PyBytes_Check(K) && PyBytes_GET_SIZE(K)==2)
|
||||
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = ((*(K) < *(T) || (*(K) == *(T) && (K)[1] < (T)[1])) ? -1 : ((*(K) == *(T) && (K)[1] == (T)[1]) ? 0 : 1)) ), 0 )
|
||||
#define DECREF_KEY(KEY)
|
||||
#define INCREF_KEY(k)
|
||||
#define COPY_KEY(KEY, E) (*(KEY)=*(E), (KEY)[1]=(E)[1])
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,2)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (KEY_CHECK(ARG)) memcpy(TARGET, PyBytes_AS_STRING(ARG), 2); else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected two-character string key"); \
|
||||
(STATUS)=0; }
|
||||
|
||||
/*#include "intvaluemacros.h"*/
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
#define VALUE_TYPE char6
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) memcmp(K,T,6)
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (memcpy(V, E, 6))
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,6)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if ((PyBytes_Check(ARG) && PyBytes_GET_SIZE(ARG)==6)) \
|
||||
memcpy(TARGET, PyBytes_AS_STRING(ARG), 6); else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected six-character string key"); \
|
||||
(STATUS)=0; }
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN)
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
static PyObject *bucket_toBytes(PyObject *self);
|
||||
|
||||
static PyObject *bucket_fromBytes(PyObject *self, PyObject *state);
|
||||
|
||||
#define EXTRA_BUCKET_METHODS \
|
||||
{"toBytes", (PyCFunction) bucket_toBytes, METH_NOARGS, \
|
||||
"toBytes() -- Return the state as a bytes array"}, \
|
||||
{"fromBytes", (PyCFunction) bucket_fromBytes, METH_O, \
|
||||
"fromSBytes(s) -- Set the state of the object from a bytes array"}, \
|
||||
{"toString", (PyCFunction) bucket_toBytes, METH_NOARGS, \
|
||||
"toString() -- Deprecated alias for 'toBytes'"}, \
|
||||
{"fromString", (PyCFunction) bucket_fromBytes, METH_O, \
|
||||
"fromString(s) -- Deprecated alias for 'fromBytes'"}, \
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__fsBTree
|
||||
#else
|
||||
#define INITMODULE init_fsBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
|
||||
static PyObject *
|
||||
bucket_toBytes(PyObject *oself)
|
||||
{
|
||||
Bucket *self = (Bucket *)oself;
|
||||
PyObject *items = NULL;
|
||||
int len;
|
||||
|
||||
PER_USE_OR_RETURN(self, NULL);
|
||||
|
||||
len = self->len;
|
||||
|
||||
items = PyBytes_FromStringAndSize(NULL, len*8);
|
||||
if (items == NULL)
|
||||
goto err;
|
||||
memcpy(PyBytes_AS_STRING(items), self->keys, len*2);
|
||||
memcpy(PyBytes_AS_STRING(items)+len*2, self->values, len*6);
|
||||
|
||||
PER_UNUSE(self);
|
||||
return items;
|
||||
|
||||
err:
|
||||
PER_UNUSE(self);
|
||||
Py_XDECREF(items);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
bucket_fromBytes(PyObject *oself, PyObject *state)
|
||||
{
|
||||
Bucket *self = (Bucket *)oself;
|
||||
int len;
|
||||
KEY_TYPE *keys;
|
||||
VALUE_TYPE *values;
|
||||
|
||||
len = PyBytes_Size(state);
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
|
||||
if (len%8)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "state string of wrong size");
|
||||
return NULL;
|
||||
}
|
||||
len /= 8;
|
||||
|
||||
if (self->next) {
|
||||
Py_DECREF(self->next);
|
||||
self->next = NULL;
|
||||
}
|
||||
|
||||
if (len > self->size) {
|
||||
keys = BTree_Realloc(self->keys, sizeof(KEY_TYPE)*len);
|
||||
if (keys == NULL)
|
||||
return NULL;
|
||||
values = BTree_Realloc(self->values, sizeof(VALUE_TYPE)*len);
|
||||
if (values == NULL)
|
||||
return NULL;
|
||||
self->keys = keys;
|
||||
self->values = values;
|
||||
self->size = len;
|
||||
}
|
||||
|
||||
memcpy(self->keys, PyBytes_AS_STRING(state), len*2);
|
||||
memcpy(self->values, PyBytes_AS_STRING(state)+len*2, len*6);
|
||||
|
||||
self->len = len;
|
||||
|
||||
Py_INCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,428 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Utilities for working with BTrees (TreeSets, Buckets, and Sets) at a low
|
||||
level.
|
||||
|
||||
The primary function is check(btree), which performs value-based consistency
|
||||
checks of a kind btree._check() does not perform. See the function docstring
|
||||
for details.
|
||||
|
||||
display(btree) displays the internal structure of a BTree (TreeSet, etc) to
|
||||
stdout.
|
||||
|
||||
CAUTION: When a BTree node has only a single bucket child, it can be
|
||||
impossible to get at the bucket from Python code (__getstate__() may squash
|
||||
the bucket object out of existence, as a pickling storage optimization). In
|
||||
such a case, the code here synthesizes a temporary bucket with the same keys
|
||||
(and values, if the bucket is of a mapping type). This has no first-order
|
||||
consequences, but can mislead if you pay close attention to reported object
|
||||
addresses and/or object identity (the synthesized bucket has an address
|
||||
that doesn't exist in the actual BTree).
|
||||
"""
|
||||
|
||||
from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet
|
||||
from BTrees.IFBTree import IFBTreePy, IFBucketPy, IFSetPy, IFTreeSetPy
|
||||
from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
|
||||
from BTrees.IIBTree import IIBTreePy, IIBucketPy, IISetPy, IITreeSetPy
|
||||
from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
|
||||
from BTrees.IOBTree import IOBTreePy, IOBucketPy, IOSetPy, IOTreeSetPy
|
||||
from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet
|
||||
from BTrees.LFBTree import LFBTreePy, LFBucketPy, LFSetPy, LFTreeSetPy
|
||||
from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet
|
||||
from BTrees.LLBTree import LLBTreePy, LLBucketPy, LLSetPy, LLTreeSetPy
|
||||
from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet
|
||||
from BTrees.LOBTree import LOBTreePy, LOBucketPy, LOSetPy, LOTreeSetPy
|
||||
from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
|
||||
from BTrees.OIBTree import OIBTreePy, OIBucketPy, OISetPy, OITreeSetPy
|
||||
from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet
|
||||
from BTrees.OLBTree import OLBTreePy, OLBucketPy, OLSetPy, OLTreeSetPy
|
||||
from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet
|
||||
from BTrees.OOBTree import OOBTreePy, OOBucketPy, OOSetPy, OOTreeSetPy
|
||||
|
||||
from BTrees.utils import positive_id
|
||||
from BTrees.utils import oid_repr
|
||||
|
||||
TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)
|
||||
|
||||
from ._compat import compare
|
||||
|
||||
_type2kind = {}
|
||||
for kv in ('OO',
|
||||
'II', 'IO', 'OI', 'IF',
|
||||
'LL', 'LO', 'OL', 'LF',
|
||||
):
|
||||
for name, kind in (
|
||||
('BTree', (TYPE_BTREE, True)),
|
||||
('Bucket', (TYPE_BUCKET, True)),
|
||||
('TreeSet', (TYPE_BTREE, False)),
|
||||
('Set', (TYPE_BUCKET, False)),
|
||||
):
|
||||
_type2kind[globals()[kv+name]] = kind
|
||||
py = kv + name + 'Py'
|
||||
_type2kind[globals()[py]] = kind
|
||||
|
||||
# Return pair
|
||||
#
|
||||
# TYPE_BTREE or TYPE_BUCKET, is_mapping
|
||||
|
||||
def classify(obj):
|
||||
return _type2kind[type(obj)]
|
||||
|
||||
|
||||
BTREE_EMPTY, BTREE_ONE, BTREE_NORMAL = range(3)
|
||||
|
||||
# If the BTree is empty, returns
|
||||
#
|
||||
# BTREE_EMPTY, [], []
|
||||
#
|
||||
# If the BTree has only one bucket, sometimes returns
|
||||
#
|
||||
# BTREE_ONE, bucket_state, None
|
||||
#
|
||||
# Else returns
|
||||
#
|
||||
# BTREE_NORMAL, list of keys, list of kids
|
||||
#
|
||||
# and the list of kids has one more entry than the list of keys.
|
||||
#
|
||||
# BTree.__getstate__() docs:
|
||||
#
|
||||
# For an empty BTree (self->len == 0), None.
|
||||
#
|
||||
# For a BTree with one child (self->len == 1), and that child is a bucket,
|
||||
# and that bucket has a NULL oid, a one-tuple containing a one-tuple
|
||||
# containing the bucket's state:
|
||||
#
|
||||
# (
|
||||
# (
|
||||
# child[0].__getstate__(),
|
||||
# ),
|
||||
# )
|
||||
#
|
||||
# Else a two-tuple. The first element is a tuple interleaving the BTree's
|
||||
# keys and direct children, of size 2*self->len - 1 (key[0] is unused and
|
||||
# is not saved). The second element is the firstbucket:
|
||||
#
|
||||
# (
|
||||
# (child[0], key[1], child[1], key[2], child[2], ...,
|
||||
# key[len-1], child[len-1]),
|
||||
# self->firstbucket
|
||||
# )
|
||||
|
||||
_btree2bucket = {}
|
||||
for kv in ('OO',
|
||||
'II', 'IO', 'OI', 'IF',
|
||||
'LL', 'LO', 'OL', 'LF',
|
||||
):
|
||||
_btree2bucket[globals()[kv+'BTree']] = globals()[kv+'Bucket']
|
||||
py = kv + 'BTreePy'
|
||||
_btree2bucket[globals()[py]] = globals()[kv+'BucketPy']
|
||||
_btree2bucket[globals()[kv+'TreeSet']] = globals()[kv+'Set']
|
||||
py = kv + 'TreeSetPy'
|
||||
_btree2bucket[globals()[kv+'TreeSetPy']] = globals()[kv+'SetPy']
|
||||
|
||||
|
||||
def crack_btree(t, is_mapping):
|
||||
state = t.__getstate__()
|
||||
if state is None:
|
||||
return BTREE_EMPTY, [], []
|
||||
|
||||
assert isinstance(state, tuple)
|
||||
if len(state) == 1:
|
||||
state = state[0]
|
||||
assert isinstance(state, tuple) and len(state) == 1
|
||||
state = state[0]
|
||||
return BTREE_ONE, state, None
|
||||
|
||||
assert len(state) == 2
|
||||
data, firstbucket = state
|
||||
n = len(data)
|
||||
assert n & 1
|
||||
kids = []
|
||||
keys = []
|
||||
i = 0
|
||||
for x in data:
|
||||
if i & 1:
|
||||
keys.append(x)
|
||||
else:
|
||||
kids.append(x)
|
||||
i += 1
|
||||
return BTREE_NORMAL, keys, kids
|
||||
|
||||
# Returns
|
||||
#
|
||||
# keys, values # for a mapping; len(keys) == len(values) in this case
|
||||
# or
|
||||
# keys, [] # for a set
|
||||
#
|
||||
# bucket.__getstate__() docs:
|
||||
#
|
||||
# For a set bucket (self->values is NULL), a one-tuple or two-tuple. The
|
||||
# first element is a tuple of keys, of length self->len. The second element
|
||||
# is the next bucket, present if and only if next is non-NULL:
|
||||
#
|
||||
# (
|
||||
# (keys[0], keys[1], ..., keys[len-1]),
|
||||
# <self->next iff non-NULL>
|
||||
# )
|
||||
#
|
||||
# For a mapping bucket (self->values is not NULL), a one-tuple or two-tuple.
|
||||
# The first element is a tuple interleaving keys and values, of length
|
||||
# 2 * self->len. The second element is the next bucket, present iff next is
|
||||
# non-NULL:
|
||||
#
|
||||
# (
|
||||
# (keys[0], values[0], keys[1], values[1], ...,
|
||||
# keys[len-1], values[len-1]),
|
||||
# <self->next iff non-NULL>
|
||||
# )
|
||||
|
||||
def crack_bucket(b, is_mapping):
|
||||
state = b.__getstate__()
|
||||
assert isinstance(state, tuple)
|
||||
assert 1 <= len(state) <= 2
|
||||
data = state[0]
|
||||
if not is_mapping:
|
||||
return data, []
|
||||
keys = []
|
||||
values = []
|
||||
n = len(data)
|
||||
assert n & 1 == 0
|
||||
i = 0
|
||||
for x in data:
|
||||
if i & 1:
|
||||
values.append(x)
|
||||
else:
|
||||
keys.append(x)
|
||||
i += 1
|
||||
return keys, values
|
||||
|
||||
def type_and_adr(obj):
|
||||
if hasattr(obj, '_p_oid'):
|
||||
oid = oid_repr(obj._p_oid)
|
||||
else:
|
||||
oid = 'None'
|
||||
return "%s (0x%x oid=%s)" % (type(obj).__name__, positive_id(obj), oid)
|
||||
|
||||
# Walker implements a depth-first search of a BTree (or TreeSet or Set or
|
||||
# Bucket). Subclasses must implement the visit_btree() and visit_bucket()
|
||||
# methods, and arrange to call the walk() method. walk() calls the
|
||||
# visit_XYZ() methods once for each node in the tree, in depth-first
|
||||
# left-to-right order.
|
||||
|
||||
class Walker:
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
|
||||
# obj is the BTree (BTree or TreeSet).
|
||||
# path is a list of indices, from the root. For example, if a BTree node
|
||||
# is child[5] of child[3] of the root BTree, [3, 5].
|
||||
# parent is the parent BTree object, or None if this is the root BTree.
|
||||
# is_mapping is True for a BTree and False for a TreeSet.
|
||||
# keys is a list of the BTree's internal keys.
|
||||
# kids is a list of the BTree's children.
|
||||
# If the BTree is an empty root node, keys == kids == [].
|
||||
# Else len(kids) == len(keys) + 1.
|
||||
# lo and hi are slice bounds on the values the elements of keys *should*
|
||||
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
|
||||
# bound known, and hi is None if no upper bound is known.
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
raise NotImplementedError
|
||||
|
||||
# obj is the bucket (Bucket or Set).
|
||||
# path is a list of indices, from the root. For example, if a bucket
|
||||
# node is child[5] of child[3] of the root BTree, [3, 5].
|
||||
# parent is the parent BTree object.
|
||||
# is_mapping is True for a Bucket and False for a Set.
|
||||
# keys is a list of the bucket's keys.
|
||||
# values is a list of the bucket's values.
|
||||
# If is_mapping is false, values == []. Else len(keys) == len(values).
|
||||
# lo and hi are slice bounds on the values the elements of keys *should*
|
||||
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
|
||||
# bound known, and hi is None if no upper bound is known.
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
raise NotImplementedError
|
||||
|
||||
def walk(self):
|
||||
obj = self.obj
|
||||
path = []
|
||||
stack = [(obj, path, None, None, None)]
|
||||
while stack:
|
||||
obj, path, parent, lo, hi = stack.pop()
|
||||
kind, is_mapping = classify(obj)
|
||||
if kind is TYPE_BTREE:
|
||||
bkind, keys, kids = crack_btree(obj, is_mapping)
|
||||
if bkind is BTREE_NORMAL:
|
||||
# push the kids, in reverse order (so they're popped off
|
||||
# the stack in forward order)
|
||||
n = len(kids)
|
||||
for i in range(len(kids)-1, -1, -1):
|
||||
newlo, newhi = lo, hi
|
||||
if i < n-1:
|
||||
newhi = keys[i]
|
||||
if i > 0:
|
||||
newlo = keys[i-1]
|
||||
stack.append((kids[i],
|
||||
path + [i],
|
||||
obj,
|
||||
newlo,
|
||||
newhi))
|
||||
|
||||
elif bkind is BTREE_EMPTY:
|
||||
pass
|
||||
else:
|
||||
assert bkind is BTREE_ONE
|
||||
# Yuck. There isn't a bucket object to pass on, as
|
||||
# the bucket state is embedded directly in the BTree
|
||||
# state. Synthesize a bucket.
|
||||
assert kids is None # and "keys" is really the bucket
|
||||
# state
|
||||
bucket = _btree2bucket[type(obj)]()
|
||||
bucket.__setstate__(keys)
|
||||
stack.append((bucket,
|
||||
path + [0],
|
||||
obj,
|
||||
lo,
|
||||
hi))
|
||||
keys = []
|
||||
kids = [bucket]
|
||||
|
||||
self.visit_btree(obj,
|
||||
path,
|
||||
parent,
|
||||
is_mapping,
|
||||
keys,
|
||||
kids,
|
||||
lo,
|
||||
hi)
|
||||
else:
|
||||
assert kind is TYPE_BUCKET
|
||||
keys, values = crack_bucket(obj, is_mapping)
|
||||
self.visit_bucket(obj,
|
||||
path,
|
||||
parent,
|
||||
is_mapping,
|
||||
keys,
|
||||
values,
|
||||
lo,
|
||||
hi)
|
||||
|
||||
|
||||
class Checker(Walker):
|
||||
def __init__(self, obj):
|
||||
Walker.__init__(self, obj)
|
||||
self.errors = []
|
||||
|
||||
def check(self):
|
||||
self.walk()
|
||||
if self.errors:
|
||||
s = "Errors found in %s:" % type_and_adr(self.obj)
|
||||
self.errors.insert(0, s)
|
||||
s = "\n".join(self.errors)
|
||||
raise AssertionError(s)
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
self.check_sorted(obj, path, keys, lo, hi)
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
self.check_sorted(obj, path, keys, lo, hi)
|
||||
|
||||
def check_sorted(self, obj, path, keys, lo, hi):
|
||||
i, n = 0, len(keys)
|
||||
for x in keys:
|
||||
# lo or hi are ommitted by supplying None. Thus the not
|
||||
# None checkes below.
|
||||
if lo is not None and not compare(lo, x) <= 0:
|
||||
s = "key %r < lower bound %r at index %d" % (x, lo, i)
|
||||
self.complain(s, obj, path)
|
||||
if hi is not None and not compare(x, hi) < 0:
|
||||
s = "key %r >= upper bound %r at index %d" % (x, hi, i)
|
||||
self.complain(s, obj, path)
|
||||
if i < n-1 and not compare(x, keys[i+1]) < 0:
|
||||
s = "key %r at index %d >= key %r at index %d" % (
|
||||
x, i, keys[i+1], i+1)
|
||||
self.complain(s, obj, path)
|
||||
i += 1
|
||||
|
||||
def complain(self, msg, obj, path):
|
||||
s = "%s, in %s, path from root %s" % (
|
||||
msg,
|
||||
type_and_adr(obj),
|
||||
".".join(map(str, path)))
|
||||
self.errors.append(s)
|
||||
|
||||
class Printer(Walker): #pragma NO COVER
|
||||
def __init__(self, obj):
|
||||
Walker.__init__(self, obj)
|
||||
|
||||
def display(self):
|
||||
self.walk()
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
indent = " " * len(path)
|
||||
print("%s%s %s with %d children" % (
|
||||
indent,
|
||||
".".join(map(str, path)),
|
||||
type_and_adr(obj),
|
||||
len(kids)))
|
||||
indent += " "
|
||||
n = len(keys)
|
||||
for i in range(n):
|
||||
print("%skey %d: %r" % (indent, i, keys[i]))
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
indent = " " * len(path)
|
||||
print("%s%s %s with %d keys" % (
|
||||
indent,
|
||||
".".join(map(str, path)),
|
||||
type_and_adr(obj),
|
||||
len(keys)))
|
||||
indent += " "
|
||||
n = len(keys)
|
||||
for i in range(n):
|
||||
print("%skey %d: %r" % (indent, i, keys[i]),)
|
||||
if is_mapping:
|
||||
print("value %r" % (values[i],))
|
||||
|
||||
def check(btree):
|
||||
"""Check internal value-based invariants in a BTree or TreeSet.
|
||||
|
||||
The btree._check() method checks internal C-level pointer consistency.
|
||||
The check() function here checks value-based invariants: whether the
|
||||
keys in leaf bucket and internal nodes are in strictly increasing order,
|
||||
and whether they all lie in their expected range. The latter is a subtle
|
||||
invariant that can't be checked locally -- it requires propagating
|
||||
range info down from the root of the tree, and modifying it at each
|
||||
level for each child.
|
||||
|
||||
Raises AssertionError if anything is wrong, with a string detail
|
||||
explaining the problems. The entire tree is checked before
|
||||
AssertionError is raised, and the string detail may be large (depending
|
||||
on how much went wrong).
|
||||
"""
|
||||
|
||||
Checker(btree).check()
|
||||
|
||||
def display(btree): #pragma NO COVER
|
||||
"Display the internal structure of a BTree, Bucket, TreeSet or Set."
|
||||
Printer(btree).display()
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#define VALUE_TYPE float
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
|
||||
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define VALUE_PARSE "f"
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (V=(E))
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=PyFloat_FromDouble(K)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (PyFloat_Check(ARG)) TARGET = (float)PyFloat_AsDouble(ARG); \
|
||||
else if (INT_CHECK(ARG)) TARGET = (float)INT_AS_LONG(ARG); \
|
||||
else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected float or int value"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
|
||||
|
||||
#define MERGE_DEFAULT 1.0f
|
||||
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
|
||||
#define MERGE_WEIGHT(O, w) ((O)*(w))
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# fsBTrees are data structures used for ZODB FileStorage. They are not
|
||||
# expected to be "public" excpect to FileStorage.
|
||||
# Each item in an fsBTree maps a two-byte key to a six-byte value.
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'fsBucket', 'fsSet', 'fsBTree', 'fsTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
)
|
||||
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_bytes as _to_bytes
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
|
||||
_BUCKET_SIZE = 500
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
_to_key = _to_bytes(2)
|
||||
_to_value = _to_bytes(6)
|
||||
|
||||
|
||||
class fsBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
def toString(self):
|
||||
return b''.join(self._keys) + b''.join(self._values)
|
||||
|
||||
def fromString(self, v):
|
||||
length = len(v)
|
||||
if length % 8 != 0:
|
||||
raise ValueError()
|
||||
count = length // 8
|
||||
keys, values = v[:count*2], v[count*2:]
|
||||
self.clear()
|
||||
while keys and values:
|
||||
key, keys = keys[:2], keys[2:]
|
||||
value, values = values[:6], values[6:]
|
||||
self._keys.append(key)
|
||||
self._values.append(value)
|
||||
return self
|
||||
|
||||
|
||||
class fsSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class fsBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class fsTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
fsBucketPy._mapping_type = fsBucketPy._bucket_type = fsBucketPy
|
||||
fsBucketPy._set_type = fsSetPy
|
||||
|
||||
fsSetPy._mapping_type = fsBucketPy
|
||||
fsSetPy._set_type = fsSetPy._bucket_type = fsSetPy
|
||||
|
||||
fsBTreePy._mapping_type = fsBTreePy._bucket_type = fsBucketPy
|
||||
fsBTreePy._set_type = fsSetPy
|
||||
|
||||
fsTreeSetPy._mapping_type = fsBucketPy
|
||||
fsTreeSetPy._set_type = fsTreeSetPy._bucket_type = fsSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, fsSetPy)
|
||||
unionPy = _set_operation(_union, fsSetPy)
|
||||
intersectionPy = _set_operation(_intersection, fsSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
/* PY_LONG_LONG as key */
|
||||
#define NEED_LONG_LONG_SUPPORT
|
||||
#define NEED_LONG_LONG_KEYS
|
||||
#define KEY_TYPE PY_LONG_LONG
|
||||
#define KEY_CHECK longlong_check
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=longlong_as_object(K)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (!longlong_convert((ARG), &TARGET)) \
|
||||
{ \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
}
|
||||
#else
|
||||
/* C int as key */
|
||||
#define KEY_TYPE int
|
||||
#define KEY_CHECK INT_CHECK
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (INT_CHECK(ARG)) { \
|
||||
long vcopy = INT_AS_LONG(ARG); \
|
||||
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
|
||||
else if ((int)vcopy != vcopy) { \
|
||||
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
} \
|
||||
else TARGET = vcopy; \
|
||||
} else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
#endif
|
||||
|
||||
#undef KEY_TYPE_IS_PYOBJECT
|
||||
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) ) , 0 )
|
||||
#define DECREF_KEY(KEY)
|
||||
#define INCREF_KEY(k)
|
||||
#define COPY_KEY(KEY, E) (KEY=(E))
|
||||
#define MULTI_INT_UNION 1
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
#define NEED_LONG_LONG_SUPPORT
|
||||
#define VALUE_TYPE PY_LONG_LONG
|
||||
#define VALUE_PARSE "L"
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=longlong_as_object(K)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (!longlong_convert((ARG), &TARGET)) \
|
||||
{ \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
}
|
||||
#else
|
||||
#define VALUE_TYPE int
|
||||
#define VALUE_PARSE "i"
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
|
||||
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (INT_CHECK(ARG)) { \
|
||||
long vcopy = INT_AS_LONG(ARG); \
|
||||
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
|
||||
else if ((int)vcopy != vcopy) { \
|
||||
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
} \
|
||||
else TARGET = vcopy; \
|
||||
} else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
|
||||
#endif
|
||||
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
|
||||
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (V=(E))
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
|
||||
|
||||
#define MERGE_DEFAULT 1
|
||||
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
|
||||
#define MERGE_WEIGHT(O, w) ((O)*(w))
|
||||
@@ -0,0 +1,44 @@
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
#define KEY_TYPE PyObject *
|
||||
#define KEY_TYPE_IS_PYOBJECT
|
||||
|
||||
#include "Python.h"
|
||||
#include "_compat.h"
|
||||
|
||||
static PyObject *object_; /* initialized in BTreeModuleTemplate init */
|
||||
|
||||
static int
|
||||
check_argument_cmp(PyObject *arg)
|
||||
{
|
||||
/* printf("check cmp %p %p %p %p\n", */
|
||||
/* arg->ob_type->tp_richcompare, */
|
||||
/* ((PyTypeObject *)object_)->ob_type->tp_richcompare, */
|
||||
/* arg->ob_type->tp_compare, */
|
||||
/* ((PyTypeObject *)object_)->ob_type->tp_compare); */
|
||||
if (arg == Py_None) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
if (Py_TYPE(arg)->tp_richcompare == Py_TYPE(object_)->tp_richcompare)
|
||||
#else
|
||||
if (Py_TYPE(arg)->tp_richcompare == NULL
|
||||
&& Py_TYPE(arg)->tp_compare == Py_TYPE(object_)->tp_compare)
|
||||
#endif
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Object has default comparison");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define TEST_KEY_SET_OR(V, KEY, TARGET) \
|
||||
if ( ( (V) = COMPARE((KEY),(TARGET)) ), PyErr_Occurred() )
|
||||
#define INCREF_KEY(k) Py_INCREF(k)
|
||||
#define DECREF_KEY(KEY) Py_DECREF(KEY)
|
||||
#define COPY_KEY(KEY, E) KEY=(E)
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, S) \
|
||||
TARGET=(ARG); \
|
||||
(S) = 1;
|
||||
#define KEY_CHECK_ON_SET check_argument_cmp
|
||||
@@ -0,0 +1,12 @@
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#define VALUE_TYPE PyObject *
|
||||
#define VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(VALUE, TARGET) (COMPARE((VALUE),(TARGET)))
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define INCREF_VALUE(k) Py_INCREF(k)
|
||||
#define DECREF_VALUE(k) Py_DECREF(k)
|
||||
#define COPY_VALUE(k,e) k=(e)
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, S) TARGET=(ARG)
|
||||
#define NORMALIZE_VALUE(V, MIN) Py_INCREF(V)
|
||||
@@ -0,0 +1,542 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
/* Revision information: $Id$ */
|
||||
|
||||
/* The only routine here intended to be used outside the file is
|
||||
size_t sort_int_nodups(int *p, size_t n)
|
||||
|
||||
Sort the array of n ints pointed at by p, in place, and also remove
|
||||
duplicates. Return the number of unique elements remaining, which occupy
|
||||
a contiguous and monotonically increasing slice of the array starting at p.
|
||||
|
||||
Example: If the input array is [3, 1, 2, 3, 1, 5, 2], sort_int_nodups
|
||||
returns 4, and the first 4 elements of the array are changed to
|
||||
[1, 2, 3, 5]. The content of the remaining array positions is not defined.
|
||||
|
||||
Notes:
|
||||
|
||||
+ This is specific to n-byte signed ints, with endianness natural to the
|
||||
platform. `n` is determined based on ZODB_64BIT_INTS.
|
||||
|
||||
+ 4*n bytes of available heap memory are required for best speed
|
||||
(8*n when ZODB_64BIT_INTS is defined).
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* The type of array elements to be sorted. Most of the routines don't
|
||||
care about the type, and will work fine for any scalar C type (provided
|
||||
they're recompiled with element_type appropriately redefined). However,
|
||||
the radix sort has to know everything about the type's internal
|
||||
representation.
|
||||
*/
|
||||
typedef KEY_TYPE element_type;
|
||||
|
||||
/* The radixsort is faster than the quicksort for large arrays, but radixsort
|
||||
has high fixed overhead, making it a poor choice for small arrays. The
|
||||
crossover point isn't critical, and is sensitive to things like compiler
|
||||
and machine cache structure, so don't worry much about this.
|
||||
*/
|
||||
#define QUICKSORT_BEATS_RADIXSORT 800U
|
||||
|
||||
/* In turn, the quicksort backs off to an insertion sort for very small
|
||||
slices. MAX_INSERTION is the largest slice quicksort leaves entirely to
|
||||
insertion. Because this version of quicksort uses a median-of-3 rule for
|
||||
selecting a pivot, MAX_INSERTION must be at least 2 (so that quicksort
|
||||
has at least 3 values to look at in a slice). Again, the exact value here
|
||||
isn't critical.
|
||||
*/
|
||||
#define MAX_INSERTION 25U
|
||||
|
||||
#if MAX_INSERTION < 2U
|
||||
# error "MAX_INSERTION must be >= 2"
|
||||
#endif
|
||||
|
||||
/* LSB-first radix sort of the n elements in 'in'.
|
||||
'work' is work storage at least as large as 'in'. Depending on how many
|
||||
swaps are done internally, the final result may come back in 'in' or 'work';
|
||||
and that pointer is returned.
|
||||
|
||||
radixsort_int is specific to signed n-byte ints, with natural machine
|
||||
endianness. `n` is determined based on ZODB_64BIT_INTS.
|
||||
*/
|
||||
static element_type*
|
||||
radixsort_int(element_type *in, element_type *work, size_t n)
|
||||
{
|
||||
/* count[i][j] is the number of input elements that have byte value j
|
||||
in byte position i, where byte position 0 is the LSB. Note that
|
||||
holding i fixed, the sum of count[i][j] over all j in range(256)
|
||||
is n.
|
||||
*/
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
size_t count[8][256];
|
||||
#else
|
||||
size_t count[4][256];
|
||||
#endif
|
||||
size_t i;
|
||||
int offset, offsetinc;
|
||||
|
||||
/* Which byte position are we working on now? 0=LSB, 1, 2, ... */
|
||||
size_t bytenum;
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
assert(sizeof(element_type) == 8);
|
||||
#else
|
||||
assert(sizeof(element_type) == 4);
|
||||
#endif
|
||||
assert(in);
|
||||
assert(work);
|
||||
|
||||
/* Compute all of count in one pass. */
|
||||
memset(count, 0, sizeof(count));
|
||||
for (i = 0; i < n; ++i) {
|
||||
element_type const x = in[i];
|
||||
++count[0][(x ) & 0xff];
|
||||
++count[1][(x >> 8) & 0xff];
|
||||
++count[2][(x >> 16) & 0xff];
|
||||
++count[3][(x >> 24) & 0xff];
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
++count[4][(x >> 32) & 0xff];
|
||||
++count[5][(x >> 40) & 0xff];
|
||||
++count[6][(x >> 48) & 0xff];
|
||||
++count[7][(x >> 56) & 0xff];
|
||||
#endif
|
||||
}
|
||||
|
||||
/* For p an element_type* cast to char*, offset is how much farther we
|
||||
have to go to get to the LSB of the element; this is 0 for little-
|
||||
endian boxes and sizeof(element_type)-1 for big-endian.
|
||||
offsetinc is 1 or -1, respectively, telling us which direction to go
|
||||
from p+offset to get to the element's more-significant bytes.
|
||||
*/
|
||||
{
|
||||
element_type one = 1;
|
||||
if (*(char*)&one) {
|
||||
/* Little endian. */
|
||||
offset = 0;
|
||||
offsetinc = 1;
|
||||
}
|
||||
else {
|
||||
/* Big endian. */
|
||||
offset = sizeof(element_type) - 1;
|
||||
offsetinc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The radix sort. */
|
||||
for (bytenum = 0;
|
||||
bytenum < sizeof(element_type);
|
||||
++bytenum, offset += offsetinc) {
|
||||
|
||||
/* Do a stable distribution sort on byte position bytenum,
|
||||
from in to work. index[i] tells us the work index at which
|
||||
to store the next in element with byte value i. pinbyte
|
||||
points to the correct byte in the input array.
|
||||
*/
|
||||
size_t index[256];
|
||||
unsigned char* pinbyte;
|
||||
size_t total = 0;
|
||||
size_t *pcount = count[bytenum];
|
||||
|
||||
/* Compute the correct output starting index for each possible
|
||||
byte value.
|
||||
*/
|
||||
if (bytenum < sizeof(element_type) - 1) {
|
||||
for (i = 0; i < 256; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 256) {
|
||||
/* All bytes in the current position have value
|
||||
i, so there's nothing to do on this pass.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* The MSB of signed ints needs to be distributed
|
||||
differently than the other bytes, in order
|
||||
0x80, 0x81, ... 0xff, 0x00, 0x01, ... 0x7f
|
||||
*/
|
||||
for (i = 128; i < 256; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 256)
|
||||
continue;
|
||||
for (i = 0; i < 128; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 128)
|
||||
continue;
|
||||
}
|
||||
assert(total == n);
|
||||
|
||||
/* Distribute the elements according to byte value. Note that
|
||||
this is where most of the time is spent.
|
||||
Note: The loop is unrolled 4x by hand, for speed. This
|
||||
may be a pessimization someday, but was a significant win
|
||||
on my MSVC 6.0 timing tests.
|
||||
*/
|
||||
pinbyte = (unsigned char *)in + offset;
|
||||
i = 0;
|
||||
/* Reduce number of elements to copy to a multiple of 4. */
|
||||
while ((n - i) & 0x3) {
|
||||
unsigned char byte = *pinbyte;
|
||||
work[index[byte]++] = in[i];
|
||||
++i;
|
||||
pinbyte += sizeof(element_type);
|
||||
}
|
||||
for (; i < n; i += 4, pinbyte += 4 * sizeof(element_type)) {
|
||||
unsigned char byte1 = *(pinbyte );
|
||||
unsigned char byte2 = *(pinbyte + sizeof(element_type));
|
||||
unsigned char byte3 = *(pinbyte + 2 * sizeof(element_type));
|
||||
unsigned char byte4 = *(pinbyte + 3 * sizeof(element_type));
|
||||
|
||||
element_type in1 = in[i ];
|
||||
element_type in2 = in[i+1];
|
||||
element_type in3 = in[i+2];
|
||||
element_type in4 = in[i+3];
|
||||
|
||||
work[index[byte1]++] = in1;
|
||||
work[index[byte2]++] = in2;
|
||||
work[index[byte3]++] = in3;
|
||||
work[index[byte4]++] = in4;
|
||||
}
|
||||
/* Swap in and work (just a pointer swap). */
|
||||
{
|
||||
element_type *temp = in;
|
||||
in = work;
|
||||
work = temp;
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
/* Remove duplicates from sorted array in, storing exactly one of each distinct
|
||||
element value into sorted array out. It's OK (and expected!) for in == out,
|
||||
but otherwise the n elements beginning at in must not overlap with the n
|
||||
beginning at out.
|
||||
Return the number of elements in out.
|
||||
*/
|
||||
static size_t
|
||||
uniq(element_type *out, element_type *in, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
element_type lastelt;
|
||||
element_type *pout;
|
||||
|
||||
assert(out);
|
||||
assert(in);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
/* i <- first index in 'in' that contains a duplicate.
|
||||
in[0], in[1], ... in[i-1] are unique, but in[i-1] == in[i].
|
||||
Set i to n if everything is unique.
|
||||
*/
|
||||
for (i = 1; i < n; ++i) {
|
||||
if (in[i-1] == in[i])
|
||||
break;
|
||||
}
|
||||
|
||||
/* in[:i] is unique; copy to out[:i] if needed. */
|
||||
assert(i > 0);
|
||||
if (in != out)
|
||||
memcpy(out, in, i * sizeof(element_type));
|
||||
|
||||
pout = out + i;
|
||||
lastelt = in[i-1]; /* safe even when i == n */
|
||||
for (++i; i < n; ++i) {
|
||||
element_type elt = in[i];
|
||||
if (elt != lastelt)
|
||||
*pout++ = lastelt = elt;
|
||||
}
|
||||
return pout - out;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* insertionsort is no longer referenced directly, but I'd like to keep
|
||||
* the code here just in case.
|
||||
*/
|
||||
|
||||
/* Straight insertion sort of the n elements starting at 'in'. */
|
||||
static void
|
||||
insertionsort(element_type *in, size_t n)
|
||||
{
|
||||
element_type *p, *q;
|
||||
element_type minimum; /* smallest seen so far */
|
||||
element_type *plimit = in + n;
|
||||
|
||||
assert(in);
|
||||
if (n < 2)
|
||||
return;
|
||||
|
||||
minimum = *in;
|
||||
for (p = in+1; p < plimit; ++p) {
|
||||
/* *in <= *(in+1) <= ... <= *(p-1). Slide *p into place. */
|
||||
element_type thiselt = *p;
|
||||
if (thiselt < minimum) {
|
||||
/* This is a new minimum. This saves p-in compares
|
||||
when it happens, but should happen so rarely that
|
||||
it's not worth checking for its own sake: the
|
||||
point is that the far more popular 'else' branch can
|
||||
exploit that thiselt is *not* the smallest so far.
|
||||
*/
|
||||
memmove(in+1, in, (p - in) * sizeof(*in));
|
||||
*in = minimum = thiselt;
|
||||
}
|
||||
else {
|
||||
/* thiselt >= minimum, so the loop will find a q
|
||||
with *q <= thiselt. This saves testing q >= in
|
||||
on each trip. It's such a simple loop that saving
|
||||
a per-trip test is a major speed win.
|
||||
*/
|
||||
for (q = p-1; *q > thiselt; --q)
|
||||
*(q+1) = *q;
|
||||
*(q+1) = thiselt;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The maximum number of elements in the pending-work stack quicksort
|
||||
maintains. The maximum stack depth is approximately log2(n), so
|
||||
arrays of size up to approximately MAX_INSERTION * 2**STACKSIZE can be
|
||||
sorted. The memory burden for the stack is small, so better safe than
|
||||
sorry.
|
||||
*/
|
||||
#define STACKSIZE 60
|
||||
|
||||
/* A _stacknode remembers a contiguous slice of an array that needs to sorted.
|
||||
lo must be <= hi, and, unlike Python array slices, this includes both ends.
|
||||
*/
|
||||
struct _stacknode {
|
||||
element_type *lo;
|
||||
element_type *hi;
|
||||
};
|
||||
|
||||
static void
|
||||
quicksort(element_type *plo, size_t n)
|
||||
{
|
||||
element_type *phi;
|
||||
|
||||
/* Swap two array elements. */
|
||||
element_type _temp;
|
||||
#define SWAP(P, Q) (_temp = *(P), *(P) = *(Q), *(Q) = _temp)
|
||||
|
||||
/* Stack of pending array slices to be sorted. */
|
||||
struct _stacknode stack[STACKSIZE];
|
||||
struct _stacknode *stackfree = stack; /* available stack slot */
|
||||
|
||||
/* Push an array slice on the pending-work stack. */
|
||||
#define PUSH(PLO, PHI) \
|
||||
do { \
|
||||
assert(stackfree - stack < STACKSIZE); \
|
||||
assert((PLO) <= (PHI)); \
|
||||
stackfree->lo = (PLO); \
|
||||
stackfree->hi = (PHI); \
|
||||
++stackfree; \
|
||||
} while(0)
|
||||
|
||||
assert(plo);
|
||||
phi = plo + n - 1;
|
||||
|
||||
for (;;) {
|
||||
element_type pivot;
|
||||
element_type *pi, *pj;
|
||||
|
||||
assert(plo <= phi);
|
||||
n = phi - plo + 1;
|
||||
if (n <= MAX_INSERTION) {
|
||||
/* Do a small insertion sort. Contra Knuth, we do
|
||||
this now instead of waiting until the end, because
|
||||
this little slice is likely still in cache now.
|
||||
*/
|
||||
element_type *p, *q;
|
||||
element_type minimum = *plo;
|
||||
|
||||
for (p = plo+1; p <= phi; ++p) {
|
||||
/* *plo <= *(plo+1) <= ... <= *(p-1).
|
||||
Slide *p into place. */
|
||||
element_type thiselt = *p;
|
||||
if (thiselt < minimum) {
|
||||
/* New minimum. */
|
||||
memmove(plo+1,
|
||||
plo,
|
||||
(p - plo) * sizeof(*p));
|
||||
*plo = minimum = thiselt;
|
||||
}
|
||||
else {
|
||||
/* thiselt >= minimum, so the loop will
|
||||
find a q with *q <= thiselt.
|
||||
*/
|
||||
for (q = p-1; *q > thiselt; --q)
|
||||
*(q+1) = *q;
|
||||
*(q+1) = thiselt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop another slice off the stack. */
|
||||
if (stack == stackfree)
|
||||
break; /* no more slices -- we're done */
|
||||
--stackfree;
|
||||
plo = stackfree->lo;
|
||||
phi = stackfree->hi;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parition the slice.
|
||||
For pivot, take the median of the leftmost, rightmost, and
|
||||
middle elements. First sort those three; then the median
|
||||
is the middle one. For technical reasons, the middle
|
||||
element is swapped to plo+1 first (see Knuth Vol 3 Ed 2
|
||||
section 5.2.2 exercise 55 -- reverse-sorted arrays can
|
||||
take quadratic time otherwise!).
|
||||
*/
|
||||
{
|
||||
element_type *plop1 = plo + 1;
|
||||
element_type *pmid = plo + (n >> 1);
|
||||
|
||||
assert(plo < pmid && pmid < phi);
|
||||
SWAP(plop1, pmid);
|
||||
|
||||
/* Sort plo, plop1, phi. */
|
||||
/* Smaller of rightmost two -> middle. */
|
||||
if (*plop1 > *phi)
|
||||
SWAP(plop1, phi);
|
||||
/* Smallest of all -> left; if plo is already the
|
||||
smallest, the sort is complete.
|
||||
*/
|
||||
if (*plo > *plop1) {
|
||||
SWAP(plo, plop1);
|
||||
/* Largest of all -> right. */
|
||||
if (*plop1 > *phi)
|
||||
SWAP(plop1, phi);
|
||||
}
|
||||
pivot = *plop1;
|
||||
pi = plop1;
|
||||
}
|
||||
assert(*plo <= pivot);
|
||||
assert(*pi == pivot);
|
||||
assert(*phi >= pivot);
|
||||
pj = phi;
|
||||
|
||||
/* Partition wrt pivot. This is the time-critical part, and
|
||||
nearly every decision in the routine aims at making this
|
||||
loop as fast as possible -- even small points like
|
||||
arranging that all loop tests can be done correctly at the
|
||||
bottoms of loops instead of the tops, and that pointers can
|
||||
be derefenced directly as-is (without fiddly +1 or -1).
|
||||
The aim is to make the C here so simple that a compiler
|
||||
has a good shot at doing as well as hand-crafted assembler.
|
||||
*/
|
||||
for (;;) {
|
||||
/* Invariants:
|
||||
1. pi < pj.
|
||||
2. All elements at plo, plo+1 .. pi are <= pivot.
|
||||
3. All elements at pj, pj+1 .. phi are >= pivot.
|
||||
4. There is an element >= pivot to the right of pi.
|
||||
5. There is an element <= pivot to the left of pj.
|
||||
|
||||
Note that #4 and #5 save us from needing to check
|
||||
that the pointers stay in bounds.
|
||||
*/
|
||||
assert(pi < pj);
|
||||
|
||||
do { ++pi; } while (*pi < pivot);
|
||||
assert(pi <= pj);
|
||||
|
||||
do { --pj; } while (*pj > pivot);
|
||||
assert(pj >= pi - 1);
|
||||
|
||||
if (pi < pj)
|
||||
SWAP(pi, pj);
|
||||
else
|
||||
break;
|
||||
}
|
||||
assert(plo+1 < pi && pi <= phi);
|
||||
assert(plo < pj && pj < phi);
|
||||
assert(*pi >= pivot);
|
||||
assert( (pi == pj && *pj == pivot) ||
|
||||
(pj + 1 == pi && *pj <= pivot) );
|
||||
|
||||
/* Swap pivot into its final position, pj. */
|
||||
assert(plo[1] == pivot);
|
||||
plo[1] = *pj;
|
||||
*pj = pivot;
|
||||
|
||||
/* Subfiles are from plo to pj-1 inclusive, and pj+1 to phi
|
||||
inclusive. Push the larger one, and loop back to do the
|
||||
smaller one directly.
|
||||
*/
|
||||
if (pj - plo >= phi - pj) {
|
||||
PUSH(plo, pj-1);
|
||||
plo = pj+1;
|
||||
}
|
||||
else {
|
||||
PUSH(pj+1, phi);
|
||||
phi = pj-1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef PUSH
|
||||
#undef SWAP
|
||||
}
|
||||
|
||||
/* Sort p and remove duplicates, as fast as we can. */
|
||||
static size_t
|
||||
sort_int_nodups(KEY_TYPE *p, size_t n)
|
||||
{
|
||||
size_t nunique;
|
||||
element_type *work;
|
||||
|
||||
assert(sizeof(KEY_TYPE) == sizeof(element_type));
|
||||
assert(p);
|
||||
|
||||
/* Use quicksort if the array is small, OR if malloc can't find
|
||||
enough temp memory for radixsort.
|
||||
*/
|
||||
work = NULL;
|
||||
if (n > QUICKSORT_BEATS_RADIXSORT)
|
||||
work = (element_type *)malloc(n * sizeof(element_type));
|
||||
|
||||
if (work) {
|
||||
element_type *out = radixsort_int(p, work, n);
|
||||
nunique = uniq(p, out, n);
|
||||
free(work);
|
||||
}
|
||||
else {
|
||||
quicksort(p, n);
|
||||
nunique = uniq(p, p, n);
|
||||
}
|
||||
|
||||
return nunique;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# If tests is a package, debugging is a bit easier.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,534 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from BTrees.tests.common import permutations
|
||||
|
||||
|
||||
class DegenerateBTree(unittest.TestCase):
|
||||
# Build a degenerate tree (set). Boxes are BTree nodes. There are
|
||||
# 5 leaf buckets, each containing a single int. Keys in the BTree
|
||||
# nodes don't appear in the buckets. Seven BTree nodes are purely
|
||||
# indirection nodes (no keys). Buckets aren't all at the same depth:
|
||||
#
|
||||
# +------------------------+
|
||||
# | 4 |
|
||||
# +------------------------+
|
||||
# | |
|
||||
# | v
|
||||
# | +-+
|
||||
# | | |
|
||||
# | +-+
|
||||
# | |
|
||||
# v v
|
||||
# +-------+ +-------------+
|
||||
# | 2 | | 6 10 |
|
||||
# +-------+ +-------------+
|
||||
# | | | | |
|
||||
# v v v v v
|
||||
# +-+ +-+ +-+ +-+ +-+
|
||||
# | | | | | | | | | |
|
||||
# +-+ +-+ +-+ +-+ +-+
|
||||
# | | | | |
|
||||
# v v v v v
|
||||
# 1 3 +-+ 7 11
|
||||
# | |
|
||||
# +-+
|
||||
# |
|
||||
# v
|
||||
# 5
|
||||
#
|
||||
# This is nasty for many algorithms. Consider a high-end range search
|
||||
# for 4. The BTree nodes direct it to the 5 bucket, but the correct
|
||||
# answer is the 3 bucket, which requires going in a different direction
|
||||
# at the very top node already. Consider a low-end range search for
|
||||
# 9. The BTree nodes direct it to the 7 bucket, but the correct answer
|
||||
# is the 11 bucket. This is also a nasty-case tree for deletions.
|
||||
|
||||
def _build_degenerate_tree(self):
|
||||
# Build the buckets and chain them together.
|
||||
from BTrees.IIBTree import IISet
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.check import check
|
||||
bucket11 = IISet([11])
|
||||
|
||||
bucket7 = IISet()
|
||||
bucket7.__setstate__(((7,), bucket11))
|
||||
|
||||
bucket5 = IISet()
|
||||
bucket5.__setstate__(((5,), bucket7))
|
||||
|
||||
bucket3 = IISet()
|
||||
bucket3.__setstate__(((3,), bucket5))
|
||||
|
||||
bucket1 = IISet()
|
||||
bucket1.__setstate__(((1,), bucket3))
|
||||
|
||||
# Build the deepest layers of indirection nodes.
|
||||
ts = IITreeSet
|
||||
tree1 = ts()
|
||||
tree1.__setstate__(((bucket1,), bucket1))
|
||||
|
||||
tree3 = ts()
|
||||
tree3.__setstate__(((bucket3,), bucket3))
|
||||
|
||||
tree5lower = ts()
|
||||
tree5lower.__setstate__(((bucket5,), bucket5))
|
||||
tree5 = ts()
|
||||
tree5.__setstate__(((tree5lower,), bucket5))
|
||||
|
||||
tree7 = ts()
|
||||
tree7.__setstate__(((bucket7,), bucket7))
|
||||
|
||||
tree11 = ts()
|
||||
tree11.__setstate__(((bucket11,), bucket11))
|
||||
|
||||
# Paste together the middle layers.
|
||||
tree13 = ts()
|
||||
tree13.__setstate__(((tree1, 2, tree3), bucket1))
|
||||
|
||||
tree5711lower = ts()
|
||||
tree5711lower.__setstate__(((tree5, 6, tree7, 10, tree11), bucket5))
|
||||
tree5711 = ts()
|
||||
tree5711.__setstate__(((tree5711lower,), bucket5))
|
||||
|
||||
# One more.
|
||||
t = ts()
|
||||
t.__setstate__(((tree13, 4, tree5711), bucket1))
|
||||
t._check()
|
||||
check(t)
|
||||
return t, [1, 3, 5, 7, 11]
|
||||
|
||||
def testBasicOps(self):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
self.assertEqual(len(t), len(keys))
|
||||
self.assertEqual(list(t.keys()), keys)
|
||||
# has_key actually returns the depth of a bucket.
|
||||
self.assertEqual(t.has_key(1), 4)
|
||||
self.assertEqual(t.has_key(3), 4)
|
||||
self.assertEqual(t.has_key(5), 6)
|
||||
self.assertEqual(t.has_key(7), 5)
|
||||
self.assertEqual(t.has_key(11), 5)
|
||||
for i in 0, 2, 4, 6, 8, 9, 10, 12:
|
||||
self.assertTrue(i not in t)
|
||||
|
||||
def _checkRanges(self, tree, keys):
|
||||
self.assertEqual(len(tree), len(keys))
|
||||
sorted_keys = keys[:]
|
||||
sorted_keys.sort()
|
||||
self.assertEqual(list(tree.keys()), sorted_keys)
|
||||
for k in keys:
|
||||
self.assertTrue(k in tree)
|
||||
if keys:
|
||||
lokey = sorted_keys[0]
|
||||
hikey = sorted_keys[-1]
|
||||
self.assertEqual(lokey, tree.minKey())
|
||||
self.assertEqual(hikey, tree.maxKey())
|
||||
else:
|
||||
lokey = hikey = 42
|
||||
|
||||
# Try all range searches.
|
||||
for lo in range(lokey - 1, hikey + 2):
|
||||
for hi in range(lo - 1, hikey + 2):
|
||||
for skipmin in False, True:
|
||||
for skipmax in False, True:
|
||||
wantlo, wanthi = lo, hi
|
||||
if skipmin:
|
||||
wantlo += 1
|
||||
if skipmax:
|
||||
wanthi -= 1
|
||||
want = [k for k in keys if wantlo <= k <= wanthi]
|
||||
got = list(tree.keys(lo, hi, skipmin, skipmax))
|
||||
self.assertEqual(want, got)
|
||||
|
||||
def testRanges(self):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
self._checkRanges(t, keys)
|
||||
|
||||
def testDeletes(self):
|
||||
# Delete keys in all possible orders, checking each tree along
|
||||
# the way.
|
||||
|
||||
# This is a tough test. Previous failure modes included:
|
||||
# 1. A variety of assertion failures in _checkRanges.
|
||||
# 2. Assorted "Invalid firstbucket pointer" failures at
|
||||
# seemingly random times, coming out of the BTree destructor.
|
||||
# 3. Under Python 2.3 CVS, some baffling
|
||||
# RuntimeWarning: tp_compare didn't return -1 or -2 for exception
|
||||
# warnings, possibly due to memory corruption after a BTree
|
||||
# goes insane.
|
||||
# On CPython in PURE_PYTHON mode, this is a *slow* test, taking 15+s
|
||||
# on a 2015 laptop.
|
||||
from BTrees.check import check
|
||||
t, keys = self._build_degenerate_tree()
|
||||
for oneperm in permutations(keys):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
for key in oneperm:
|
||||
t.remove(key)
|
||||
keys.remove(key)
|
||||
t._check()
|
||||
check(t)
|
||||
self._checkRanges(t, keys)
|
||||
# We removed all the keys, so the tree should be empty now.
|
||||
self.assertEqual(t.__getstate__(), None)
|
||||
|
||||
# A damaged tree may trigger an "invalid firstbucket pointer"
|
||||
# failure at the time its destructor is invoked. Try to force
|
||||
# that to happen now, so it doesn't look like a baffling failure
|
||||
# at some unrelated line.
|
||||
del t # trigger destructor
|
||||
|
||||
|
||||
LP294788_ids = {}
|
||||
|
||||
|
||||
class ToBeDeleted(object):
|
||||
def __init__(self, id):
|
||||
assert isinstance(id, int) #we don't want to store any object ref here
|
||||
self.id = id
|
||||
|
||||
global LP294788_ids
|
||||
LP294788_ids[id] = 1
|
||||
|
||||
def __del__(self):
|
||||
global LP294788_ids
|
||||
LP294788_ids.pop(self.id, None)
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.id, other.id)
|
||||
|
||||
def __le__(self, other):
|
||||
return self.id <= other.id
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.id < other.id
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.id != other.id
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.id > other.id
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.id >= other.id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.id)
|
||||
|
||||
|
||||
class BugFixes(unittest.TestCase):
|
||||
|
||||
# Collector 1843. Error returns were effectively ignored in
|
||||
# Bucket_rangeSearch(), leading to "delayed" errors, or worse.
|
||||
def testFixed1843(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
t = IISet()
|
||||
t.insert(1)
|
||||
# This one used to fail to raise the TypeError when it occurred.
|
||||
self.assertRaises(TypeError, t.keys, "")
|
||||
# This one used to segfault.
|
||||
self.assertRaises(TypeError, t.keys, 0, "")
|
||||
|
||||
def test_LP294788(self):
|
||||
# https://bugs.launchpad.net/bugs/294788
|
||||
# BTree keeps some deleted objects referenced
|
||||
|
||||
# The logic here together with the ToBeDeleted class is that
|
||||
# a separate reference dict is populated on object creation
|
||||
# and removed in __del__
|
||||
# That means what's left in the reference dict is never GC'ed
|
||||
# therefore referenced somewhere
|
||||
# To simulate real life, some random data is used to exercise the tree
|
||||
import gc
|
||||
import random
|
||||
from BTrees.OOBTree import OOBTree
|
||||
|
||||
t = OOBTree()
|
||||
|
||||
trandom = random.Random('OOBTree')
|
||||
|
||||
global LP294788_ids
|
||||
|
||||
# /// BTree keys are integers, value is an object
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = ToBeDeleted(id)
|
||||
else:
|
||||
#del
|
||||
keys = list(ids.keys())
|
||||
if keys:
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del t[id]
|
||||
del ids[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
ids = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
# \\\
|
||||
|
||||
# /// BTree keys are integers, value is a tuple having an object
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = (id, ToBeDeleted(id), u'somename')
|
||||
else:
|
||||
#del
|
||||
keys = list(ids.keys())
|
||||
if keys:
|
||||
id = trandom.choice(keys)
|
||||
del t[id]
|
||||
del ids[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
ids = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
# \\\
|
||||
|
||||
|
||||
# /// BTree keys are objects, value is an int
|
||||
t = OOBTree()
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = ToBeDeleted(trandom.randint(0, 1000000))
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = 1
|
||||
else:
|
||||
#del
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del ids[id]
|
||||
del t[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
#release all refs
|
||||
ids = id = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
|
||||
# /// BTree keys are tuples having objects, value is an int
|
||||
t = OOBTree()
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
id = (id, ToBeDeleted(id), u'somename')
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = 1
|
||||
else:
|
||||
#del
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del ids[id]
|
||||
del t[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
#release all refs
|
||||
ids = id = key = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
|
||||
|
||||
# cmp error propagation tests
|
||||
|
||||
|
||||
class DoesntLikeBeingCompared:
|
||||
|
||||
def __cmp__(self, other):
|
||||
raise ValueError('incomparable')
|
||||
__lt__ = __le__ = __eq__ = __ne__ = __ge__ = __gt__ = __cmp__
|
||||
|
||||
class TestCmpError(unittest.TestCase):
|
||||
|
||||
def testFoo(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
t = OOBTree()
|
||||
t['hello world'] = None
|
||||
try:
|
||||
t[DoesntLikeBeingCompared()] = None
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), 'incomparable')
|
||||
else:
|
||||
self.fail('incomarable objects should not be allowed into '
|
||||
'the tree')
|
||||
|
||||
|
||||
class FamilyTest(unittest.TestCase):
|
||||
def test32(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
import BTrees
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family32)
|
||||
self.assertEqual(
|
||||
BTrees.family32.IO, BTrees.IOBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.OI, BTrees.OIBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.II, BTrees.IIBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.IF, BTrees.IFBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.OO, BTrees.OOBTree)
|
||||
s = IOTreeSet()
|
||||
s.insert(BTrees.family32.maxint)
|
||||
self.assertTrue(BTrees.family32.maxint in s)
|
||||
s = IOTreeSet()
|
||||
s.insert(BTrees.family32.minint)
|
||||
self.assertTrue(BTrees.family32.minint in s)
|
||||
s = IOTreeSet()
|
||||
# this next bit illustrates an, um, "interesting feature". If
|
||||
# the characteristics change to match the 64 bit version, please
|
||||
# feel free to change.
|
||||
with self.assertRaises((TypeError, OverflowError)):
|
||||
s.insert(BTrees.family32.maxint + 1)
|
||||
|
||||
with self.assertRaises((TypeError, OverflowError)):
|
||||
s.insert(BTrees.family32.minint - 1)
|
||||
self.check_pickling(BTrees.family32)
|
||||
|
||||
def test64(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
import BTrees
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family64)
|
||||
self.assertEqual(
|
||||
BTrees.family64.IO, BTrees.LOBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.OI, BTrees.OLBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.II, BTrees.LLBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.IF, BTrees.LFBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.OO, BTrees.OOBTree)
|
||||
s = LOTreeSet()
|
||||
s.insert(BTrees.family64.maxint)
|
||||
self.assertTrue(BTrees.family64.maxint in s)
|
||||
s = LOTreeSet()
|
||||
s.insert(BTrees.family64.minint)
|
||||
self.assertTrue(BTrees.family64.minint in s)
|
||||
s = LOTreeSet()
|
||||
# XXX why oh why do we expect ValueError here, but TypeError in test32?
|
||||
self.assertRaises(ValueError, s.insert, BTrees.family64.maxint + 1)
|
||||
self.assertRaises(ValueError, s.insert, BTrees.family64.minint - 1)
|
||||
self.check_pickling(BTrees.family64)
|
||||
|
||||
def check_pickling(self, family):
|
||||
# The "family" objects are singletons; they can be pickled and
|
||||
# unpickled, and the same instances will always be returned on
|
||||
# unpickling, whether from the same unpickler or different
|
||||
# unpicklers.
|
||||
import pickle
|
||||
from io import BytesIO
|
||||
|
||||
s = pickle.dumps((family, family))
|
||||
(f1, f2) = pickle.loads(s)
|
||||
self.assertIs(f1, family)
|
||||
self.assertIs(f2, family)
|
||||
|
||||
# Using a single memo across multiple pickles:
|
||||
sio = BytesIO()
|
||||
p = pickle.Pickler(sio)
|
||||
p.dump(family)
|
||||
p.dump([family])
|
||||
u = pickle.Unpickler(BytesIO(sio.getvalue()))
|
||||
f1 = u.load()
|
||||
f2, = u.load()
|
||||
self.assertTrue(f1 is family)
|
||||
self.assertTrue(f2 is family)
|
||||
|
||||
# Using separate memos for each pickle:
|
||||
sio = BytesIO()
|
||||
p = pickle.Pickler(sio)
|
||||
p.dump(family)
|
||||
p.clear_memo()
|
||||
p.dump([family])
|
||||
u = pickle.Unpickler(BytesIO(sio.getvalue()))
|
||||
f1 = u.load()
|
||||
f2, = u.load()
|
||||
self.assertTrue(f1 is family)
|
||||
self.assertTrue(f2 is family)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(DegenerateBTree),
|
||||
unittest.makeSuite(BugFixes),
|
||||
unittest.makeSuite(TestCmpError),
|
||||
unittest.makeSuite(FamilyTest),
|
||||
))
|
||||
@@ -0,0 +1,76 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import unittest
|
||||
from .common import _skip_under_Py3k
|
||||
|
||||
# When an OOBtree contains unicode strings as keys,
|
||||
# it is neccessary accessing non-unicode strings are
|
||||
# either ascii strings or encoded as unicoded using the
|
||||
# corresponding encoding
|
||||
|
||||
encoding = 'ISO-8859-1'
|
||||
|
||||
class TestBTreesUnicode(unittest.TestCase):
|
||||
""" test unicode"""
|
||||
|
||||
def setUp(self):
|
||||
#setup an OOBTree with some unicode strings
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from BTrees._compat import _bytes
|
||||
|
||||
self.s = b'dreit\xe4gigen'.decode('latin1')
|
||||
|
||||
self.data = [(b'alien', 1),
|
||||
(b'k\xf6nnten', 2),
|
||||
(b'fox', 3),
|
||||
(b'future', 4),
|
||||
(b'quick', 5),
|
||||
(b'zerst\xf6rt', 6),
|
||||
(u'dreit\xe4gigen', 7),
|
||||
]
|
||||
|
||||
self.tree = OOBTree()
|
||||
for k, v in self.data:
|
||||
if isinstance(k, _bytes):
|
||||
k = k.decode('latin1')
|
||||
self.tree[k] = v
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testAllKeys(self):
|
||||
# check every item of the tree
|
||||
from BTrees._compat import _bytes
|
||||
for k, v in self.data:
|
||||
if isinstance(k, _bytes):
|
||||
k = k.decode(encoding)
|
||||
self.assertTrue(k in self.tree)
|
||||
self.assertEqual(self.tree[k], v)
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testUnicodeKeys(self):
|
||||
# try to access unicode keys in tree
|
||||
k, v = self.data[-1]
|
||||
self.assertEqual(k, self.s)
|
||||
self.assertEqual(self.tree[k], v)
|
||||
self.assertEqual(self.tree[self.s], v)
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testAsciiKeys(self):
|
||||
# try to access some "plain ASCII" keys in the tree
|
||||
for k, v in self.data[0], self.data[2]:
|
||||
self.assertTrue(isinstance(k, str))
|
||||
self.assertEqual(self.tree[k], v)
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(TestBTreesUnicode)
|
||||
@@ -0,0 +1,573 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import _skip_wo_ZODB
|
||||
from .common import ConflictTestBase
|
||||
|
||||
|
||||
class NastyConfictFunctionalTests(ConflictTestBase, unittest.TestCase):
|
||||
# FUNCTESTS: Provoke various conflict scenarios using ZODB + transaction
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
def openDB(self):
|
||||
# The conflict tests tend to open two or more connections
|
||||
# and then try to commit them. A standard FileStorage
|
||||
# is not MVCC aware, and so each connection would have the same
|
||||
# instance of the storage, leading to the error
|
||||
# "Duplicate tpc_begin calls for same transaction" on commit;
|
||||
# thus we use a MVCCMappingStorage for these tests, ensuring each
|
||||
# connection has its own storage.
|
||||
# Unfortunately, it wants to acquire the identically same
|
||||
# non-recursive lock in each of its *its* tpc_* methods, which deadlocks.
|
||||
# The solution is to give each instance its own lock, and trust in the
|
||||
# serialization (ordering) of the datamanager, and the fact that these tests are
|
||||
# single-threaded.
|
||||
import threading
|
||||
from ZODB.tests.MVCCMappingStorage import MVCCMappingStorage
|
||||
class _MVCCMappingStorage(MVCCMappingStorage):
|
||||
def new_instance(self):
|
||||
inst = MVCCMappingStorage.new_instance(self)
|
||||
inst._commit_lock = threading.Lock()
|
||||
return inst
|
||||
from ZODB.DB import DB
|
||||
self.storage = _MVCCMappingStorage()
|
||||
self.db = DB(self.storage)
|
||||
return self.db
|
||||
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testSimpleConflict(self):
|
||||
# Invoke conflict resolution by committing a transaction and
|
||||
# catching a conflict in the storage.
|
||||
import transaction
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = t = self._makeOne()
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
list(copy) # unghostify
|
||||
|
||||
self.assertEqual(t._p_serial, copy._p_serial)
|
||||
|
||||
t.update({1:2, 2:3})
|
||||
transaction.commit()
|
||||
|
||||
copy.update({3:4})
|
||||
transaction.commit()
|
||||
|
||||
# This tests a problem that cropped up while trying to write
|
||||
# testBucketSplitConflict (below): conflict resolution wasn't
|
||||
# working at all in non-trivial cases. Symptoms varied from
|
||||
# strange complaints about pickling (despite that the test isn't
|
||||
# doing any *directly*), thru SystemErrors from Python and
|
||||
# AssertionErrors inside the BTree code.
|
||||
@_skip_wo_ZODB
|
||||
def testResolutionBlowsUp(self):
|
||||
import transaction
|
||||
b = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = b
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(b._p_serial, copy._p_serial)
|
||||
|
||||
b.update({1:2, 2:3})
|
||||
transaction.commit()
|
||||
|
||||
copy.update({3:4})
|
||||
transaction.commit() # if this doesn't blow up
|
||||
list(copy.values()) # and this doesn't either, then fine
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testBucketSplitConflict(self):
|
||||
# Tests that a bucket split is viewed as a conflict.
|
||||
# It's (almost necessarily) a white-box test, and sensitive to
|
||||
# implementation details.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, add 16 new keys to bucket1, to force a bucket
|
||||
# split.
|
||||
b = orig
|
||||
numtoadd = 16
|
||||
candidate = 60
|
||||
while numtoadd:
|
||||
if candidate not in b:
|
||||
b[candidate] = candidate
|
||||
numtoadd -= 1
|
||||
candidate += 1
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 61 .. 74
|
||||
# bucket 2 has 16 values: [75, 76 .. 81] + [84, 88 ..116]
|
||||
# bucket 3 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((b0, 60, b1, 75, b2, 120, b3), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state) , 2)
|
||||
self.assertEqual(len(state[0]), 7)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 75)
|
||||
self.assertEqual(state[0][5], 120)
|
||||
|
||||
tm1.commit()
|
||||
|
||||
# In the other transaction, add 3 values near the tail end of bucket1.
|
||||
# This doesn't cause a split.
|
||||
b = copy
|
||||
for i in range(112, 116):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 18 values: 60, 64 .. 112, 113, 114, 115, 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testEmptyBucketConflict(self):
|
||||
# Tests that an emptied bucket *created by* conflict resolution is
|
||||
# viewed as a conflict: conflict resolution doesn't have enough
|
||||
# info to unlink the empty bucket from the BTree correctly.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, delete half of bucket 1.
|
||||
b = orig
|
||||
for k in 60, 64, 68, 72, 76, 80, 84, 88:
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 7 values: 92, 96, 100, 104, 108, 112, 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state) , 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 92)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
tm1.commit()
|
||||
|
||||
# In the other transaction, delete the other half of bucket 1.
|
||||
b = copy
|
||||
for k in 92, 96, 100, 104, 108, 112, 116:
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 8 values: 60, 64, 68, 72, 76, 80, 84, 88
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Conflict resolution empties bucket1 entirely. This used to
|
||||
# create an "insane" BTree (a legit BTree cannot contain an empty
|
||||
# bucket -- it contains NULL pointers the BTree code doesn't
|
||||
# expect, and segfaults result).
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testEmptyBucketNoConflict(self):
|
||||
# Tests that a plain empty bucket (on input) is not viewed as a
|
||||
# conflict.
|
||||
import transaction
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = orig
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, just add a key.
|
||||
b = orig
|
||||
b[1] = 1
|
||||
# bucket 0 has 16 values: [0, 1] + [4, 8 .. 56]
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
transaction.commit()
|
||||
|
||||
# In the other transaction, delete bucket 2.
|
||||
b = copy
|
||||
for k in range(120, 200, 4):
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 3)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
|
||||
# This shouldn't create a ConflictError.
|
||||
transaction.commit()
|
||||
# And the resulting BTree shouldn't have internal damage.
|
||||
b._check()
|
||||
|
||||
# The snaky control flow in _bucket__p_resolveConflict ended up trying
|
||||
# to decref a NULL pointer if conflict resolution was fed 3 empty
|
||||
# buckets. http://collector.zope.org/Zope/553
|
||||
def testThreeEmptyBucketsNoSegfault(self):
|
||||
# Note that the conflict is raised by our C extension, rather than
|
||||
# indirectly via the storage, and hence is a more specialized type.
|
||||
# This test therefore does not require ZODB.
|
||||
from BTrees.Interfaces import BTreesConflictError
|
||||
t = self._makeOne()
|
||||
t[1] = 1
|
||||
bucket = t._firstbucket
|
||||
del t[1]
|
||||
state1 = bucket.__getstate__()
|
||||
state2 = bucket.__getstate__()
|
||||
state3 = bucket.__getstate__()
|
||||
self.assertTrue(state2 is not state1 and
|
||||
state2 is not state3 and
|
||||
state3 is not state1)
|
||||
self.assertTrue(state2 == state1 and
|
||||
state3 == state1)
|
||||
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
|
||||
state1, state2, state3)
|
||||
# When an empty BTree resolves conflicts, it computes the
|
||||
# bucket state as None, so...
|
||||
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
|
||||
None, None, None)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testCantResolveBTreeConflict(self):
|
||||
# Test that a conflict involving two different changes to
|
||||
# an internal BTree node is unresolvable. An internal node
|
||||
# only changes when there are enough additions or deletions
|
||||
# to a child bucket that the bucket is split or removed.
|
||||
# It's (almost necessarily) a white-box test, and sensitive to
|
||||
# implementation details.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Set up database connections to provoke conflict.
|
||||
self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = orig
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction should add enough keys to cause a split,
|
||||
# and another should remove all the keys in one bucket.
|
||||
|
||||
for k in range(200, 300, 4):
|
||||
orig[k] = k
|
||||
tm1.commit()
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del copy[k]
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testConflictWithOneEmptyBucket(self):
|
||||
# If one transaction empties a bucket, while another adds an item
|
||||
# to the bucket, all the changes "look resolvable": bucket conflict
|
||||
# resolution returns a bucket containing (only) the item added by
|
||||
# the latter transaction, but changes from the former transaction
|
||||
# removing the bucket are uncontested: the bucket is removed from
|
||||
# the BTree despite that resolution thinks it's non-empty! This
|
||||
# was first reported by Dieter Maurer, to zodb-dev on 22 Mar 2005.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Set up database connections to provoke conflict.
|
||||
self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = orig
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction empties the first bucket, and another adds a
|
||||
# key to the first bucket.
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del orig[k]
|
||||
tm1.commit()
|
||||
|
||||
copy[1] = 1
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
# Same thing, except commit the transactions in the opposite order.
|
||||
b = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(b._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction empties the first bucket, and another adds a
|
||||
# key to the first bucket.
|
||||
b[1] = 1
|
||||
tm1.commit()
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del copy[k]
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testConflictOfInsertAndDeleteOfFirstBucketItem(self):
|
||||
# Recently, BTrees became careful about removing internal keys
|
||||
# (keys in internal aka BTree nodes) when they were deleted from
|
||||
# buckets. This poses a problem for conflict resolution.
|
||||
|
||||
# We want to guard against a case in which the first key in a
|
||||
# bucket is removed in one transaction while a key is added
|
||||
# after that key but before the next key in another transaction
|
||||
# with the result that the added key is unreachable.
|
||||
|
||||
# original:
|
||||
|
||||
# Bucket(...), k1, Bucket((k1, v1), (k3, v3), ...)
|
||||
|
||||
# tran1
|
||||
|
||||
# Bucket(...), k3, Bucket(k3, v3), ...)
|
||||
|
||||
# tran2
|
||||
|
||||
# Bucket(...), k1, Bucket((k1, v1), (k2, v2), (k3, v3), ...)
|
||||
|
||||
# where k1 < k2 < k3
|
||||
|
||||
# We don't want:
|
||||
|
||||
# Bucket(...), k3, Bucket((k2, v2), (k3, v3), ...)
|
||||
|
||||
# as k2 would be unfindable, so we want a conflict.
|
||||
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
mytype = self._getTargetClass()
|
||||
db = self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
conn1 = db.open(tm1)
|
||||
conn1.root.t = t = mytype()
|
||||
for i in range(0, 200, 2):
|
||||
t[i] = i
|
||||
tm1.commit()
|
||||
k = t.__getstate__()[0][1]
|
||||
assert t.__getstate__()[0][2].keys()[0] == k
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
conn2 = db.open(tm2)
|
||||
|
||||
t[k+1] = k+1
|
||||
del conn2.root.t[k]
|
||||
for i in range(200,300):
|
||||
conn2.root.t[i] = i
|
||||
|
||||
tm1.commit()
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
tm2.abort()
|
||||
|
||||
k = t.__getstate__()[0][1]
|
||||
t[k+1] = k+1
|
||||
del conn2.root.t[k]
|
||||
|
||||
tm2.commit()
|
||||
self.assertRaises(ConflictError, tm1.commit)
|
||||
tm1.abort()
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(NastyConfictFunctionalTests),
|
||||
))
|
||||
@@ -0,0 +1,378 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree
|
||||
|
||||
|
||||
class IFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy
|
||||
|
||||
|
||||
class IFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucket
|
||||
return IFBucket
|
||||
|
||||
|
||||
class IFBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
return IFBucketPy
|
||||
|
||||
|
||||
class IFTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet
|
||||
|
||||
|
||||
class IFSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return IFSetPy
|
||||
|
||||
|
||||
class IFBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
|
||||
class IFBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
if using64bits:
|
||||
|
||||
class IFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
class IFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class _TestIFBTreesBase(object):
|
||||
|
||||
def testNonIntegerKeyRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraiseskey)
|
||||
self.assertRaises(TypeError, self._floatraiseskey)
|
||||
self.assertRaises(TypeError, self._noneraiseskey)
|
||||
|
||||
def testNonNumericValueRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraisesvalue)
|
||||
self.assertRaises(TypeError, self._noneraisesvalue)
|
||||
self._makeOne()[1] = 1
|
||||
self._makeOne()[1] = 1.0
|
||||
|
||||
def _stringraiseskey(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraiseskey(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraiseskey(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def _stringraisesvalue(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraisesvalue(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraisesvalue(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
|
||||
class TestIFBTrees(_TestIFBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
|
||||
class TestIFBTreesPy(_TestIFBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
|
||||
class TestIFMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IFBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IFBTree import IFSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IFBTree import IFTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IFBTree import IFBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IFBTree import IFBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIFMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IFBTree import IFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IFBTree import IFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IFBTree import IFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IFBTree import IFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureIF(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IFBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IFBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
from BTrees.IFBTree import IFBucket
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet, IFTreeSet, makeBuilder(IFBTree), makeBuilder(IFBucket)
|
||||
|
||||
|
||||
class PureIFPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IFBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IFBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return (IFSetPy, IFTreeSetPy,
|
||||
makeBuilder(IFBTreePy), makeBuilder(IFBucketPy))
|
||||
|
||||
|
||||
class IFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree
|
||||
|
||||
|
||||
class IFBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy
|
||||
|
||||
|
||||
class IFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucket
|
||||
return IFBucket
|
||||
|
||||
|
||||
class IFBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
return IFBucketPy
|
||||
|
||||
|
||||
class IFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet
|
||||
|
||||
|
||||
class IFSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return IFSetPy
|
||||
|
||||
|
||||
class IFModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'IF'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IFBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerFloatBTreeModule
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IFBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IFBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IFTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IFTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IFBucketTest),
|
||||
unittest.makeSuite(IFBucketPyTest),
|
||||
unittest.makeSuite(IFTreeSetTest),
|
||||
unittest.makeSuite(IFTreeSetPyTest),
|
||||
unittest.makeSuite(IFSetTest),
|
||||
unittest.makeSuite(IFSetPyTest),
|
||||
unittest.makeSuite(IFBTreeTest),
|
||||
unittest.makeSuite(IFBTreePyTest),
|
||||
unittest.makeSuite(TestIFBTrees),
|
||||
unittest.makeSuite(TestIFBTreesPy),
|
||||
unittest.makeSuite(TestIFMultiUnion),
|
||||
unittest.makeSuite(TestIFMultiUnionPy),
|
||||
unittest.makeSuite(PureIF),
|
||||
unittest.makeSuite(PureIFPy),
|
||||
unittest.makeSuite(IFBTreeConflictTests),
|
||||
unittest.makeSuite(IFBTreePyConflictTests),
|
||||
unittest.makeSuite(IFBucketConflictTests),
|
||||
unittest.makeSuite(IFBucketPyConflictTests),
|
||||
unittest.makeSuite(IFTreeSetConflictTests),
|
||||
unittest.makeSuite(IFTreeSetPyConflictTests),
|
||||
unittest.makeSuite(IFSetConflictTests),
|
||||
unittest.makeSuite(IFSetPyConflictTests),
|
||||
unittest.makeSuite(IFModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,507 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree
|
||||
|
||||
|
||||
class IIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy
|
||||
|
||||
|
||||
class IITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IIBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucket
|
||||
return IIBucket
|
||||
|
||||
|
||||
class IIBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
return IIBucketPy
|
||||
|
||||
|
||||
class IITreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IISetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet
|
||||
|
||||
|
||||
class IISetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy
|
||||
|
||||
|
||||
class _IIBTreeTestBase(BTreeTests):
|
||||
|
||||
def testIIBTreeOverflow(self):
|
||||
good = set()
|
||||
b = self._makeOne()
|
||||
|
||||
def trial(i):
|
||||
i = int(i)
|
||||
try:
|
||||
b[i] = 0
|
||||
except OverflowError:
|
||||
self.assertRaises(OverflowError, b.__setitem__, 0, i)
|
||||
except TypeError:
|
||||
self.assertRaises(TypeError, b.__setitem__, 0, i)
|
||||
else:
|
||||
good.add(i)
|
||||
b[0] = i
|
||||
self.assertEqual(b[0], i)
|
||||
|
||||
for i in range((1<<31) - 3, (1<<31) + 3):
|
||||
trial(i)
|
||||
trial(-i)
|
||||
|
||||
del b[0]
|
||||
self.assertEqual(sorted(good), sorted(b))
|
||||
|
||||
|
||||
class IIBTreeTest(_IIBTreeTestBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
|
||||
class IIBTreeTestPy(_IIBTreeTestBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class _TestIIBTreesBase(object):
|
||||
|
||||
def testNonIntegerKeyRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraiseskey)
|
||||
self.assertRaises(TypeError, self._floatraiseskey)
|
||||
self.assertRaises(TypeError, self._noneraiseskey)
|
||||
|
||||
def testNonIntegerValueRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraisesvalue)
|
||||
self.assertRaises(TypeError, self._floatraisesvalue)
|
||||
self.assertRaises(TypeError, self._noneraisesvalue)
|
||||
|
||||
def _stringraiseskey(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraiseskey(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraiseskey(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def _stringraisesvalue(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraisesvalue(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraisesvalue(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
|
||||
class TestIIBTrees(_TestIIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
|
||||
class TestIIBTreesPy(_TestIIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
|
||||
class TestIISets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet()
|
||||
|
||||
|
||||
class TestIISetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy()
|
||||
|
||||
|
||||
class TestIITreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet()
|
||||
|
||||
|
||||
class TestIITreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy()
|
||||
|
||||
|
||||
class PureII(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IIBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IIBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
from BTrees.IIBTree import IIBucket
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet, IITreeSet, makeBuilder(IIBTree), makeBuilder(IIBucket)
|
||||
|
||||
|
||||
class PureIIPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IIBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IIBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return (IISetPy, IITreeSetPy,
|
||||
makeBuilder(IIBTreePy), makeBuilder(IIBucketPy))
|
||||
|
||||
|
||||
class TestIIMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IIBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IIBTree import IISet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IIBTree import IITreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IIBTree import IIBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIIMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IIBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IIBTree import IISetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IIBTree import IITreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IIBTree import IIBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestWeightedII(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.IIBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.IIBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.IIBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.IIBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
from BTrees.IIBTree import IIBucket
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.IIBTree import IISet
|
||||
return IIBucket, IIBTree, itemsToSet(IISet), itemsToSet(IITreeSet)
|
||||
|
||||
|
||||
class TestWeightedIIPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.IIBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.IIBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.IIBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return (IIBucketPy, IIBTreePy,
|
||||
itemsToSet(IISetPy), itemsToSet(IITreeSetPy))
|
||||
|
||||
|
||||
class IIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree
|
||||
|
||||
|
||||
class IIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy
|
||||
|
||||
|
||||
class IIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucket
|
||||
return IIBucket
|
||||
|
||||
|
||||
class IIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
return IIBucketPy
|
||||
|
||||
|
||||
class IITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IISetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet
|
||||
|
||||
|
||||
class IISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy
|
||||
|
||||
|
||||
class IIModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'II'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IIBTree
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerIntegerBTreeModule
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IIBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IIBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IITreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IITreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IIBucketTest),
|
||||
unittest.makeSuite(IIBucketPyTest),
|
||||
unittest.makeSuite(IITreeSetTest),
|
||||
unittest.makeSuite(IITreeSetPyTest),
|
||||
unittest.makeSuite(IISetTest),
|
||||
unittest.makeSuite(IISetPyTest),
|
||||
unittest.makeSuite(IIBTreeTest),
|
||||
unittest.makeSuite(IIBTreeTestPy),
|
||||
unittest.makeSuite(TestIIBTrees),
|
||||
unittest.makeSuite(TestIIBTreesPy),
|
||||
unittest.makeSuite(TestIISets),
|
||||
unittest.makeSuite(TestIISetsPy),
|
||||
unittest.makeSuite(TestIITreeSets),
|
||||
unittest.makeSuite(TestIITreeSetsPy),
|
||||
unittest.makeSuite(TestIIMultiUnion),
|
||||
unittest.makeSuite(TestIIMultiUnionPy),
|
||||
unittest.makeSuite(PureII),
|
||||
unittest.makeSuite(PureIIPy),
|
||||
unittest.makeSuite(TestWeightedII),
|
||||
unittest.makeSuite(TestWeightedIIPy),
|
||||
unittest.makeSuite(IIBTreeConflictTests),
|
||||
unittest.makeSuite(IIBTreeConflictTestsPy),
|
||||
unittest.makeSuite(IIBucketConflictTests),
|
||||
unittest.makeSuite(IIBucketConflictTestsPy),
|
||||
unittest.makeSuite(IITreeSetConflictTests),
|
||||
unittest.makeSuite(IITreeSetConflictTestsPy),
|
||||
unittest.makeSuite(IISetConflictTests),
|
||||
unittest.makeSuite(IISetConflictTestsPy),
|
||||
unittest.makeSuite(IIModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,414 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TypeTest
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree
|
||||
|
||||
|
||||
class IOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy
|
||||
|
||||
|
||||
class IOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucket
|
||||
return IOBucket
|
||||
|
||||
|
||||
class IOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
return IOBucketPy
|
||||
|
||||
|
||||
class IOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet
|
||||
|
||||
|
||||
class IOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy
|
||||
|
||||
|
||||
class IOBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class IOBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
|
||||
class IOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class IOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
class _TestIOBTreesBase(TypeTest):
|
||||
|
||||
def _stringraises(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraises(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraises(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def testStringAllowedInContains(self):
|
||||
self.assertFalse('key' in self._makeOne())
|
||||
|
||||
def testStringKeyRaisesKeyErrorWhenMissing(self):
|
||||
self.assertRaises(KeyError, self._makeOne().__getitem__, 'key')
|
||||
|
||||
def testStringKeyReturnsDefaultFromGetWhenMissing(self):
|
||||
self.assertEqual(self._makeOne().get('key', 42), 42)
|
||||
|
||||
class TestIOBTrees(_TestIOBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class TestIOBTreesPy(_TestIOBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
class TestIOSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet()
|
||||
|
||||
|
||||
class TestIOSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy()
|
||||
|
||||
|
||||
class TestIOTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet()
|
||||
|
||||
|
||||
class TestIOTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy()
|
||||
|
||||
|
||||
class PureIO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from BTrees.IOBTree import IOBucket
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet, IOTreeSet, makeBuilder(IOBTree), makeBuilder(IOBucket)
|
||||
|
||||
|
||||
class PureIOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return (IOSetPy, IOTreeSetPy,
|
||||
makeBuilder(IOBTreePy), makeBuilder(IOBucketPy))
|
||||
|
||||
|
||||
class TestIOMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IOBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IOBTree import IOSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IOBTree import IOTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IOBTree import IOBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IOBTree import IOBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIOMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IOBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IOBTree import IOSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IOBTree import IOTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IOBTree import IOBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IOBTree import IOBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class IOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree
|
||||
|
||||
|
||||
class IOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy
|
||||
|
||||
|
||||
class IOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucket
|
||||
return IOBucket
|
||||
|
||||
|
||||
class IOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
return IOBucketPy
|
||||
|
||||
|
||||
class IOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet
|
||||
|
||||
|
||||
class IOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy
|
||||
|
||||
|
||||
class IOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'IO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.IOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("IOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.IOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("IOBTree shouldn't have weightedIntersection")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IOBucketTest),
|
||||
unittest.makeSuite(IOBucketPyTest),
|
||||
unittest.makeSuite(IOTreeSetTest),
|
||||
unittest.makeSuite(IOTreeSetPyTest),
|
||||
unittest.makeSuite(IOSetTest),
|
||||
unittest.makeSuite(IOSetPyTest),
|
||||
unittest.makeSuite(IOBTreeTest),
|
||||
unittest.makeSuite(IOBTreePyTest),
|
||||
unittest.makeSuite(TestIOBTrees),
|
||||
unittest.makeSuite(TestIOBTreesPy),
|
||||
unittest.makeSuite(TestIOSets),
|
||||
unittest.makeSuite(TestIOSetsPy),
|
||||
unittest.makeSuite(TestIOTreeSets),
|
||||
unittest.makeSuite(TestIOTreeSetsPy),
|
||||
unittest.makeSuite(TestIOMultiUnion),
|
||||
unittest.makeSuite(TestIOMultiUnionPy),
|
||||
unittest.makeSuite(PureIO),
|
||||
unittest.makeSuite(PureIOPy),
|
||||
unittest.makeSuite(IOBTreeConflictTests),
|
||||
unittest.makeSuite(IOBTreeConflictTestsPy),
|
||||
unittest.makeSuite(IOBucketConflictTests),
|
||||
unittest.makeSuite(IOBucketConflictTestsPy),
|
||||
unittest.makeSuite(IOTreeSetConflictTests),
|
||||
unittest.makeSuite(IOTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(IOSetConflictTests),
|
||||
unittest.makeSuite(IOSetConflictTestsPy),
|
||||
unittest.makeSuite(IOModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,316 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree
|
||||
|
||||
|
||||
class LFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy
|
||||
|
||||
|
||||
class LFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucket
|
||||
return LFBucket
|
||||
|
||||
|
||||
class LFBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
return LFBucketPy
|
||||
|
||||
|
||||
class LFTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet
|
||||
|
||||
|
||||
class LFSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return LFSetPy
|
||||
|
||||
|
||||
class LFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class LFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class TestLFMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LFBTree import LFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LFBTree import LFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LFBTree import LFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LFBTree import LFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLFMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LFBTree import LFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LFBTree import LFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LFBTree import LFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LFBTree import LFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureLF(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LFBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LFBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
from BTrees.LFBTree import LFBucket
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet, LFTreeSet, makeBuilder(LFBTree), makeBuilder(LFBucket)
|
||||
|
||||
|
||||
class PureLFPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LFBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LFBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return (LFSetPy, LFTreeSetPy,
|
||||
makeBuilder(LFBTreePy), makeBuilder(LFBucketPy))
|
||||
|
||||
|
||||
class LFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree
|
||||
|
||||
|
||||
class LFBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy
|
||||
|
||||
|
||||
class LFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucket
|
||||
return LFBucket
|
||||
|
||||
|
||||
class LFBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
return LFBucketPy
|
||||
|
||||
|
||||
class LFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet
|
||||
|
||||
|
||||
class LFSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return LFSetPy
|
||||
|
||||
|
||||
class LFModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LF'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LFBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerFloatBTreeModule
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LFBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LFBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(LFTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LFTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(LFBucketTest),
|
||||
unittest.makeSuite(LFBucketPyTest),
|
||||
unittest.makeSuite(LFTreeSetTest),
|
||||
unittest.makeSuite(LFTreeSetPyTest),
|
||||
unittest.makeSuite(LFSetTest),
|
||||
unittest.makeSuite(LFSetPyTest),
|
||||
unittest.makeSuite(LFBTreeTest),
|
||||
unittest.makeSuite(LFBTreePyTest),
|
||||
unittest.makeSuite(TestLFMultiUnion),
|
||||
unittest.makeSuite(TestLFMultiUnionPy),
|
||||
unittest.makeSuite(PureLF),
|
||||
unittest.makeSuite(PureLFPy),
|
||||
unittest.makeSuite(LFBTreeConflictTests),
|
||||
unittest.makeSuite(LFBTreeConflictTestsPy),
|
||||
unittest.makeSuite(LFBucketConflictTests),
|
||||
unittest.makeSuite(LFBucketConflictTestsPy),
|
||||
unittest.makeSuite(LFTreeSetConflictTests),
|
||||
unittest.makeSuite(LFTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(LFSetConflictTests),
|
||||
unittest.makeSuite(LFSetConflictTestsPy),
|
||||
unittest.makeSuite(LFModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,415 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree
|
||||
|
||||
|
||||
class LLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy
|
||||
|
||||
|
||||
class LLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucket
|
||||
return LLBucket
|
||||
|
||||
|
||||
class LLBucketTestPy(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
return LLBucketPy
|
||||
|
||||
|
||||
class LLTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetTestPy(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet
|
||||
|
||||
|
||||
class LLSetTestPy(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy
|
||||
|
||||
|
||||
class LLBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree()
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class LLBTreeTestPy(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy()
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class TestLLSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet()
|
||||
|
||||
|
||||
class TestLLSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy()
|
||||
|
||||
|
||||
class TestLLTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet()
|
||||
|
||||
|
||||
class TestLLTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy()
|
||||
|
||||
|
||||
class PureLL(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LLBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LLBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
from BTrees.LLBTree import LLBucket
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet, LLTreeSet, makeBuilder(LLBTree), makeBuilder(LLBucket)
|
||||
|
||||
|
||||
class PureLLPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LLBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LLBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return (LLSetPy, LLTreeSetPy,
|
||||
makeBuilder(LLBTreePy), makeBuilder(LLBucketPy))
|
||||
|
||||
|
||||
class TestLLMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LLBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LLBTree import LLSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LLBTree import LLTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LLBTree import LLBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLLMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LLBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LLBTree import LLSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LLBTree import LLTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LLBTree import LLBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestWeightedLL(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.LLBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.LLBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.LLBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.LLBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
from BTrees.LLBTree import LLBucket
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLBucket, LLBTree, itemsToSet(LLSet), itemsToSet(LLTreeSet)
|
||||
|
||||
|
||||
class TestWeightedLLPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.LLBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.LLBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.LLBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return (LLBucketPy, LLBTreePy,
|
||||
itemsToSet(LLSetPy), itemsToSet(LLTreeSetPy))
|
||||
|
||||
|
||||
class LLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree
|
||||
|
||||
|
||||
class LLBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy
|
||||
|
||||
|
||||
class LLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucket
|
||||
return LLBucket
|
||||
|
||||
|
||||
class LLBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
return LLBucketPy
|
||||
|
||||
|
||||
class LLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet
|
||||
|
||||
|
||||
class LLSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy
|
||||
|
||||
|
||||
class LLModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LL'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LLBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerIntegerBTreeModule
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LLBucketTest),
|
||||
unittest.makeSuite(LLBucketTest),
|
||||
unittest.makeSuite(LLTreeSetTest),
|
||||
unittest.makeSuite(LLTreeSetTest),
|
||||
unittest.makeSuite(LLSetTest),
|
||||
unittest.makeSuite(LLSetTest),
|
||||
unittest.makeSuite(LLBTreeTest),
|
||||
unittest.makeSuite(LLBTreeTest),
|
||||
unittest.makeSuite(TestLLSets),
|
||||
unittest.makeSuite(TestLLSets),
|
||||
unittest.makeSuite(TestLLTreeSets),
|
||||
unittest.makeSuite(TestLLTreeSets),
|
||||
unittest.makeSuite(TestLLMultiUnion),
|
||||
unittest.makeSuite(TestLLMultiUnion),
|
||||
unittest.makeSuite(PureLL),
|
||||
unittest.makeSuite(PureLL),
|
||||
unittest.makeSuite(TestWeightedLL),
|
||||
unittest.makeSuite(TestWeightedLL),
|
||||
unittest.makeSuite(LLBTreeConflictTests),
|
||||
unittest.makeSuite(LLBTreeConflictTests),
|
||||
unittest.makeSuite(LLBucketConflictTests),
|
||||
unittest.makeSuite(LLBucketConflictTests),
|
||||
unittest.makeSuite(LLTreeSetConflictTests),
|
||||
unittest.makeSuite(LLTreeSetConflictTests),
|
||||
unittest.makeSuite(LLSetConflictTests),
|
||||
unittest.makeSuite(LLSetConflictTests),
|
||||
unittest.makeSuite(LLModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,359 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree
|
||||
|
||||
|
||||
class LOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy
|
||||
|
||||
|
||||
class LOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucket
|
||||
return LOBucket
|
||||
|
||||
|
||||
class LOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
return LOBucketPy
|
||||
|
||||
|
||||
class LOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet
|
||||
|
||||
|
||||
class LOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy
|
||||
|
||||
|
||||
class LOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree()
|
||||
|
||||
|
||||
class LOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy()
|
||||
|
||||
|
||||
class TestLOSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet()
|
||||
|
||||
|
||||
class TestLOSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy()
|
||||
|
||||
|
||||
class TestLOTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet()
|
||||
|
||||
|
||||
class TestLOTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy()
|
||||
|
||||
|
||||
class TestLOMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LOBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LOBTree import LOSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LOBTree import LOTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LOBTree import LOBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LOBTree import LOBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLOMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LOBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LOBTree import LOSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LOBTree import LOTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LOBTree import LOBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LOBTree import LOBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureLO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
from BTrees.LOBTree import LOBucket
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet, LOTreeSet, makeBuilder(LOBTree), makeBuilder(LOBucket)
|
||||
|
||||
|
||||
class PureLOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return (LOSetPy, LOTreeSetPy,
|
||||
makeBuilder(LOBTreePy), makeBuilder(LOBucketPy))
|
||||
|
||||
|
||||
class LOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree
|
||||
|
||||
|
||||
class LOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy
|
||||
|
||||
|
||||
class LOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucket
|
||||
return LOBucket
|
||||
|
||||
|
||||
class LOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
return LOBucketPy
|
||||
|
||||
|
||||
class LOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet
|
||||
|
||||
|
||||
class LOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy
|
||||
|
||||
|
||||
class LOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.LOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("LOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.LOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("LOBTree shouldn't have weightedIntersection")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(LOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(LOBucketTest),
|
||||
unittest.makeSuite(LOBucketPyTest),
|
||||
unittest.makeSuite(LOTreeSetTest),
|
||||
unittest.makeSuite(LOTreeSetPyTest),
|
||||
unittest.makeSuite(LOSetTest),
|
||||
unittest.makeSuite(LOSetPyTest),
|
||||
unittest.makeSuite(LOBTreeTest),
|
||||
unittest.makeSuite(LOBTreePyTest),
|
||||
unittest.makeSuite(TestLOSets),
|
||||
unittest.makeSuite(TestLOSetsPy),
|
||||
unittest.makeSuite(TestLOTreeSets),
|
||||
unittest.makeSuite(TestLOTreeSetsPy),
|
||||
unittest.makeSuite(TestLOMultiUnion),
|
||||
unittest.makeSuite(TestLOMultiUnionPy),
|
||||
unittest.makeSuite(PureLO),
|
||||
unittest.makeSuite(PureLOPy),
|
||||
unittest.makeSuite(LOBTreeConflictTests),
|
||||
unittest.makeSuite(LOBTreeConflictTestsPy),
|
||||
unittest.makeSuite(LOBucketConflictTests),
|
||||
unittest.makeSuite(LOBucketConflictTestsPy),
|
||||
unittest.makeSuite(LOTreeSetConflictTests),
|
||||
unittest.makeSuite(LOTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(LOSetConflictTests),
|
||||
unittest.makeSuite(LOSetConflictTestsPy),
|
||||
unittest.makeSuite(LOModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2008 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
_marker = object()
|
||||
|
||||
class LengthTestCase(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.Length import Length
|
||||
return Length
|
||||
|
||||
def _makeOne(self, value=_marker):
|
||||
if value is _marker:
|
||||
return self._getTargetClass()()
|
||||
return self._getTargetClass()(value)
|
||||
|
||||
def test_ctor_default(self):
|
||||
length = self._makeOne()
|
||||
self.assertEqual(length.value, 0)
|
||||
|
||||
def test_ctor_explict(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test___getstate___(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length.__getstate__(), 42)
|
||||
|
||||
def test___setstate__(self):
|
||||
length = self._makeOne()
|
||||
length.__setstate__(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test_set(self):
|
||||
length = self._makeOne()
|
||||
length.set(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test__p_resolveConflict(self):
|
||||
length = self._makeOne()
|
||||
self.assertEqual(length._p_resolveConflict(5, 7, 9), 11)
|
||||
|
||||
def test_change_w_positive_delta(self):
|
||||
length = self._makeOne()
|
||||
length.change(3)
|
||||
self.assertEqual(length.value, 3)
|
||||
|
||||
def test_change_w_negative_delta(self):
|
||||
length = self._makeOne()
|
||||
length.change(-3)
|
||||
self.assertEqual(length.value, -3)
|
||||
|
||||
def test_change_overflows_to_long(self):
|
||||
import sys
|
||||
try:
|
||||
length = self._makeOne(sys.maxint)
|
||||
except AttributeError: #pragma NO COVER Py3k
|
||||
return
|
||||
else: #pragma NO COVER Py2
|
||||
self.assertEqual(length(), sys.maxint)
|
||||
self.assertTrue(type(length()) is int)
|
||||
length.change(+1)
|
||||
self.assertEqual(length(), sys.maxint + 1)
|
||||
self.assertTrue(type(length()) is long)
|
||||
|
||||
def test_change_underflows_to_long(self):
|
||||
import sys
|
||||
try:
|
||||
minint = (-sys.maxint) - 1
|
||||
except AttributeError: #pragma NO COVER Py3k
|
||||
return
|
||||
else: #pragma NO COVER Py2
|
||||
length = self._makeOne(minint)
|
||||
self.assertEqual(length(), minint)
|
||||
self.assertTrue(type(length()) is int)
|
||||
length.change(-1)
|
||||
self.assertEqual(length(), minint - 1)
|
||||
self.assertTrue(type(length()) is long)
|
||||
|
||||
def test___call___no_args(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length(), 42)
|
||||
|
||||
def test___call___w_args(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length(0, None, (), [], {}), 42)
|
||||
|
||||
def test_lp_516653(self):
|
||||
# Test for https://bugs.launchpad.net/zodb/+bug/516653
|
||||
import copy
|
||||
length = self._makeOne()
|
||||
other = copy.copy(length)
|
||||
self.assertEqual(other(), 0)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(LengthTestCase)
|
||||
@@ -0,0 +1,383 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntValues
|
||||
from .common import TypeTest
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but necessary
|
||||
|
||||
|
||||
class OIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree
|
||||
|
||||
|
||||
class OIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy
|
||||
|
||||
|
||||
class OITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OIBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucket
|
||||
return OIBucket
|
||||
|
||||
|
||||
class OIBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
return OIBucketPy
|
||||
|
||||
|
||||
class OITreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OISetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet
|
||||
|
||||
|
||||
class OISetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return OISetPy
|
||||
|
||||
|
||||
class OIBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
|
||||
|
||||
class OIBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
class OIBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
def getTwoKeys(self):
|
||||
return object(), object()
|
||||
|
||||
class OIBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
def getTwoKeys(self):
|
||||
return object(), object()
|
||||
|
||||
|
||||
class _TestOIBTreesBase(TypeTest):
|
||||
|
||||
def _stringraises(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraises(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraises(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
def testEmptyFirstBucketReportedByGuido(self):
|
||||
from .._compat import xrange
|
||||
b = self._makeOne()
|
||||
for i in xrange(29972): # reduce to 29971 and it works
|
||||
b[i] = i
|
||||
for i in xrange(30): # reduce to 29 and it works
|
||||
del b[i]
|
||||
b[i+40000] = i
|
||||
|
||||
self.assertEqual(b.keys()[0], 30)
|
||||
|
||||
|
||||
class TestOIBTrees(_TestOIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
|
||||
|
||||
class TestOIBTreesPy(_TestOIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
|
||||
|
||||
class PureOI(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OIBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OIBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
from BTrees.OIBTree import OIBucket
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet, OITreeSet, makeBuilder(OIBTree), makeBuilder(OIBucket)
|
||||
|
||||
|
||||
class PureOIPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OIBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OIBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return (OISetPy, OITreeSetPy,
|
||||
makeBuilder(OIBTreePy), makeBuilder(OIBucketPy))
|
||||
|
||||
|
||||
class TestWeightedOI(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OIBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OIBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.OIBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OIBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OIBTree import OIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
from BTrees.OIBTree import OIBucket
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
from BTrees.OIBTree import OISet
|
||||
return OIBucket, OIBTree, itemsToSet(OISet), itemsToSet(OITreeSet)
|
||||
|
||||
|
||||
class TestWeightedOIPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OIBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OIBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.OIBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OIBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OIBTree import OIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return (OIBucketPy, OIBTreePy,
|
||||
itemsToSet(OISetPy), itemsToSet(OITreeSetPy))
|
||||
|
||||
|
||||
class OIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucket
|
||||
return OIBucket
|
||||
|
||||
|
||||
class OIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
return OIBucketPy
|
||||
|
||||
|
||||
class OISetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet
|
||||
|
||||
|
||||
class OISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return OISetPy
|
||||
|
||||
|
||||
class OIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree
|
||||
|
||||
|
||||
class OIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy
|
||||
|
||||
|
||||
class OITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OIModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OI'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OIBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectIntegerBTreeModule
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OIBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OIBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OIBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OIBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OITreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OITreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OIBucketTest),
|
||||
unittest.makeSuite(OIBucketPyTest),
|
||||
unittest.makeSuite(OITreeSetTest),
|
||||
unittest.makeSuite(OITreeSetPyTest),
|
||||
unittest.makeSuite(OISetTest),
|
||||
unittest.makeSuite(OISetPyTest),
|
||||
unittest.makeSuite(OIBTreeTest),
|
||||
unittest.makeSuite(OIBTreePyTest),
|
||||
unittest.makeSuite(TestOIBTrees),
|
||||
unittest.makeSuite(TestOIBTreesPy),
|
||||
unittest.makeSuite(PureOI),
|
||||
unittest.makeSuite(PureOIPy),
|
||||
unittest.makeSuite(TestWeightedOI),
|
||||
unittest.makeSuite(TestWeightedOIPy),
|
||||
unittest.makeSuite(OIBucketConflictTests),
|
||||
unittest.makeSuite(OIBucketConflictTestsPy),
|
||||
unittest.makeSuite(OISetConflictTests),
|
||||
unittest.makeSuite(OISetConflictTestsPy),
|
||||
unittest.makeSuite(OIBTreeConflictTests),
|
||||
unittest.makeSuite(OIBTreeConflictTestsPy),
|
||||
unittest.makeSuite(OITreeSetConflictTests),
|
||||
unittest.makeSuite(OITreeSetConflictTestsPy),
|
||||
unittest.makeSuite(OIModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,348 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from .common import _skip_on_32_bits
|
||||
|
||||
|
||||
class OLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree
|
||||
|
||||
|
||||
class OLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy
|
||||
|
||||
|
||||
class OLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucket
|
||||
return OLBucket
|
||||
|
||||
|
||||
class OLBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
return OLBucketPy
|
||||
|
||||
|
||||
class OLTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet
|
||||
|
||||
|
||||
class OLSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return OLSetPy
|
||||
|
||||
|
||||
class OLBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree()
|
||||
|
||||
def getTwoKeys(self):
|
||||
return "abc", "def"
|
||||
|
||||
@_skip_on_32_bits
|
||||
def test_extremes(self):
|
||||
from BTrees.tests.common import SMALLEST_64_BITS
|
||||
from BTrees.tests.common import SMALLEST_POSITIVE_65_BITS
|
||||
from BTrees.tests.common import LARGEST_64_BITS
|
||||
from BTrees.tests.common import LARGEST_NEGATIVE_65_BITS
|
||||
btree = self._makeOne()
|
||||
btree['ZERO'] = 0
|
||||
btree['SMALLEST_64_BITS'] = SMALLEST_64_BITS
|
||||
btree['LARGEST_64_BITS'] = LARGEST_64_BITS
|
||||
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
|
||||
'SMALLEST_POSITIVE_65_BITS', SMALLEST_POSITIVE_65_BITS)
|
||||
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
|
||||
'LARGEST_NEGATIVE_65_BITS', LARGEST_NEGATIVE_65_BITS)
|
||||
|
||||
|
||||
class OLBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy()
|
||||
|
||||
def getTwoKeys(self):
|
||||
return "abc", "def"
|
||||
|
||||
|
||||
class PureOL(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OLBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OLBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
from BTrees.OLBTree import OLBucket
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet, OLTreeSet, makeBuilder(OLBTree), makeBuilder(OLBucket)
|
||||
|
||||
|
||||
class PureOLPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OLBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OLBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return (OLSetPy, OLTreeSetPy,
|
||||
makeBuilder(OLBTreePy), makeBuilder(OLBucketPy))
|
||||
|
||||
|
||||
class TestWeightedOL(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OLBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OLBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.OLBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OLBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OLBTree import OLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
from BTrees.OLBTree import OLBucket
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLBucket, OLBTree, itemsToSet(OLSet), itemsToSet(OLTreeSet)
|
||||
|
||||
|
||||
class TestWeightedOLPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OLBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OLBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.OLBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OLBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OLBTree import OLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return (OLBucketPy, OLBTreePy,
|
||||
itemsToSet(OLSetPy), itemsToSet(OLTreeSetPy))
|
||||
|
||||
|
||||
class OLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucket
|
||||
return OLBucket
|
||||
|
||||
|
||||
class OLBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
return OLBucketPy
|
||||
|
||||
|
||||
class OLSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet
|
||||
|
||||
|
||||
class OLSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return OLSetPy
|
||||
|
||||
|
||||
class OLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree
|
||||
|
||||
|
||||
class OLBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy
|
||||
|
||||
|
||||
class OLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OL'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OLBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectIntegerBTreeModule
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OLBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OLBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OLBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OLTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OLBucketTest),
|
||||
unittest.makeSuite(OLBucketPyTest),
|
||||
unittest.makeSuite(OLTreeSetTest),
|
||||
unittest.makeSuite(OLTreeSetPyTest),
|
||||
unittest.makeSuite(OLSetTest),
|
||||
unittest.makeSuite(OLSetPyTest),
|
||||
unittest.makeSuite(OLBTreeTest),
|
||||
unittest.makeSuite(OLBTreePyTest),
|
||||
unittest.makeSuite(PureOL),
|
||||
unittest.makeSuite(PureOLPy),
|
||||
unittest.makeSuite(TestWeightedOL),
|
||||
unittest.makeSuite(TestWeightedOLPy),
|
||||
unittest.makeSuite(OLBucketConflictTests),
|
||||
unittest.makeSuite(OLBucketPyConflictTests),
|
||||
unittest.makeSuite(OLSetConflictTests),
|
||||
unittest.makeSuite(OLSetPyConflictTests),
|
||||
unittest.makeSuite(OLBTreeConflictTests),
|
||||
unittest.makeSuite(OLBTreePyConflictTests),
|
||||
unittest.makeSuite(OLTreeSetConflictTests),
|
||||
unittest.makeSuite(OLTreeSetPyConflictTests),
|
||||
unittest.makeSuite(OLModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,419 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import _skip_under_Py3k
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetResult
|
||||
from .common import SetConflictTestBase
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
|
||||
class OOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy
|
||||
|
||||
class OOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
|
||||
class OOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
return OOBucket
|
||||
|
||||
|
||||
class OOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
return OOBucketPy
|
||||
|
||||
|
||||
class OOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet
|
||||
|
||||
|
||||
class OOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return OOSetPy
|
||||
|
||||
|
||||
|
||||
class OOBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self, *args):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree(*args)
|
||||
|
||||
def test_byValue(self):
|
||||
ITEMS = [(y, x) for x, y in enumerate('abcdefghijklmnopqrstuvwxyz')]
|
||||
tree = self._makeOne(ITEMS)
|
||||
self.assertEqual(list(tree.byValue(22)),
|
||||
[(y, x) for x, y in reversed(ITEMS[22:])])
|
||||
|
||||
def testRejectDefaultComparisonOnSet(self):
|
||||
# Check that passing int keys w default comparison fails.
|
||||
# Only applies to new-style class instances. Old-style
|
||||
# instances are too hard to introspect.
|
||||
|
||||
# This is white box because we know that the check is being
|
||||
# used in a function that's used in lots of places.
|
||||
# Otherwise, there are many permutations that would have to be
|
||||
# checked.
|
||||
from .._compat import PY2
|
||||
t = self._makeOne()
|
||||
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(TypeError, lambda : t.__setitem__(C(), 1))
|
||||
|
||||
with self.assertRaises(TypeError) as raising:
|
||||
t[C()] = 1
|
||||
|
||||
self.assertEqual(raising.exception.args[0], "Object has default comparison")
|
||||
|
||||
if PY2: # we only check for __cmp__ on Python2
|
||||
|
||||
class With___cmp__(object):
|
||||
def __cmp__(*args):
|
||||
return 1
|
||||
c = With___cmp__()
|
||||
t[c] = 1
|
||||
|
||||
t.clear()
|
||||
|
||||
class With___lt__(object):
|
||||
def __lt__(*args):
|
||||
return 1
|
||||
|
||||
c = With___lt__()
|
||||
t[c] = 1
|
||||
|
||||
t.clear()
|
||||
|
||||
def testAcceptDefaultComparisonOnGet(self):
|
||||
# Issue #42
|
||||
t = self._makeOne()
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
self.assertEqual(t.get(C(), 42), 42)
|
||||
self.assertRaises(KeyError, t.__getitem__, C())
|
||||
self.assertFalse(C() in t)
|
||||
|
||||
def test_None_is_smallest(self):
|
||||
t = self._makeOne()
|
||||
for i in range(999): # Make sure we multiple buckets
|
||||
t[i] = i*i
|
||||
t[None] = -1
|
||||
for i in range(-99,0): # Make sure we multiple buckets
|
||||
t[i] = i*i
|
||||
self.assertEqual(list(t), [None] + list(range(-99, 999)))
|
||||
self.assertEqual(list(t.values()),
|
||||
[-1] + [i*i for i in range(-99, 999)])
|
||||
self.assertEqual(t[2], 4)
|
||||
self.assertEqual(t[-2], 4)
|
||||
self.assertEqual(t[None], -1)
|
||||
t[None] = -2
|
||||
self.assertEqual(t[None], -2)
|
||||
t2 = t.__class__(t)
|
||||
del t[None]
|
||||
self.assertEqual(list(t), list(range(-99, 999)))
|
||||
|
||||
if 'Py' in self.__class__.__name__:
|
||||
return
|
||||
from BTrees.OOBTree import difference, union, intersection
|
||||
self.assertEqual(list(difference(t2, t).items()), [(None, -2)])
|
||||
self.assertEqual(list(union(t, t2)), list(t2))
|
||||
self.assertEqual(list(intersection(t, t2)), list(t))
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testDeleteNoneKey(self):
|
||||
# Check that a None key can be deleted in Python 2.
|
||||
# This doesn't work on Python 3 because None is unorderable,
|
||||
# so the tree can't be searched. But None also can't be inserted,
|
||||
# and we don't support migrating Python 2 databases to Python 3.
|
||||
t = self._makeOne()
|
||||
bucket_state = ((None, 42),)
|
||||
tree_state = ((bucket_state,),)
|
||||
t.__setstate__(tree_state)
|
||||
|
||||
self.assertEqual(t[None], 42)
|
||||
del t[None]
|
||||
|
||||
def testUnpickleNoneKey(self):
|
||||
# All versions (py2 and py3, C and Python) can unpickle
|
||||
# data that looks like this: {None: 42}, even though None
|
||||
# is unorderable..
|
||||
# This pickle was captured in BTree/ZODB3 3.10.7
|
||||
data = b'ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((NI42\ntp4\ntp5\ntp6\ntp7\nb.'
|
||||
|
||||
import pickle
|
||||
t = pickle.loads(data)
|
||||
keys = list(t)
|
||||
self.assertEqual([None], keys)
|
||||
|
||||
def testIdentityTrumpsBrokenComparison(self):
|
||||
# Identical keys always match, even if their comparison is
|
||||
# broken. See https://github.com/zopefoundation/BTrees/issues/50
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class Bad(object):
|
||||
def __eq__(self, other):
|
||||
return False
|
||||
|
||||
def __cmp__(self, other):
|
||||
return 1
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
t = self._makeOne()
|
||||
bad_key = Bad()
|
||||
t[bad_key] = 42
|
||||
|
||||
self.assertIn(bad_key, t)
|
||||
self.assertEqual(list(t), [bad_key])
|
||||
|
||||
del t[bad_key]
|
||||
self.assertNotIn(bad_key, t)
|
||||
self.assertEqual(list(t), [])
|
||||
|
||||
|
||||
class OOBTreePyTest(OOBTreeTest):
|
||||
#
|
||||
# Right now, we can't match the C extension's test / prohibition of the
|
||||
# default 'object' comparison semantics.
|
||||
#class OOBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self, *args):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy(*args)
|
||||
|
||||
|
||||
|
||||
class PureOO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from BTrees.OOBTree import OOBucket
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet, OOTreeSet, makeBuilder(OOBTree), makeBuilder(OOBucket)
|
||||
|
||||
|
||||
class PureOOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return (OOSetPy, OOTreeSetPy,
|
||||
makeBuilder(OOBTreePy), makeBuilder(OOBucketPy))
|
||||
|
||||
|
||||
class OOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
return OOBucket
|
||||
|
||||
|
||||
class OOBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
return OOBucketPy
|
||||
|
||||
|
||||
class OOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet
|
||||
|
||||
|
||||
class OOSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return OOSetPy
|
||||
|
||||
|
||||
class OOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
|
||||
class OOBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy
|
||||
|
||||
|
||||
class OOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have weightedIntersection")
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OOBucketTest),
|
||||
unittest.makeSuite(OOBucketPyTest),
|
||||
unittest.makeSuite(OOTreeSetTest),
|
||||
unittest.makeSuite(OOTreeSetPyTest),
|
||||
unittest.makeSuite(OOSetTest),
|
||||
unittest.makeSuite(OOSetPyTest),
|
||||
unittest.makeSuite(OOBTreeTest),
|
||||
unittest.makeSuite(OOBTreePyTest),
|
||||
unittest.makeSuite(PureOO),
|
||||
unittest.makeSuite(PureOOPy),
|
||||
unittest.makeSuite(OOBucketConflictTests),
|
||||
unittest.makeSuite(OOBucketPyConflictTests),
|
||||
unittest.makeSuite(OOSetConflictTests),
|
||||
unittest.makeSuite(OOSetPyConflictTests),
|
||||
unittest.makeSuite(OOBTreeConflictTests),
|
||||
unittest.makeSuite(OOBTreePyConflictTests),
|
||||
unittest.makeSuite(OOTreeSetConflictTests),
|
||||
unittest.makeSuite(OOTreeSetPyConflictTests),
|
||||
unittest.makeSuite(OOModuleTest),
|
||||
))
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user