url(r'^post/(?P<pk>\d+)/remove/$', views.post_remove, name='post_remove'), | url(r'^post/(?P<pk>\d+)/remove/$', views.post_remove, name='post_remove'), | ||||
url(r'^student/(?P<slug>[-\w]+)/remove/$', views.tag_remove, name='tag_remove'), | url(r'^student/(?P<slug>[-\w]+)/remove/$', views.tag_remove, name='tag_remove'), | ||||
url(r'^tags/', include('taggit_templatetags2.urls')), | url(r'^tags/', include('taggit_templatetags2.urls')), | ||||
url(r'^newsletter/', include('newsletter.urls')), | |||||
] | ] | ||||
if settings.DEBUG: | if settings.DEBUG: |
[24/Oct/2018 19:03:28] INFO [mysite:191] <QuerySet [<Post: Third one>]> | [24/Oct/2018 19:03:28] INFO [mysite:191] <QuerySet [<Post: Third one>]> | ||||
[24/Oct/2018 19:03:45] INFO [mysite:189] bamberg | [24/Oct/2018 19:03:45] INFO [mysite:189] bamberg | ||||
[24/Oct/2018 19:03:45] INFO [mysite:191] <QuerySet [<Post: Third one>, <Post: here i go again>]> | [24/Oct/2018 19:03:45] INFO [mysite:191] <QuerySet [<Post: Third one>, <Post: here i go again>]> | ||||
[25/Oct/2018 10:33:13] INFO [mysite:189] bamberg | |||||
[25/Oct/2018 15:28:52] INFO [mysite:189] hi | |||||
[25/Oct/2018 15:28:54] INFO [mysite:189] |
'django.contrib.sessions', | 'django.contrib.sessions', | ||||
'django.contrib.messages', | 'django.contrib.messages', | ||||
'django.contrib.staticfiles', | 'django.contrib.staticfiles', | ||||
'django.contrib.sites', | |||||
'application', | 'application', | ||||
'taggit', | 'taggit', | ||||
'taggit_templatetags2', | 'taggit_templatetags2', | ||||
'djcelery', | 'djcelery', | ||||
'kombu.transport.django', | 'kombu.transport.django', | ||||
'sorl.thumbnail', | |||||
'newsletter', | |||||
] | ] | ||||
MIDDLEWARE = [ | MIDDLEWARE = [ | ||||
CELERY_RESULT_SERIALIZER = 'json' | CELERY_RESULT_SERIALIZER = 'json' | ||||
CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend' | CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend' | ||||
CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler" | CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler" | ||||
djcelery.setup_loader() | |||||
djcelery.setup_loader() | |||||
SITE_ID = 1 |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'python-card-me==0.9.3','console_scripts','change_tz' | |||||
__requires__ = 'python-card-me==0.9.3' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('python-card-me==0.9.3', 'console_scripts', 'change_tz')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from chardet.cli.chardetect import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZODB.FileStorage.fsdump import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZODB.scripts.fsoids import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZODB.scripts.fsrefs import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZODB.scripts.fstail import Main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(Main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'python-card-me==0.9.3','console_scripts','ics_diff' | |||||
__requires__ = 'python-card-me==0.9.3' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('python-card-me==0.9.3', 'console_scripts', 'ics_diff')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZODB.scripts.repozo import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'ZEO==5.2.0','console_scripts','runzeo' | |||||
__requires__ = 'ZEO==5.2.0' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('ZEO==5.2.0', 'console_scripts', 'runzeo')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
from surlex import Surlex | |||||
import sys | |||||
from optparse import OptionParser | |||||
def main(): | |||||
parser = OptionParser() | |||||
parser.set_usage('surlex2regex.py <surlex>') | |||||
if len(sys.argv) == 1: | |||||
argv = ['-h'] | |||||
else: | |||||
argv = sys.argv[1:] | |||||
options, args = parser.parse_args(argv) | |||||
print (Surlex(args[0]).translate()) | |||||
if __name__ == '__main__': | |||||
main() |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZConfig.validator import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# -*- coding: utf-8 -*- | |||||
import re | |||||
import sys | |||||
from ZConfig.schema2html import main | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit(main()) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'zdaemon==4.2.0','console_scripts','zdaemon' | |||||
__requires__ = 'zdaemon==4.2.0' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('zdaemon==4.2.0', 'console_scripts', 'zdaemon')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'ZEO==5.2.0','console_scripts','zeo-nagios' | |||||
__requires__ = 'ZEO==5.2.0' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('ZEO==5.2.0', 'console_scripts', 'zeo-nagios')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'ZEO==5.2.0','console_scripts','zeoctl' | |||||
__requires__ = 'ZEO==5.2.0' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('ZEO==5.2.0', 'console_scripts', 'zeoctl')() | |||||
) |
#!/Users/Esthi/thesis_ek/thesisenv/bin/python3 | |||||
# EASY-INSTALL-ENTRY-SCRIPT: 'ZEO==5.2.0','console_scripts','zeopack' | |||||
__requires__ = 'ZEO==5.2.0' | |||||
import re | |||||
import sys | |||||
from pkg_resources import load_entry_point | |||||
if __name__ == '__main__': | |||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | |||||
sys.exit( | |||||
load_entry_point('ZEO==5.2.0', 'console_scripts', 'zeopack')() | |||||
) |
/***************************************************************************** | |||||
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 | |||||
****************************************************************************/ | |||||
#ifndef CPERSISTENCE_H | |||||
#define CPERSISTENCE_H | |||||
#include "_compat.h" | |||||
#include "bytesobject.h" | |||||
#include "ring.h" | |||||
#define CACHE_HEAD \ | |||||
PyObject_HEAD \ | |||||
CPersistentRing ring_home; \ | |||||
int non_ghost_count; \ | |||||
Py_ssize_t total_estimated_size; | |||||
struct ccobject_head_struct; | |||||
typedef struct ccobject_head_struct PerCache; | |||||
/* How big is a persistent object? | |||||
12 PyGC_Head is two pointers and an int | |||||
8 PyObject_HEAD is an int and a pointer | |||||
12 jar, oid, cache pointers | |||||
8 ring struct | |||||
8 serialno | |||||
4 state + extra | |||||
4 size info | |||||
(56) so far | |||||
4 dict ptr | |||||
4 weaklist ptr | |||||
------------------------- | |||||
68 only need 62, but obmalloc rounds up to multiple of eight | |||||
Even a ghost requires 64 bytes. It's possible to make a persistent | |||||
instance with slots and no dict, which changes the storage needed. | |||||
*/ | |||||
#define cPersistent_HEAD \ | |||||
PyObject_HEAD \ | |||||
PyObject *jar; \ | |||||
PyObject *oid; \ | |||||
PerCache *cache; \ | |||||
CPersistentRing ring; \ | |||||
char serial[8]; \ | |||||
signed state:8; \ | |||||
unsigned estimated_size:24; | |||||
/* We recently added estimated_size. We originally added it as a new | |||||
unsigned long field after a signed char state field and a | |||||
3-character reserved field. This didn't work because there | |||||
are packages in the wild that have their own copies of cPersistence.h | |||||
that didn't see the update. | |||||
To get around this, we used the reserved space by making | |||||
estimated_size a 24-bit bit field in the space occupied by the old | |||||
3-character reserved field. To fit in 24 bits, we made the units | |||||
of estimated_size 64-character blocks. This allows is to handle up | |||||
to a GB. We should never see that, but to be paranoid, we also | |||||
truncate sizes greater than 1GB. We also set the minimum size to | |||||
64 bytes. | |||||
We use the _estimated_size_in_24_bits and _estimated_size_in_bytes | |||||
macros both to avoid repetition and to make intent a little clearer. | |||||
*/ | |||||
#define _estimated_size_in_24_bits(I) ((I) > 1073741696 ? 16777215 : (I)/64+1) | |||||
#define _estimated_size_in_bytes(I) ((I)*64) | |||||
#define cPersistent_GHOST_STATE -1 | |||||
#define cPersistent_UPTODATE_STATE 0 | |||||
#define cPersistent_CHANGED_STATE 1 | |||||
#define cPersistent_STICKY_STATE 2 | |||||
typedef struct { | |||||
cPersistent_HEAD | |||||
} cPersistentObject; | |||||
typedef void (*percachedelfunc)(PerCache *, PyObject *); | |||||
typedef struct { | |||||
PyTypeObject *pertype; | |||||
getattrofunc getattro; | |||||
setattrofunc setattro; | |||||
int (*changed)(cPersistentObject*); | |||||
void (*accessed)(cPersistentObject*); | |||||
void (*ghostify)(cPersistentObject*); | |||||
int (*setstate)(PyObject*); | |||||
percachedelfunc percachedel; | |||||
int (*readCurrent)(cPersistentObject*); | |||||
} cPersistenceCAPIstruct; | |||||
#define cPersistenceType cPersistenceCAPI->pertype | |||||
#ifndef DONT_USE_CPERSISTENCECAPI | |||||
static cPersistenceCAPIstruct *cPersistenceCAPI; | |||||
#endif | |||||
#define cPersistanceModuleName "cPersistence" | |||||
#define PER_TypeCheck(O) PyObject_TypeCheck((O), cPersistenceCAPI->pertype) | |||||
#define PER_USE_OR_RETURN(O,R) {if((O)->state==cPersistent_GHOST_STATE && cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R); else if ((O)->state==cPersistent_UPTODATE_STATE) (O)->state=cPersistent_STICKY_STATE;} | |||||
#define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O))) | |||||
#define PER_READCURRENT(O, E) \ | |||||
if (cPersistenceCAPI->readCurrent((cPersistentObject*)(O)) < 0) { E; } | |||||
#define PER_GHOSTIFY(O) (cPersistenceCAPI->ghostify((cPersistentObject*)(O))) | |||||
/* If the object is sticky, make it non-sticky, so that it can be ghostified. | |||||
The value is not meaningful | |||||
*/ | |||||
#define PER_ALLOW_DEACTIVATION(O) ((O)->state==cPersistent_STICKY_STATE && ((O)->state=cPersistent_UPTODATE_STATE)) | |||||
#define PER_PREVENT_DEACTIVATION(O) ((O)->state==cPersistent_UPTODATE_STATE && ((O)->state=cPersistent_STICKY_STATE)) | |||||
/* | |||||
Make a persistent object usable from C by: | |||||
- Making sure it is not a ghost | |||||
- Making it sticky. | |||||
IMPORTANT: If you call this and don't call PER_ALLOW_DEACTIVATION, | |||||
your object will not be ghostified. | |||||
PER_USE returns a 1 on success and 0 failure, where failure means | |||||
error. | |||||
*/ | |||||
#define PER_USE(O) \ | |||||
(((O)->state != cPersistent_GHOST_STATE \ | |||||
|| (cPersistenceCAPI->setstate((PyObject*)(O)) >= 0)) \ | |||||
? (((O)->state==cPersistent_UPTODATE_STATE) \ | |||||
? ((O)->state=cPersistent_STICKY_STATE) : 1) : 0) | |||||
#define PER_ACCESSED(O) (cPersistenceCAPI->accessed((cPersistentObject*)(O))) | |||||
#endif |
/***************************************************************************** | |||||
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 | |||||
****************************************************************************/ | |||||
/* Support routines for the doubly-linked list of cached objects. | |||||
The cache stores a headed, doubly-linked, circular list of persistent | |||||
objects, with space for the pointers allocated in the objects themselves. | |||||
The cache stores the distinguished head of the list, which is not a valid | |||||
persistent object. The other list members are non-ghost persistent | |||||
objects, linked in LRU (least-recently used) order. | |||||
The r_next pointers traverse the ring starting with the least recently used | |||||
object. The r_prev pointers traverse the ring starting with the most | |||||
recently used object. | |||||
Obscure: While each object is pointed at twice by list pointers (once by | |||||
its predecessor's r_next, again by its successor's r_prev), the refcount | |||||
on the object is bumped only by 1. This leads to some possibly surprising | |||||
sequences of incref and decref code. Note that since the refcount is | |||||
bumped at least once, the list does hold a strong reference to each | |||||
object in it. | |||||
*/ | |||||
typedef struct CPersistentRing_struct | |||||
{ | |||||
struct CPersistentRing_struct *r_prev; | |||||
struct CPersistentRing_struct *r_next; | |||||
} CPersistentRing; | |||||
/* The list operations here take constant time independent of the | |||||
* number of objects in the list: | |||||
*/ | |||||
/* Add elt as the most recently used object. elt must not already be | |||||
* in the list, although this isn't checked. | |||||
*/ | |||||
void ring_add(CPersistentRing *ring, CPersistentRing *elt); | |||||
/* Remove elt from the list. elt must already be in the list, although | |||||
* this isn't checked. | |||||
*/ | |||||
void ring_del(CPersistentRing *elt); | |||||
/* elt must already be in the list, although this isn't checked. It's | |||||
* unlinked from its current position, and relinked into the list as the | |||||
* most recently used object (which is arguably the tail of the list | |||||
* instead of the head -- but the name of this function could be argued | |||||
* either way). This is equivalent to | |||||
* | |||||
* ring_del(elt); | |||||
* ring_add(ring, elt); | |||||
* | |||||
* but may be a little quicker. | |||||
*/ | |||||
void ring_move_to_head(CPersistentRing *ring, CPersistentRing *elt); |
#ifndef _proxy_H_ | |||||
#define _proxy_H_ 1 | |||||
typedef struct { | |||||
PyObject_HEAD | |||||
PyObject *proxy_object; | |||||
} ProxyObject; | |||||
#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) | |||||
typedef struct { | |||||
PyTypeObject *proxytype; | |||||
int (*check)(PyObject *obj); | |||||
PyObject *(*create)(PyObject *obj); | |||||
PyObject *(*getobject)(PyObject *proxy); | |||||
} ProxyInterface; | |||||
#ifndef PROXY_MODULE | |||||
/* These are only defined in the public interface, and are not | |||||
* available within the module implementation. There we use the | |||||
* classic Python/C API only. | |||||
*/ | |||||
static ProxyInterface *_proxy_api = NULL; | |||||
static int | |||||
Proxy_Import(void) | |||||
{ | |||||
if (_proxy_api == NULL) { | |||||
PyObject *m = PyImport_ImportModule("zope.proxy"); | |||||
if (m != NULL) { | |||||
PyObject *tmp = PyObject_GetAttrString(m, "_CAPI"); | |||||
if (tmp != NULL) { | |||||
#if PY_VERSION_HEX < 0x02070000 | |||||
if (PyCObject_Check(tmp)) | |||||
_proxy_api = (ProxyInterface *) | |||||
PyCObject_AsVoidPtr(tmp); | |||||
#else | |||||
if (PyCapsule_CheckExact(tmp)) | |||||
_proxy_api = (ProxyInterface *) | |||||
PyCapsule_GetPointer(tmp, NULL); | |||||
#endif | |||||
Py_DECREF(tmp); | |||||
} | |||||
} | |||||
} | |||||
return (_proxy_api == NULL) ? -1 : 0; | |||||
} | |||||
#define ProxyType (*_proxy_api->proxytype) | |||||
#define Proxy_Check(obj) (_proxy_api->check((obj))) | |||||
#define Proxy_CheckExact(obj) ((obj)->ob_type == ProxyType) | |||||
#define Proxy_New(obj) (_proxy_api->create((obj))) | |||||
#define Proxy_GetObject(proxy) (_proxy_api->getobject((proxy))) | |||||
#endif /* PROXY_MODULE */ | |||||
#endif /* _proxy_H_ */ |
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 |
.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 |
../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 |
ExtensionClass>=4.2.0 | |||||
zope.interface |
Acquisition |
/***************************************************************************** | |||||
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 |
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) |
############################################################################## | |||||
# | |||||
# 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.""") |
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`` | |||||
pip |
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`` | |||||
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 |
{"payload": "eyJoYXNoIjogInNoYTI1Nj1FbUpVbjFTUEdYeXhKc2FnMWpxNGJkMnlnYW80TlhzY2hORW5iSEFISHdnIn0", "recipients": [{"header": "eyJhbGciOiAiRWQyNTUxOSIsICJqd2siOiB7Imt0eSI6ICJFZDI1NTE5IiwgInZrIjogInZUZU1lalVPMTV1SkQzY203enZPLV9oc0dMSld4ZlFNMEFFd2R5SUQyeTQifX0", "signature": "D1i5q57sD-x3nU_8L_49SwW7b--DjetQhUpnMDwDDui6PVsLfsAr65teCK_GbT4txrxkqzCHEMV8byYKzz4XDw"}]} |
Wheel-Version: 1.0 | |||||
Generator: bdist_wheel (0.30.0.a0) | |||||
Root-Is-Purelib: true | |||||
Tag: py2-none-any | |||||
Tag: py3-none-any | |||||
{"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"} |
AuthEncoding |
############################################################################## | |||||
# | |||||
# 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 |
############################################################################## | |||||
# | |||||
# 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) |
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 |
# -*- 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')) |
pip |
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``). | |||||
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 |
Wheel-Version: 1.0 | |||||
Generator: bdist_wheel (0.31.1) | |||||
Root-Is-Purelib: false | |||||
Tag: cp36-cp36m-macosx_10_6_intel | |||||
BTrees | |||||
terryfy |
/***************************************************************************** | |||||
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 */ | |||||
}; |
/***************************************************************************** | |||||
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 |
===================== | |||||
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). |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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. | |||||
# | |||||
################################################################ |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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 |
/***************************************************************************** | |||||
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; | |||||
} |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
############################################################################## | |||||
# | |||||
# 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) |
/***************************************************************************** | |||||
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 |
/***************************************************************************** | |||||
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; | |||||
} |
/***************************************************************************** | |||||
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 */ | |||||
}; |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
/*############################################################################ | |||||
# | |||||
# 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" |
############################################################################# | |||||
# | |||||
# 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 |
/* 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 */ |
############################################################################## | |||||
# | |||||
# 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'] |
/*############################################################################ | |||||
# | |||||
# 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; | |||||
} |
############################################################################## | |||||
# | |||||
# 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() |