Commit 023d96a7 authored by Jason Madden's avatar Jason Madden Committed by GitHub

Merge pull request #80 from zopefoundation/issue78

Support PURE_PYTHON at runtime. 
parents 822181c6 5aae589d
...@@ -4,6 +4,9 @@ matrix: ...@@ -4,6 +4,9 @@ matrix:
include: include:
- os: linux - os: linux
python: 2.7 python: 2.7
- os: linux
python: 2.7
env: PURE_PYTHON=1
- os: linux - os: linux
python: 3.3 python: 3.3
- os: linux - os: linux
...@@ -12,10 +15,13 @@ matrix: ...@@ -12,10 +15,13 @@ matrix:
python: 3.5 python: 3.5
- os: linux - os: linux
python: 3.6 python: 3.6
- os: linux
python: 3.6
env: PURE_PYTHON=1
- os: linux - os: linux
python: pypy python: pypy
- os: linux - os: linux
python: pypy3.3-5.2-alpha1 python: pypy3
# It's important to use 'macpython' builds to get the least # It's important to use 'macpython' builds to get the least
# restrictive wheel tag. It's also important to avoid # restrictive wheel tag. It's also important to avoid
# 'homebrew 3' because it floats instead of being a specific version. # 'homebrew 3' because it floats instead of being a specific version.
...@@ -45,7 +51,7 @@ matrix: ...@@ -45,7 +51,7 @@ matrix:
before_install: before_install:
- if [[ $TRAVIS_TAG ]]; then bash .manylinux.sh; fi - if [[ $TRAVIS_TAG ]]; then bash .manylinux.sh; fi
- exit 0 - exit 0
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git clone https://github.com/MacPython/terryfy; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git clone https://github.com/MacPython/terryfy; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source terryfy/travis_tools.sh; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source terryfy/travis_tools.sh; fi
...@@ -58,9 +64,9 @@ install: ...@@ -58,9 +64,9 @@ install:
# (https://travis-ci.org/zopefoundation/BTrees/jobs/192340692) so # (https://travis-ci.org/zopefoundation/BTrees/jobs/192340692) so
# we install with pip manually. # we install with pip manually.
- pip install -U persistent - pip install -U persistent
- pip install -e .[ZODB] - pip install -e .[test,ZODB]
script: script:
- python setup.py -q test -q - zope-testrunner --test-path=. --auto-color --auto-progress
notifications: notifications:
email: false email: false
after_success: after_success:
......
...@@ -39,6 +39,7 @@ from ._base import union as _union ...@@ -39,6 +39,7 @@ from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120 _BUCKET_SIZE = 120
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -104,36 +105,7 @@ multiunionPy = _set_operation(_multiunion, IFSetPy) ...@@ -104,36 +105,7 @@ multiunionPy = _set_operation(_multiunion, IFSetPy)
weightedUnionPy = _set_operation(_weightedUnion, IFSetPy) weightedUnionPy = _set_operation(_weightedUnion, IFSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, IFSetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, IFSetPy)
try: import_c_extension(globals())
from ._IFBTree import IFBucket
except ImportError: #pragma NO COVER w/ C extensions
IFBucket = IFBucketPy
IFSet = IFSetPy
IFBTree = IFBTreePy
IFTreeSet = IFTreeSetPy
IFTreeIterator = IFTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._IFBTree import IFSet
from ._IFBTree import IFBTree
from ._IFBTree import IFTreeSet
from ._IFBTree import IFTreeIterator
from ._IFBTree import difference
from ._IFBTree import union
from ._IFBTree import intersection
from ._IFBTree import multiunion
from ._IFBTree import weightedUnion
from ._IFBTree import weightedIntersection
Bucket = IFBucket
Set = IFSet
BTree = IFBTree
TreeSet = IFTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -34,11 +34,12 @@ from ._base import intersection as _intersection ...@@ -34,11 +34,12 @@ from ._base import intersection as _intersection
from ._base import multiunion as _multiunion from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation from ._base import set_operation as _set_operation
from ._base import to_int as _to_key from ._base import to_int as _to_key
from ._base import to_int as _to_value _to_value = _to_key
from ._base import union as _union from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120 _BUCKET_SIZE = 120
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, IISetPy) ...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, IISetPy)
weightedUnionPy = _set_operation(_weightedUnion, IISetPy) weightedUnionPy = _set_operation(_weightedUnion, IISetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, IISetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, IISetPy)
try: import_c_extension(globals())
from ._IIBTree import IIBucket
except ImportError: #pragma NO COVER w/ C extensions
IIBucket = IIBucketPy
IISet = IISetPy
IIBTree = IIBTreePy
IITreeSet = IITreeSetPy
IITreeIterator = IITreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._IIBTree import IISet
from ._IIBTree import IIBTree
from ._IIBTree import IITreeSet
from ._IIBTree import IITreeIterator
from ._IIBTree import difference
from ._IIBTree import union
from ._IIBTree import intersection
from ._IIBTree import multiunion
from ._IIBTree import weightedUnion
from ._IIBTree import weightedIntersection
Bucket = IIBucket
Set = IISet
BTree = IIBTree
TreeSet = IITreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -34,6 +34,7 @@ from ._base import to_int as _to_key ...@@ -34,6 +34,7 @@ from ._base import to_int as _to_key
from ._base import to_ob as _to_value from ._base import to_ob as _to_value
from ._base import union as _union from ._base import union as _union
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60 _BUCKET_SIZE = 60
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -87,32 +88,7 @@ unionPy = _set_operation(_union, IOSetPy) ...@@ -87,32 +88,7 @@ unionPy = _set_operation(_union, IOSetPy)
intersectionPy = _set_operation(_intersection, IOSetPy) intersectionPy = _set_operation(_intersection, IOSetPy)
multiunionPy = _set_operation(_multiunion, IOSetPy) multiunionPy = _set_operation(_multiunion, IOSetPy)
try: import_c_extension(globals())
from ._IOBTree import IOBucket
except ImportError: #pragma NO COVER w/ C extensions
IOBucket = IOBucketPy
IOSet = IOSetPy
IOBTree = IOBTreePy
IOTreeSet = IOTreeSetPy
IOTreeIterator = IOTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
else: #pragma NO COVER w/o C extensions
from ._IOBTree import IOSet
from ._IOBTree import IOBTree
from ._IOBTree import IOTreeSet
from ._IOBTree import IOTreeIterator
from ._IOBTree import difference
from ._IOBTree import union
from ._IOBTree import intersection
from ._IOBTree import multiunion
Bucket = IOBucket
Set = IOSet
BTree = IOBTree
TreeSet = IOTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -39,6 +39,7 @@ from ._base import union as _union ...@@ -39,6 +39,7 @@ from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120 _BUCKET_SIZE = 120
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, LFSetPy) ...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, LFSetPy)
weightedUnionPy = _set_operation(_weightedUnion, LFSetPy) weightedUnionPy = _set_operation(_weightedUnion, LFSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, LFSetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, LFSetPy)
try: import_c_extension(globals())
from ._LFBTree import LFBucket
except ImportError: #pragma NO COVER w/ C extensions
LFBucket = LFBucketPy
LFSet = LFSetPy
LFBTree = LFBTreePy
LFTreeSet = LFTreeSetPy
LFTreeIterator = LFTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._LFBTree import LFSet
from ._LFBTree import LFBTree
from ._LFBTree import LFTreeSet
from ._LFBTree import LFTreeIterator
from ._LFBTree import difference
from ._LFBTree import union
from ._LFBTree import intersection
from ._LFBTree import multiunion
from ._LFBTree import weightedUnion
from ._LFBTree import weightedIntersection
Bucket = LFBucket
Set = LFSet
BTree = LFBTree
TreeSet = LFTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -39,6 +39,7 @@ from ._base import union as _union ...@@ -39,6 +39,7 @@ from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120 _BUCKET_SIZE = 120
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, LLSetPy) ...@@ -105,36 +106,7 @@ multiunionPy = _set_operation(_multiunion, LLSetPy)
weightedUnionPy = _set_operation(_weightedUnion, LLSetPy) weightedUnionPy = _set_operation(_weightedUnion, LLSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, LLSetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, LLSetPy)
try: import_c_extension(globals())
from ._LLBTree import LLBucket
except ImportError: #pragma NO COVER w/ C extensions
LLBucket = LLBucketPy
LLSet = LLSetPy
LLBTree = LLBTreePy
LLTreeSet = LLTreeSetPy
LLTreeIterator = LLTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._LLBTree import LLSet
from ._LLBTree import LLBTree
from ._LLBTree import LLTreeSet
from ._LLBTree import LLTreeIterator
from ._LLBTree import difference
from ._LLBTree import union
from ._LLBTree import intersection
from ._LLBTree import multiunion
from ._LLBTree import weightedUnion
from ._LLBTree import weightedIntersection
Bucket = LLBucket
Set = LLSet
BTree = LLBTree
TreeSet = LLTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -34,6 +34,7 @@ from ._base import to_long as _to_key ...@@ -34,6 +34,7 @@ from ._base import to_long as _to_key
from ._base import to_ob as _to_value from ._base import to_ob as _to_value
from ._base import union as _union from ._base import union as _union
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60 _BUCKET_SIZE = 60
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -88,32 +89,7 @@ unionPy = _set_operation(_union, LOSetPy) ...@@ -88,32 +89,7 @@ unionPy = _set_operation(_union, LOSetPy)
intersectionPy = _set_operation(_intersection, LOSetPy) intersectionPy = _set_operation(_intersection, LOSetPy)
multiunionPy = _set_operation(_multiunion, LOSetPy) multiunionPy = _set_operation(_multiunion, LOSetPy)
try: import_c_extension(globals())
from ._LOBTree import LOBucket
except ImportError: #pragma NO COVER w/ C extensions
LOBucket = LOBucketPy
LOSet = LOSetPy
LOBTree = LOBTreePy
LOTreeSet = LOTreeSetPy
LOTreeIterator = LOTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
multiunion = multiunionPy
else: #pragma NO COVER w/o C extensions
from ._LOBTree import LOSet
from ._LOBTree import LOBTree
from ._LOBTree import LOTreeSet
from ._LOBTree import LOTreeIterator
from ._LOBTree import difference
from ._LOBTree import union
from ._LOBTree import intersection
from ._LOBTree import multiunion
Bucket = LOBucket
Set = LOSet
BTree = LOBTree
TreeSet = LOTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -38,6 +38,7 @@ from ._base import union as _union ...@@ -38,6 +38,7 @@ from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60 _BUCKET_SIZE = 60
_TREE_SIZE = 250 _TREE_SIZE = 250
...@@ -102,35 +103,7 @@ intersectionPy = _set_operation(_intersection, OISetPy) ...@@ -102,35 +103,7 @@ intersectionPy = _set_operation(_intersection, OISetPy)
weightedUnionPy = _set_operation(_weightedUnion, OISetPy) weightedUnionPy = _set_operation(_weightedUnion, OISetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, OISetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, OISetPy)
try: import_c_extension(globals())
from ._OIBTree import OIBucket
except ImportError: #pragma NO COVER w/ C extensions
OIBucket = OIBucketPy
OISet = OISetPy
OIBTree = OIBTreePy
OITreeSet = OITreeSetPy
OITreeIterator = OITreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._OIBTree import OISet
from ._OIBTree import OIBTree
from ._OIBTree import OITreeSet
from ._OIBTree import OITreeIterator
from ._OIBTree import difference
from ._OIBTree import union
from ._OIBTree import intersection
from ._OIBTree import weightedUnion
from ._OIBTree import weightedIntersection
Bucket = OIBucket
Set = OISet
BTree = OIBTree
TreeSet = OITreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -38,6 +38,7 @@ from ._base import union as _union ...@@ -38,6 +38,7 @@ from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60 _BUCKET_SIZE = 60
_TREE_SIZE = 250 _TREE_SIZE = 250
...@@ -103,34 +104,7 @@ intersectionPy = _set_operation(_intersection, OLSetPy) ...@@ -103,34 +104,7 @@ intersectionPy = _set_operation(_intersection, OLSetPy)
weightedUnionPy = _set_operation(_weightedUnion, OLSetPy) weightedUnionPy = _set_operation(_weightedUnion, OLSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, OLSetPy) weightedIntersectionPy = _set_operation(_weightedIntersection, OLSetPy)
try: import_c_extension(globals())
from ._OLBTree import OLBucket
except ImportError: #pragma NO COVER w/ C extensions
OLBucket = OLBucketPy
OLSet = OLSetPy
OLBTree = OLBTreePy
OLTreeSet = OLTreeSetPy
OLTreeIterator = OLTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
weightedUnion = weightedUnionPy
weightedIntersection = weightedIntersectionPy
else: #pragma NO COVER w/o C extensions
from ._OLBTree import OLSet
from ._OLBTree import OLBTree
from ._OLBTree import OLTreeSet
from ._OLBTree import OLTreeIterator
from ._OLBTree import difference
from ._OLBTree import union
from ._OLBTree import intersection
from ._OLBTree import weightedUnion
from ._OLBTree import weightedIntersection
Bucket = OLBucket
Set = OLSet
BTree = OLBTree
TreeSet = OLTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -29,9 +29,10 @@ from ._base import difference as _difference ...@@ -29,9 +29,10 @@ from ._base import difference as _difference
from ._base import intersection as _intersection from ._base import intersection as _intersection
from ._base import set_operation as _set_operation from ._base import set_operation as _set_operation
from ._base import to_ob as _to_key from ._base import to_ob as _to_key
from ._base import to_ob as _to_value _to_value = _to_key
from ._base import union as _union from ._base import union as _union
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 30 _BUCKET_SIZE = 30
_TREE_SIZE = 250 _TREE_SIZE = 250
...@@ -83,32 +84,7 @@ differencePy = _set_operation(_difference, OOSetPy) ...@@ -83,32 +84,7 @@ differencePy = _set_operation(_difference, OOSetPy)
unionPy = _set_operation(_union, OOSetPy) unionPy = _set_operation(_union, OOSetPy)
intersectionPy = _set_operation(_intersection, OOSetPy) intersectionPy = _set_operation(_intersection, OOSetPy)
try: import_c_extension(globals())
from ._OOBTree import OOBucket
except ImportError as e: #pragma NO COVER w/ C extensions
OOBucket = OOBucketPy
OOSet = OOSetPy
OOBTree = OOBTreePy
OOTreeSet = OOTreeSetPy
OOTreeIterator = OOTreeIteratorPy
difference = differencePy
union = unionPy
intersection = intersectionPy
else: #pragma NO COVER w/o C extensions
from ._OOBTree import OOSet
from ._OOBTree import OOBTree
from ._OOBTree import OOTreeSet
from ._OOBTree import OOTreeIterator
from ._OOBTree import difference
from ._OOBTree import union
from ._OOBTree import intersection
Bucket = OOBucket
Set = OOSet
BTree = OOBTree
TreeSet = OOTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -14,8 +14,7 @@ ...@@ -14,8 +14,7 @@
"""Python BTree implementation """Python BTree implementation
""" """
from struct import pack from struct import Struct
from struct import unpack
from struct import error as struct_error from struct import error as struct_error
from persistent import Persistent from persistent import Persistent
...@@ -1496,10 +1495,16 @@ def multiunion(set_type, seqs): ...@@ -1496,10 +1495,16 @@ def multiunion(set_type, seqs):
def to_ob(self, v): def to_ob(self, v):
return v return v
def _packer_unpacker(struct_format):
s = Struct(struct_format)
return s.pack, s.unpack
int_pack, int_unpack = _packer_unpacker('i')
def to_int(self, v): def to_int(self, v):
try: try:
# XXX Python 2.6 doesn't truncate, it spews a warning. # XXX Python 2.6 doesn't truncate, it spews a warning.
if not unpack("i", pack("i", v))[0] == v: #pragma: no cover if not int_unpack(int_pack(v))[0] == v: #pragma: no cover
raise TypeError('32-bit integer expected') raise TypeError('32-bit integer expected')
except (struct_error, except (struct_error,
OverflowError, #PyPy OverflowError, #PyPy
...@@ -1508,17 +1513,22 @@ def to_int(self, v): ...@@ -1508,17 +1513,22 @@ def to_int(self, v):
return int(v) return int(v)
float_pack = _packer_unpacker('f')[0]
def to_float(self, v): def to_float(self, v):
try: try:
pack("f", v) float_pack(v)
except struct_error: except struct_error:
raise TypeError('float expected') raise TypeError('float expected')
return float(v) return float(v)
long_pack, long_unpack = _packer_unpacker('q')
def to_long(self, v): def to_long(self, v):
try: try:
# XXX Python 2.6 doesn't truncate, it spews a warning. # XXX Python 2.6 doesn't truncate, it spews a warning.
if not unpack("q", pack("q", v))[0] == v: #pragma: no cover if not long_unpack(long_pack(v))[0] == v: #pragma: no cover
if isinstance(v, int_types): if isinstance(v, int_types):
raise ValueError("Value out of range", v) raise ValueError("Value out of range", v)
raise TypeError('64-bit integer expected') raise TypeError('64-bit integer expected')
......
...@@ -11,16 +11,21 @@ ...@@ -11,16 +11,21 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
import os
import sys 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 if sys.version_info[0] < 3: #pragma NO COVER Python2
PY2 = True PY2 = True
PY3 = False PY3 = False
from StringIO import StringIO
BytesIO = StringIO
int_types = int, long int_types = int, long
xrange = xrange xrange = xrange
def compare(x, y): def compare(x, y):
...@@ -38,17 +43,11 @@ if sys.version_info[0] < 3: #pragma NO COVER Python2 ...@@ -38,17 +43,11 @@ if sys.version_info[0] < 3: #pragma NO COVER Python2
def _ascii(x): def _ascii(x):
return bytes(x) return bytes(x)
def _u(s, encoding='unicode_escape'):
return unicode(s, encoding)
else: #pragma NO COVER Python3 else: #pragma NO COVER Python3
PY2 = False PY2 = False
PY3 = True PY3 = True
from io import StringIO
from io import BytesIO
int_types = int, int_types = int,
xrange = range xrange = range
...@@ -67,7 +66,35 @@ else: #pragma NO COVER Python3 ...@@ -67,7 +66,35 @@ else: #pragma NO COVER Python3
def _ascii(x): def _ascii(x):
return bytes(x, 'ascii') return bytes(x, 'ascii')
def _u(s, encoding=None):
if encoding is None: def import_c_extension(mod_globals):
return s import importlib
return str(s, encoding) 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']
...@@ -35,6 +35,8 @@ from ._base import set_operation as _set_operation ...@@ -35,6 +35,8 @@ from ._base import set_operation as _set_operation
from ._base import to_bytes as _to_bytes from ._base import to_bytes as _to_bytes
from ._base import union as _union from ._base import union as _union
from ._base import _fix_pickle from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 500 _BUCKET_SIZE = 500
_TREE_SIZE = 500 _TREE_SIZE = 500
...@@ -101,28 +103,7 @@ differencePy = _set_operation(_difference, fsSetPy) ...@@ -101,28 +103,7 @@ differencePy = _set_operation(_difference, fsSetPy)
unionPy = _set_operation(_union, fsSetPy) unionPy = _set_operation(_union, fsSetPy)
intersectionPy = _set_operation(_intersection, fsSetPy) intersectionPy = _set_operation(_intersection, fsSetPy)
try: import_c_extension(globals())
from ._fsBTree import fsBucket
except ImportError: #pragma NO COVER w/ C extensions
fsBucket = fsBucketPy
fsSet = fsSetPy
fsBTree = fsBTreePy
fsTreeSet = fsTreeSetPy
difference = differencePy
union = unionPy
intersection = intersectionPy
else: #pragma NO COVER w/o C extensions
from ._fsBTree import fsSet
from ._fsBTree import fsBTree
from ._fsBTree import fsTreeSet
from ._fsBTree import difference
from ._fsBTree import union
from ._fsBTree import intersection
Bucket = fsBucket
Set = fsSet
BTree = fsBTree
TreeSet = fsTreeSet
_fix_pickle(globals(), __name__) _fix_pickle(globals(), __name__)
......
...@@ -11,34 +11,48 @@ ...@@ -11,34 +11,48 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
from __future__ import division
import unittest
import platform import platform
from unittest import skip from unittest import skip
def _skip_wo_ZODB(test_method): # pragma: no COVER from BTrees._compat import PY3
try: from BTrees._compat import PURE_PYTHON
import ZODB # noqa from BTrees._compat import PYPY
except ImportError: # skip this test if ZODB is not available
return skip("ZODB not available")(test_method)
else:
return test_method
def _skip_under_Py3k(test_method): # pragma: no COVER
try:
unicode
except NameError: # skip this test
return skip("Python 3")(test_method)
else:
return test_method
def _skip_on_32_bits(test_method): # pragma: no COVER def _no_op(test_method):
if platform.architecture()[0] == '32bit':
return skip("32-bit platform")(test_method)
return test_method return test_method
try:
__import__('ZODB')
except ImportError:
_skip_wo_ZODB = skip('ZODB not available')
else:
_skip_wo_ZODB = _no_op
if PY3:
_skip_under_Py3k = skip("Not on Python 3")
else:
_skip_under_Py3k = _no_op
if platform.architecture()[0] == '32bit':
_skip_on_32_bits = skip("32-bit platform")
else:
_skip_on_32_bits = _no_op
if PURE_PYTHON:
skipOnPurePython = skip("Not on Pure Python")
else:
skipOnPurePython = _no_op
def _skip_if_pure_py_and_py_test(self):
if PURE_PYTHON and 'Py' in type(self).__name__:
# No need to run this again. The "C" tests will catch it.
# This relies on the fact that we always define tests in pairs,
# one normal/C and one with Py in the name for the Py test.
raise unittest.SkipTest("Redundant with the C test")
class Base(object): class Base(object):
# Tests common to all types: sets, buckets, and BTrees # Tests common to all types: sets, buckets, and BTrees
...@@ -51,6 +65,9 @@ class Base(object): ...@@ -51,6 +65,9 @@ class Base(object):
def _makeOne(self): def _makeOne(self):
return self._getTargetClass()() return self._getTargetClass()()
def setUp(self):
super(Base, self).setUp()
_skip_if_pure_py_and_py_test(self)
def tearDown(self): def tearDown(self):
if self.db is not None: if self.db is not None:
...@@ -77,6 +94,38 @@ class Base(object): ...@@ -77,6 +94,38 @@ class Base(object):
transaction.abort() transaction.abort()
root._p_jar.close() root._p_jar.close()
def testPersistentSubclass(self):
# Can we subclass this and Persistent?
# https://github.com/zopefoundation/BTrees/issues/78
import persistent
class PersistentSubclass(persistent.Persistent):
pass
__traceback_info__ = self._getTargetClass(), persistent.Persistent
type('Subclass', (self._getTargetClass(), PersistentSubclass), {})
def testPurePython(self):
import importlib
kind = self._getTargetClass()
class_name = kind.__name__
module_name = kind.__module__
module = importlib.import_module(module_name)
# If we're in pure python mode, our target class module
# should not have an '_' in it (fix_pickle changes the name
# to remove the 'Py')
# If we're in the C extension mode, our target class
# module still doesn't have the _ in it, but we should be able to find
# a Py class that's different
self.assertNotIn('_', module_name)
self.assertIs(getattr(module, class_name), kind)
if not PURE_PYTHON and 'Py' not in type(self).__name__:
self.assertIsNot(getattr(module, class_name + 'Py'), kind)
@_skip_wo_ZODB @_skip_wo_ZODB
def testLoadAndStore(self): def testLoadAndStore(self):
import transaction import transaction
...@@ -1735,6 +1784,10 @@ class TypeTest(object): ...@@ -1735,6 +1784,10 @@ class TypeTest(object):
class I_SetsBase(object): class I_SetsBase(object):
def setUp(self):
super(I_SetsBase, self).setUp()
_skip_if_pure_py_and_py_test(self)
def testBadBadKeyAfterFirst(self): def testBadBadKeyAfterFirst(self):
t = self._makeOne() t = self._makeOne()
self.assertRaises(TypeError, t.__class__, [1, '']) self.assertRaises(TypeError, t.__class__, [1, ''])
...@@ -1768,7 +1821,7 @@ SMALLEST_POSITIVE_65_BITS = LARGEST_64_BITS + 1 ...@@ -1768,7 +1821,7 @@ SMALLEST_POSITIVE_65_BITS = LARGEST_64_BITS + 1
LARGEST_NEGATIVE_65_BITS = SMALLEST_64_BITS - 1 LARGEST_NEGATIVE_65_BITS = SMALLEST_64_BITS - 1
class TestLongIntSupport: class TestLongIntSupport(object):
def getTwoValues(self): def getTwoValues(self):
# Return two distinct values; these must compare as un-equal. # Return two distinct values; these must compare as un-equal.
...@@ -1885,6 +1938,9 @@ def makeBuilder(mapbuilder): ...@@ -1885,6 +1938,9 @@ def makeBuilder(mapbuilder):
# intersection, union, difference - set to the type-correct versions # intersection, union, difference - set to the type-correct versions
class SetResult(object): class SetResult(object):
def setUp(self): def setUp(self):
super(SetResult, self).setUp()
_skip_if_pure_py_and_py_test(self)
self.Akeys = [1, 3, 5, 6 ] self.Akeys = [1, 3, 5, 6 ]
self.Bkeys = [ 2, 3, 4, 6, 7] self.Bkeys = [ 2, 3, 4, 6, 7]
self.As = [makeset(self.Akeys) for makeset in self.builders()] self.As = [makeset(self.Akeys) for makeset in self.builders()]
...@@ -2167,6 +2223,10 @@ def isaset(thing): ...@@ -2167,6 +2223,10 @@ def isaset(thing):
# mkbucket, mkbtree # mkbucket, mkbtree
class MultiUnion(object): class MultiUnion(object):
def setUp(self):
super(MultiUnion, self).setUp()
_skip_if_pure_py_and_py_test(self)
def testEmpty(self): def testEmpty(self):
self.assertEqual(len(self.multiunion([])), 0) self.assertEqual(len(self.multiunion([])), 0)
...@@ -2193,6 +2253,10 @@ class MultiUnion(object): ...@@ -2193,6 +2253,10 @@ class MultiUnion(object):
def testBigInput(self): def testBigInput(self):
N = 100000 N = 100000
if (PURE_PYTHON or 'Py' in type(self).__name__) and not PYPY:
# This is extremely slow in CPython implemented in Python,
# taking 20s or more on a 2015-era laptop
N = N // 10
input = self.mkset(list(range(N))) input = self.mkset(list(range(N)))
output = self.multiunion([input] * 10) output = self.multiunion([input] * 10)
self.assertEqual(len(output), N) self.assertEqual(len(output), N)
...@@ -2234,6 +2298,10 @@ class ConflictTestBase(object): ...@@ -2234,6 +2298,10 @@ class ConflictTestBase(object):
storage = None storage = None
def setUp(self):
super(ConflictTestBase, self).setUp()
_skip_if_pure_py_and_py_test(self)
def tearDown(self): def tearDown(self):
import transaction import transaction
transaction.abort() transaction.abort()
......
...@@ -11,11 +11,8 @@ ...@@ -11,11 +11,8 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
import sys
import unittest import unittest
python3 = sys.version_info >= (3, )
from BTrees.tests.common import permutations from BTrees.tests.common import permutations
...@@ -171,6 +168,8 @@ class DegenerateBTree(unittest.TestCase): ...@@ -171,6 +168,8 @@ class DegenerateBTree(unittest.TestCase):
# RuntimeWarning: tp_compare didn't return -1 or -2 for exception # RuntimeWarning: tp_compare didn't return -1 or -2 for exception
# warnings, possibly due to memory corruption after a BTree # warnings, possibly due to memory corruption after a BTree
# goes insane. # goes insane.
# On CPython in PURE_PYTHON mode, this is a *slow* test, taking 15+s
# on a 2015 laptop.
from BTrees.check import check from BTrees.check import check
t, keys = self._build_degenerate_tree() t, keys = self._build_degenerate_tree()
for oneperm in permutations(keys): for oneperm in permutations(keys):
...@@ -196,7 +195,7 @@ LP294788_ids = {} ...@@ -196,7 +195,7 @@ LP294788_ids = {}
class ToBeDeleted(object): class ToBeDeleted(object):
def __init__(self, id): def __init__(self, id):
assert type(id) is int #we don't want to store any object ref here assert isinstance(id, int) #we don't want to store any object ref here
self.id = id self.id = id
global LP294788_ids global LP294788_ids
...@@ -257,8 +256,6 @@ class BugFixes(unittest.TestCase): ...@@ -257,8 +256,6 @@ class BugFixes(unittest.TestCase):
import gc import gc
import random import random
from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOBTree
from .._compat import _u
from .._compat import xrange
t = OOBTree() t = OOBTree()
...@@ -269,12 +266,12 @@ class BugFixes(unittest.TestCase): ...@@ -269,12 +266,12 @@ class BugFixes(unittest.TestCase):
# /// BTree keys are integers, value is an object # /// BTree keys are integers, value is an object
LP294788_ids = {} LP294788_ids = {}
ids = {} ids = {}
for i in xrange(1024): for i in range(1024):
if trandom.random() > 0.1 or len(ids) == 0: if trandom.random() > 0.1 or not ids:
#add #add
id = None id = None
while id is None or id in ids: while id is None or id in ids:
id = trandom.randint(0,1000000) id = trandom.randint(0, 1000000)
ids[id] = 1 ids[id] = 1
t[id] = ToBeDeleted(id) t[id] = ToBeDeleted(id)
...@@ -304,15 +301,15 @@ class BugFixes(unittest.TestCase): ...@@ -304,15 +301,15 @@ class BugFixes(unittest.TestCase):
# /// BTree keys are integers, value is a tuple having an object # /// BTree keys are integers, value is a tuple having an object
LP294788_ids = {} LP294788_ids = {}
ids = {} ids = {}
for i in xrange(1024): for i in range(1024):
if trandom.random() > 0.1 or len(ids) == 0: if trandom.random() > 0.1 or not ids:
#add #add
id = None id = None
while id is None or id in ids: while id is None or id in ids:
id = trandom.randint(0,1000000) id = trandom.randint(0, 1000000)
ids[id] = 1 ids[id] = 1
t[id] = (id, ToBeDeleted(id), _u('somename')) t[id] = (id, ToBeDeleted(id), u'somename')
else: else:
#del #del
keys = list(ids.keys()) keys = list(ids.keys())
...@@ -341,12 +338,12 @@ class BugFixes(unittest.TestCase): ...@@ -341,12 +338,12 @@ class BugFixes(unittest.TestCase):
t = OOBTree() t = OOBTree()
LP294788_ids = {} LP294788_ids = {}
ids = {} ids = {}
for i in xrange(1024): for i in range(1024):
if trandom.random() > 0.1 or len(ids) == 0: if trandom.random() > 0.1 or not ids:
#add #add
id = None id = None
while id is None or id in ids: while id is None or id in ids:
id = ToBeDeleted(trandom.randint(0,1000000)) id = ToBeDeleted(trandom.randint(0, 1000000))
ids[id] = 1 ids[id] = 1
t[id] = 1 t[id] = 1
...@@ -361,7 +358,7 @@ class BugFixes(unittest.TestCase): ...@@ -361,7 +358,7 @@ class BugFixes(unittest.TestCase):
for id in ids: for id in ids:
del t[id] del t[id]
#release all refs #release all refs
ids = obj = id = None ids = id = None
#to be on the safe side run a full GC #to be on the safe side run a full GC
gc.collect() gc.collect()
...@@ -375,13 +372,13 @@ class BugFixes(unittest.TestCase): ...@@ -375,13 +372,13 @@ class BugFixes(unittest.TestCase):
t = OOBTree() t = OOBTree()
LP294788_ids = {} LP294788_ids = {}
ids = {} ids = {}
for i in xrange(1024): for i in range(1024):
if trandom.random() > 0.1 or len(ids) == 0: if trandom.random() > 0.1 or not ids:
#add #add
id = None id = None
while id is None or id in ids: while id is None or id in ids:
id = trandom.randint(0,1000000) id = trandom.randint(0, 1000000)
id = (id, ToBeDeleted(id), _u('somename')) id = (id, ToBeDeleted(id), u'somename')
ids[id] = 1 ids[id] = 1
t[id] = 1 t[id] = 1
...@@ -396,7 +393,7 @@ class BugFixes(unittest.TestCase): ...@@ -396,7 +393,7 @@ class BugFixes(unittest.TestCase):
for id in ids: for id in ids:
del t[id] del t[id]
#release all refs #release all refs
ids = id = obj = key = None ids = id = key = None
#to be on the safe side run a full GC #to be on the safe side run a full GC
gc.collect() gc.collect()
...@@ -412,7 +409,7 @@ class BugFixes(unittest.TestCase): ...@@ -412,7 +409,7 @@ class BugFixes(unittest.TestCase):
class DoesntLikeBeingCompared: class DoesntLikeBeingCompared:
def __cmp__(self,other): def __cmp__(self, other):
raise ValueError('incomparable') raise ValueError('incomparable')
__lt__ = __le__ = __eq__ = __ne__ = __ge__ = __gt__ = __cmp__ __lt__ = __le__ = __eq__ = __ne__ = __ge__ = __gt__ = __cmp__
...@@ -457,13 +454,11 @@ class FamilyTest(unittest.TestCase): ...@@ -457,13 +454,11 @@ class FamilyTest(unittest.TestCase):
# this next bit illustrates an, um, "interesting feature". If # this next bit illustrates an, um, "interesting feature". If
# the characteristics change to match the 64 bit version, please # the characteristics change to match the 64 bit version, please
# feel free to change. # feel free to change.
try: s.insert(BTrees.family32.maxint + 1) with self.assertRaises((TypeError, OverflowError)):
except (TypeError, OverflowError): pass s.insert(BTrees.family32.maxint + 1)
else: self.assert_(False)
try: s.insert(BTrees.family32.minint - 1) with self.assertRaises((TypeError, OverflowError)):
except (TypeError, OverflowError): pass s.insert(BTrees.family32.minint - 1)
else: self.assert_(False)
self.check_pickling(BTrees.family32) self.check_pickling(BTrees.family32)
def test64(self): def test64(self):
...@@ -499,12 +494,12 @@ class FamilyTest(unittest.TestCase): ...@@ -499,12 +494,12 @@ class FamilyTest(unittest.TestCase):
# unpickling, whether from the same unpickler or different # unpickling, whether from the same unpickler or different
# unpicklers. # unpicklers.
import pickle import pickle
from .._compat import BytesIO from io import BytesIO
s = pickle.dumps((family, family)) s = pickle.dumps((family, family))
(f1, f2) = pickle.loads(s) (f1, f2) = pickle.loads(s)
self.assertTrue(f1 is family) self.assertIs(f1, family)
self.assertTrue(f2 is family) self.assertIs(f2, family)
# Using a single memo across multiple pickles: # Using a single memo across multiple pickles:
sio = BytesIO() sio = BytesIO()
......
...@@ -29,9 +29,8 @@ class TestBTreesUnicode(unittest.TestCase): ...@@ -29,9 +29,8 @@ class TestBTreesUnicode(unittest.TestCase):
#setup an OOBTree with some unicode strings #setup an OOBTree with some unicode strings
from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOBTree
from BTrees._compat import _bytes from BTrees._compat import _bytes
from BTrees._compat import _u
self.s = _u(b'dreit\xe4gigen', 'latin1') self.s = b'dreit\xe4gigen'.decode('latin1')
self.data = [(b'alien', 1), self.data = [(b'alien', 1),
(b'k\xf6nnten', 2), (b'k\xf6nnten', 2),
...@@ -39,23 +38,22 @@ class TestBTreesUnicode(unittest.TestCase): ...@@ -39,23 +38,22 @@ class TestBTreesUnicode(unittest.TestCase):
(b'future', 4), (b'future', 4),
(b'quick', 5), (b'quick', 5),
(b'zerst\xf6rt', 6), (b'zerst\xf6rt', 6),
(_u(b'dreit\xe4gigen','latin1'), 7), (u'dreit\xe4gigen', 7),
] ]
self.tree = OOBTree() self.tree = OOBTree()
for k, v in self.data: for k, v in self.data:
if isinstance(k, _bytes): if isinstance(k, _bytes):
k = _u(k, 'latin1') k = k.decode('latin1')
self.tree[k] = v self.tree[k] = v
@_skip_under_Py3k @_skip_under_Py3k
def testAllKeys(self): def testAllKeys(self):
# check every item of the tree # check every item of the tree
from BTrees._compat import _u
from BTrees._compat import _bytes from BTrees._compat import _bytes
for k, v in self.data: for k, v in self.data:
if isinstance(k, _bytes): if isinstance(k, _bytes):
k = _u(k, encoding) k = k.decode(encoding)
self.assertTrue(k in self.tree) self.assertTrue(k in self.tree)
self.assertEqual(self.tree[k], v) self.assertEqual(self.tree[k], v)
......
...@@ -11,7 +11,10 @@ ...@@ -11,7 +11,10 @@
https://github.com/zopefoundation/BTrees/pull/55 https://github.com/zopefoundation/BTrees/pull/55
- Fix the possibility of a rare crash in the C extension when - Fix the possibility of a rare crash in the C extension when
deallocating items. See https://github.com/zopefoundation/BTrees/issues/75 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.
4.4.1 (2017-01-24) 4.4.1 (2017-01-24)
------------------ ------------------
......
...@@ -19,14 +19,14 @@ environment: ...@@ -19,14 +19,14 @@ environment:
install: install:
- "SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH%" - "SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH%"
- echo "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 > "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\vcvars64.bat" - echo "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 > "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\vcvars64.bat"
- pip install -e .[ZODB] - pip install -e .[test,ZODB]
build_script: build_script:
- pip install wheel - pip install wheel
- python -W ignore setup.py -q bdist_wheel - python -W ignore setup.py -q bdist_wheel
test_script: test_script:
- python setup.py -q test -q - zope-testrunner --test-path=.
artifacts: artifacts:
- path: 'dist\*.whl' - path: 'dist\*.whl'
......
...@@ -11,21 +11,60 @@ ...@@ -11,21 +11,60 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
from __future__ import print_function
__version__ = '4.4.1' __version__ = '4.4.1'
import os import os
import platform
import sys import sys
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
from setuptools import Extension from setuptools import Extension
from setuptools import find_packages from setuptools import find_packages
from setuptools import setup from setuptools import setup
from setuptools.command.build_ext import build_ext
here = os.path.abspath(os.path.dirname(__file__))
README = (open(os.path.join(here, 'README.rst')).read()
+ '\n\n' + def _read(fname):
open(os.path.join(here, 'CHANGES.rst')).read()) here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, fname)) as f:
return f.read()
README = _read("README.rst") + '\n\n' + _read('CHANGES.rst')
class optional_build_ext(build_ext):
"""This class subclasses build_ext and allows
the building of C extensions to fail.
"""
def run(self):
try:
build_ext.run(self)
except DistutilsPlatformError as e:
self._unavailable(e)
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except (CCompilerError, DistutilsExecError, OSError) as e:
self._unavailable(e)
def _unavailable(self, e):
print('*' * 80)
print("""WARNING:
An optional code optimization (C extension) could not be compiled.
Optimizations for this package will not be available!""")
print()
print(e)
print('*' * 80)
if 'bdist_wheel' in sys.argv and not os.environ.get("PURE_PYTHON"):
# pip uses bdist_wheel by default, and hides the error output.
# Let this error percolate up so the user can see it.
# pip will then go ahead and run 'setup.py install' directly.
raise
# Include directories for C extensions # Include directories for C extensions
# Sniff the location of the headers in 'persistent' or fall back # Sniff the location of the headers in 'persistent' or fall back
...@@ -49,6 +88,7 @@ class ModuleHeaderDir(object): ...@@ -49,6 +88,7 @@ class ModuleHeaderDir(object):
path = resource_filename(self._require_spec, self._where) path = resource_filename(self._require_spec, self._where)
return os.path.abspath(path) return os.path.abspath(path)
include = [ModuleHeaderDir('persistent')] include = [ModuleHeaderDir('persistent')]
# Set up dependencies for the BTrees package # Set up dependencies for the BTrees package
...@@ -87,58 +127,47 @@ def BTreeExtension(family): ...@@ -87,58 +127,47 @@ def BTreeExtension(family):
kwargs["define_macros"] = [('EXCLUDE_INTSET_SUPPORT', None)] kwargs["define_macros"] = [('EXCLUDE_INTSET_SUPPORT', None)]
return Extension(name, sources, **kwargs) return Extension(name, sources, **kwargs)
py_impl = getattr(platform, 'python_implementation', lambda: None)
pure_python = os.environ.get('PURE_PYTHON', False)
is_pypy = py_impl() == 'PyPy'
is_jython = 'java' in sys.platform
# Jython cannot build the C optimizations, while on PyPy they are ext_modules = [BTreeExtension(family) for family in FAMILIES]
# anti-optimizations (the C extension compatibility layer is known-slow,
# and defeats JIT opportunities).
if pure_python or is_pypy or is_jython:
ext_modules = []
else:
ext_modules = [BTreeExtension(family) for family in FAMILIES] REQUIRES = [
# 4.1.0 is the first version that PURE_PYTHON can run
# ZODB tests
'persistent >= 4.1.0',
'zope.interface',
]
if sys.version_info[0] >= 3: TESTS_REQUIRE = [
REQUIRES = [ 'transaction',
'persistent>=4.0.4', 'zope.testrunner',
'zope.interface', ]
]
else:
REQUIRES = [
'persistent',
'zope.interface',
]
TESTS_REQUIRE = REQUIRES + ['transaction']
setup(name='BTrees', setup(name='BTrees',
version=__version__, version=__version__,
description='Scalable persistent object containers', description='Scalable persistent object containers',
long_description=README, long_description=README,
classifiers=[ classifiers=[
"Development Status :: 6 - Mature", "Development Status :: 6 - Mature",
"License :: OSI Approved :: Zope Public License", "License :: OSI Approved :: Zope Public License",
"Programming Language :: Python", "Programming Language :: Python",
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
"Framework :: ZODB", "Framework :: ZODB",
"Topic :: Database", "Topic :: Database",
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries :: Python Modules",
"Operating System :: Microsoft :: Windows", "Operating System :: Microsoft :: Windows",
"Operating System :: Unix", "Operating System :: Unix",
], ],
author="Zope Foundation", author="Zope Foundation",
author_email="zodb-dev@zope.org", author_email="zodb-dev@zope.org",
url="http://packages.python.org/BTrees", url="https://github.com/zopefoundation/BTrees",
license="ZPL 2.1", license="ZPL 2.1",
platforms=["any"], platforms=["any"],
packages=find_packages(), packages=find_packages(),
...@@ -147,14 +176,21 @@ setup(name='BTrees', ...@@ -147,14 +176,21 @@ setup(name='BTrees',
ext_modules=ext_modules, ext_modules=ext_modules,
setup_requires=['persistent'], setup_requires=['persistent'],
extras_require={ extras_require={
'test': TESTS_REQUIRE, 'test': TESTS_REQUIRE,
'ZODB': ['ZODB'], 'ZODB': [
'testing': TESTS_REQUIRE + ['nose', 'coverage'], 'ZODB',
'docs': ['Sphinx', 'repoze.sphinx.autointerface'], ],
'docs': [
'Sphinx',
'repoze.sphinx.autointerface',
],
}, },
test_suite="BTrees.tests", test_suite="BTrees.tests",
tests_require=TESTS_REQUIRE, tests_require=TESTS_REQUIRE,
install_requires=REQUIRES, install_requires=REQUIRES,
cmdclass={
'build_ext': optional_build_ext,
},
entry_points="""\ entry_points="""\
""" """
) )
...@@ -6,34 +6,23 @@ envlist = ...@@ -6,34 +6,23 @@ envlist =
py27,py27-pure,pypy,py33,py34,py35,py35-pure,py36,pypy3,w_zodb,coverage,docs py27,py27-pure,pypy,py33,py34,py35,py35-pure,py36,pypy3,w_zodb,coverage,docs
[testenv] [testenv]
usedevelop = true
deps = deps =
zope.interface .[test]
persistent
transaction
commands = commands =
python setup.py -q test -q zope-testrunner --test-path=. --auto-color --auto-progress []
[testenv:py27-pure] [testenv:py27-pure]
basepython = basepython =
python2.7 python2.7
setenv = setenv =
PURE_PYTHON = 1 PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache
deps =
{[testenv]deps}
commands =
python setup.py -q test -q
[testenv:py35-pure] [testenv:py35-pure]
basepython = basepython =
python3.5 python3.5
setenv = setenv =
PURE_PYTHON = 1 PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache
deps =
{[testenv]deps}
commands =
python setup.py -q test -q
#[testenv:jython] #[testenv:jython]
#commands = #commands =
...@@ -42,29 +31,19 @@ commands = ...@@ -42,29 +31,19 @@ commands =
[testenv:w_zodb] [testenv:w_zodb]
basepython = basepython =
python2.7 python2.7
commands =
python setup.py -q test -q
deps = deps =
zope.interface {[testenv]deps}
persistent
transaction
ZODB ZODB
nose
coverage
nosexcover
[testenv:coverage] [testenv:coverage]
basepython = basepython =
python2.7 python2.7
commands = commands =
nosetests --with-xunit --with-xcoverage --cover-package=BTrees coverage run -m zope.testrunner --test-path=. --auto-color --auto-progress []
coverage report
deps = deps =
zope.interface {[testenv]deps}
persistent
transaction
nose
coverage coverage
nosexcover
[testenv:docs] [testenv:docs]
basepython = basepython =
...@@ -73,8 +52,4 @@ commands = ...@@ -73,8 +52,4 @@ commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
deps = deps =
zope.interface .[docs]
persistent
transaction
Sphinx
repoze.sphinx.autointerface
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment