diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8211421..28ca724 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Unit tests and code coverage +name: Unit tests on: push: @@ -20,7 +20,7 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: true steps: @@ -43,22 +43,3 @@ jobs: - name: Test with pytest run: | pytest - - coverage: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install dependencies and run tests - run: | - python -m pip install --upgrade pip - python -m pip install pytest-cov - python -m pip install . - python -m pytest --cov=./ - - uses: codecov/codecov-action@v1 - with: - fail_ci_if_error: true diff --git a/README.md b/README.md new file mode 100644 index 0000000..c709f3d --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://rwa-python.readthedocs.io/en/latest/) +[![](https://github.com/DecBayComp/RWA-python/actions/workflows/ci.yml/badge.svg)](https://github.com/DecBayComp/RWA-python/actions/workflows/ci.yml) +[![](https://codecov.io/gh/DecBayComp/RWA-python/branch/master/graph/badge.svg)](https://codecov.io/gh/DecBayComp/RWA-python) + +# RWA-python + +**RWA-python** serializes Python datatypes and stores them in HDF5 files. + +## Code example + +In module *A* referenced in *sys.path*: + +```python +class CustomClass(object): + def __init__(self, arg=None): + self.attr = arg +``` + +In module *B*: + +```python +from A import CustomClass +from rwa import HDF5Store + +# make any complex construct +any_object = CustomClass((CustomClass('a'), dict(b=1))) + +# serialize +hdf5 = HDF5Store('my_file.h5', 'w') +hdf5.poke('my object', any_object) +hdf5.close() + +# deserialize +hdf5 = HDF5Store('my_file.h5', 'r') +reloaded_object = hdf5.peek('my object') +hdf5.close() +``` + +## Introduction + +With Python3, **RWA-python** serialization is fully automatic for types with *__slots__* defined or such that the *__init__* constructor does not require any input argument. + +The library generates serialization schemes for most custom types. +When deserializing objects, it also looks for and loads the modules where the corresponding types are defined. + +If **RWA-python** complains about a type that cannot be serialized, a partial fix consists of ignoring this datatype: + +```python +hdf5_not_storable(type(unserializable_object)) +``` + +With Python2, the library requires explicit definitions in most cases. +In addition, string typing is sometimes problematic. Non-ascii characters should be explicit unicode. + + +## Installation + +Python >= 3.5 is required. **RWA-python** may still work with Python 2.7 but support has been dropped. + +Windows users should favor Conda for installing **RWA-python**, as Conda will seamlessly install the HDF5 standard library which is a required dependency. + +For other users, *pip* should work just fine: +``` +pip install --user rwa-python +``` +*pip install* will install some Python dependencies if missing, but you may still need to install the [HDF5 reference library](https://tramway.readthedocs.io/en/latest/libhdf5.html). +Note that most package managers include this library. + +The **RWA-python** package can also be installed using Conda: +``` +conda install rwa-python -c conda-forge +``` +or poetry: +``` +poetry add rwa-python +``` + +## See also + +**RWA-python** is on [readthedocs](https://rwa-python.readthedocs.io/en/latest/). + diff --git a/containers/rwa-focal b/containers/rwa-focal new file mode 120000 index 0000000..acfd8f7 --- /dev/null +++ b/containers/rwa-focal @@ -0,0 +1 @@ +rwa-openmpi-dev \ No newline at end of file diff --git a/containers/rwa-jammy b/containers/rwa-jammy new file mode 100644 index 0000000..c210a11 --- /dev/null +++ b/containers/rwa-jammy @@ -0,0 +1,144 @@ +Bootstrap: docker +From: ubuntu:22.04 + +%help +RWA-python is available in multiple Python environments: + python2.7 (alias python2) + python3.5 + python3.6 + python3.7 + python3.8 + python3.9 + python3.10 (alias python3) + python3.11 +The container OS is Ubuntu Jammy. + +%setup + + echo "fr_FR.UTF-8 UTF-8" > ${SINGULARITY_ROOTFS}/etc/locale.gen + for OLD in 2.7 3.5 3.6; do + if ! [ -f ${SINGULARITY_ROOTFS}/root/get-pip$OLD.py ]; then + wget -P ${SINGULARITY_ROOTFS}/root/ -- https://bootstrap.pypa.io/pip/$OLD/get-pip.py + mv ${SINGULARITY_ROOTFS}/root/get-pip.py ${SINGULARITY_ROOTFS}/root/get-pip$OLD.py + fi + done + if ! [ -f ${SINGULARITY_ROOTFS}/root/get-pip.py ]; then + wget -P ${SINGULARITY_ROOTFS}/root/ -- https://bootstrap.pypa.io/get-pip.py + fi + + # test local changes that have not been committed yet; + # to be run from any subdirectory in the RWA-python directory, e.g. containers, tests... + LOCAL="$(pwd)/.." + CONTAINED=${SINGULARITY_ROOTFS}/root/RWA-python + mkdir -p ${CONTAINED} + cp -u -t ${CONTAINED}/ \ + ${LOCAL}/setup.py ${LOCAL}/requirements.txt \ + ${LOCAL}/README.rst ${LOCAL}/LICENSE + cp -ru -t ${CONTAINED}/ ${LOCAL}/rwa ${LOCAL}/.git + cd ${CONTAINED} && git clean -xfd + +%post + + ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime + apt-get update -y + apt-get install -y --no-install-recommends locales + locale-gen + + apt-get install -y --no-install-recommends libhdf5-openmpi-dev \ + build-essential git software-properties-common pkg-config + + add-apt-repository -y ppa:deadsnakes/ppa + apt-get update -y + apt-get install -y --no-install-recommends \ + python2.7 python2.7-dev \ + python3.5 python3.5-dev \ + python3.6 python3.6-dev python3.6-distutils \ + python3.7 python3.7-dev python3.7-distutils \ + python3.8 python3.8-dev python3.8-venv \ + python3.9 python3.9-dev \ + python3.10 python3.10-dev python3.10-venv \ + python3.11 python3.11-dev python3.11-venv + + # python 3.8 and 3.10 won't work with get-pip.py without package venv + + for OLD in 2.7 3.5 3.6; do + python$OLD /root/get-pip$OLD.py + done + for VER in 3.7 3.8 3.9 3.10 3.11; do + python$VER /root/get-pip.py + done + + cd /root + if [ -d RWA-python ]; then + cd RWA-python + git pull || true + else + git clone git://github.com/DecBayComp/RWA-python -b dev + cd RWA-python + fi + + export CC=mpicc + export HDF5_MPI="ON" + export HDF5_LIB=/usr/lib/x86_64-linux-gnu + cur=`pwd`; cd $HDF5_LIB + ln -s libhdf5_openmpi.so libhdf5.so + cd $cur + + export LC_ALL=C + for version in 2.7 3.5 3.6 3.7 3.8 3.9 3.10 3.11; do + python="python${version}" + pip="$python -m pip" + pip_install="$pip install -U" + if [ "$version" = "3.6" -o "$version" = "3.7" -o "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" -o "$version" = "3.11" ]; then + $pip_install --force-reinstall setuptools + $pip_install pytest + fi + if [ "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" -o "$version" = "3.11" ]; then + $pip_install mpi4py + #$pip_install --no-binary=h5py h5py + fi + + #$pip uninstall -qy rwa-python || true + $pip_install . -r requirements.txt + + for pkg in scipy pandas; do + if [ -z "$($pip show $pkg)" ]; then + $pip_install $pkg + fi + done + done + + mkdir -p /pasteur + +%runscript + + python="python2.7" + if [ -n "$1" ]; then + if [ "$1" = "-2" -o "$1" = "-27" ]; then + # nothing to do + shift + elif [ "$1" = "-3" -o "$1" = "-35" ]; then + python="python3.5" + shift + elif [ "$1" = "-36" ]; then + python="python3.6" + shift + elif [ "$1" = "-37" ]; then + python="python3.7" + shift + elif [ "$1" = "-38" ]; then + python="python3.8" + shift + elif [ "$1" = "-39" ]; then + python="python3.9" + shift + elif [ "$1" = "-310" ]; then + python="python3.10" + shift + elif [ "$1" = "-311" ]; then + python="python3.11" + shift + fi + fi + exec $python $@ + diff --git a/containers/rwa-openmpi-dev b/containers/rwa-openmpi-dev index 5e3f029..7b0e652 100644 --- a/containers/rwa-openmpi-dev +++ b/containers/rwa-openmpi-dev @@ -10,6 +10,7 @@ RWA-python is available in multiple Python environments: python3.8 (alias python3) python3.9 python3.10 + python3.11 The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. %setup @@ -18,7 +19,7 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. for OLD in 2.7 3.5 3.6; do if ! [ -f ${SINGULARITY_ROOTFS}/root/get-pip$OLD.py ]; then wget -P ${SINGULARITY_ROOTFS}/root/ -- https://bootstrap.pypa.io/pip/$OLD/get-pip.py - mv ${SINGULARITY_ROOTFS}/root/get-pip.py ${SINGULARITY_ROOTFS}/root/get-pip$OLD.py + mv ${SINGULARITY_ROOTFS}/root/get-pip.py ${SINGULARITY_ROOTFS}/root/get-pip$OLD.py fi done if ! [ -f ${SINGULARITY_ROOTFS}/root/get-pip.py ]; then @@ -51,18 +52,19 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. apt-get install -y --no-install-recommends \ python2.7 python2.7-dev \ python3.5 python3.5-dev \ - python3.6 python3.6-dev \ - python3.7 python3.7-dev \ + python3.6 python3.6-dev python3.6-distutils \ + python3.7 python3.7-dev python3.7-distutils \ python3.8 python3.8-dev python3.8-venv \ python3.9 python3.9-dev \ - python3.10 python3.10-dev python3.10-venv + python3.10 python3.10-dev python3.10-venv \ + python3.11 python3.11-dev python3.11-venv # python 3.8 and 3.10 won't work with get-pip.py without package venv for OLD in 2.7 3.5 3.6; do python$OLD /root/get-pip$OLD.py done - for VER in 3.7 3.8 3.9 3.10; do + for VER in 3.7 3.8 3.9 3.10 3.11; do python$VER /root/get-pip.py done @@ -71,7 +73,7 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. cd RWA-python git pull || true else - git clone git://github.com/DecBayComp/RWA-python -b master + git clone git://github.com/DecBayComp/RWA-python -b dev cd RWA-python fi @@ -83,16 +85,15 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. cd $cur export LC_ALL=C - for version in 2.7 3.5 3.6 3.7 3.8 3.9 3.10; do + for version in 2.7 3.5 3.6 3.7 3.8 3.9 3.10 3.11; do python="python${version}" - pip_install="$python -m pip install -U" - if [ "$version" = "3.6" -o "$version" = "3.7" -o "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" ]; then + pip="$python -m pip" + pip_install="$pip install -U" + if [ "$version" = "3.6" -o "$version" = "3.7" -o "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" -o "$version" = "3.11" ]; then $pip_install --force-reinstall setuptools $pip_install pytest - else - : #$pip install --upgrade pip fi - if [ "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" ]; then + if [ "$version" = "3.8" -o "$version" = "3.9" -o "$version" = "3.10" -o "$version" = "3.11" ]; then $pip_install mpi4py #$pip_install --no-binary=h5py h5py fi @@ -101,7 +102,7 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. $pip_install . -r requirements.txt for pkg in scipy pandas; do - if [ -z "$($pip show $pkg)" ]; then + if [ -z "$($pip show -q $pkg)" ]; then $pip_install $pkg fi done @@ -134,6 +135,9 @@ The container OS is Ubuntu Focal and can run on top of old OSes like CentOS6. elif [ "$1" = "-310" ]; then python="python3.10" shift + elif [ "$1" = "-311" ]; then + python="python3.11" + shift fi fi exec $python $@ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f974cd9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "rwa" +version = "0.9.2" +description = "HDF5-based serialization library for Python datatypes" +authors = ["François Laurent "] +license = "Apache 2.0" +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.8,<3.12" +six = "^1.16.0" +numpy = "^1.24.3" +scipy = "^1.10.1" +pandas = "^2.0.2" +h5py = "^3.8.0" + + +[tool.poetry.group.dev.dependencies] +pytest = "^7.3.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/rwa/generic.py b/rwa/generic.py index 0928d07..47b9335 100644 --- a/rwa/generic.py +++ b/rwa/generic.py @@ -589,7 +589,7 @@ def poke_assoc(store, objname, assoc, container, visited=None, _stack=None): # peeks -def default_peek(python_type, exposes): +def default_peek(python_type, exposes, excess_attributes=[]): """ Autoserializer factory. @@ -601,6 +601,9 @@ def default_peek(python_type, exposes): exposes (iterable): sequence of attributes. + excess_attributes (iterable): set of unrequired attributes that might + have been serialized by other versions of the provider library. + Returns: callable: deserializer (`peek` routine). @@ -641,7 +644,9 @@ def peek(store, container, _stack=None): try: setattr(obj, attr, val) except AttributeError: - raise missing(attr) + if attr not in excess_attributes: + print(excess_attributes) + raise missing(attr) return obj else: def peek(store, container, _stack=None): @@ -655,7 +660,8 @@ def peek(store, container, _stack=None): try: setattr(obj, attr, val) except AttributeError: - raise missing(attr) + if attr not in excess_attributes: + raise missing(attr) return obj return peek diff --git a/rwa/pandas.py b/rwa/pandas.py index b0183e7..8aeea80 100644 --- a/rwa/pandas.py +++ b/rwa/pandas.py @@ -151,6 +151,20 @@ def poke_multiindex(service, ixname, ix, parent_container, *args, **kwargs): attr = tuple( getattr(ix, attrname) ) service.poke(attrname, attr, container, *args, **kwargs) + try: + # Int64Index is missing in 2.0.2 + pandas_Int64Index = pandas.Int64Index + peek_int64index = peek_numerical_index(pandas.Int64Index) + except AttributeError: + # convert Int64Index into Index + class pandas_Int64Index(object): + """ + Placeholder type. + """ + __slot__ = () + pass + peek_int64index = peek_numerical_index(pandas.Index, lambda a: a.astype(np.int64)) + try: # UInt64Index is missing in 0.17.1 pandas_UInt64Index = pandas.UInt64Index @@ -163,7 +177,21 @@ class pandas_UInt64Index(object): """ __slot__ = () pass - peek_uint64index = peek_numerical_index(pandas.Int64Index, lambda a: a.astype(np.int64)) + peek_uint64index = peek_int64index + + try: + # Float64Index is missing in 2.0.2 + pandas_Float64Index = pandas.Float64Index + peek_float64index = peek_numerical_index(pandas.Float64Index) + except AttributeError: + # convert Float64Index into Index + class pandas_Float64Index(object): + """ + Placeholder type. + """ + __slot__ = () + pass + peek_float64index = peek_numerical_index(pandas.Index, lambda a: a.astype(np.float64)) if True: poke_rangeindex = poke([('start','_start'), ('stop','_stop'), ('step','_step'), 'name']) @@ -209,19 +237,17 @@ def peek_rangeindex(*args, **kwargs): key='Python.pandas.core.index.Index', \ handlers=StorableHandler(poke=poke_index, peek=peek_index, \ peek_option='pandas.index.force_unicode')), \ - Storable(pandas.Int64Index, \ + Storable(pandas_Int64Index, \ key='Python.pandas.core.index.Int64Index', \ - handlers=StorableHandler(poke=poke_index, \ - peek=peek_numerical_index(pandas.Int64Index), \ + handlers=StorableHandler(poke=poke_index, peek=peek_int64index, \ peek_option='pandas.index.force_unicode')), \ Storable(pandas_UInt64Index, \ key='Python.pandas.core.index.UInt64Index', \ handlers=StorableHandler(poke=poke_index, peek=peek_uint64index, \ peek_option='pandas.index.force_unicode')), \ - Storable(pandas.Float64Index, \ + Storable(pandas_Float64Index, \ key='Python.pandas.core.index.Float64Index', \ - handlers=StorableHandler(poke=poke_index, \ - peek=peek_numerical_index(pandas.Float64Index), \ + handlers=StorableHandler(poke=poke_index, peek=peek_float64index, \ peek_option='pandas.index.force_unicode')), \ Storable(pandas_RangeIndex, \ key='Python.pandas.core.index.RangeIndex', \ diff --git a/rwa/scipy.py b/rwa/scipy.py index 60adaa1..835c3b5 100644 --- a/rwa/scipy.py +++ b/rwa/scipy.py @@ -104,13 +104,17 @@ def lil_peek(*args, **kwargs): Voronoi_v1_exposes = [ '_points', 'max_bound', 'min_bound', 'ndim', 'npoints', 'point_region', 'regions', 'ridge_points', 'ridge_vertices', 'vertices' ] _scipy_spatial_types = [ - ('Delaunay', Delaunay_exposes, Delaunay_v1_exposes, ('simplices', )), + ('Delaunay', Delaunay_exposes, Delaunay_v1_exposes, ('vertices', 'simplices')), ('ConvexHull', ConvexHull_exposes, ConvexHull_v1_exposes, ('vertices', 'equations')), ('Voronoi', Voronoi_exposes, Voronoi_v1_exposes, ('regions', 'point_region'))] def scipy_spatial_storable(name, exposes, v1_exposes, check): _fallback = namedtuple(name, exposes) - _type = getattr(scipy.spatial.qhull, name) + try: + _type = getattr(scipy.spatial, name) + except AttributeError: + # deprecated location + _type = getattr(scipy.spatial.qhull, name) def _init(_exposes): def __init(*args): #print(args) # debug @@ -146,13 +150,16 @@ def __init(*args): return __init handlers = [handler(_init(exposes), exposes, version=(0,))] # Py2 if six.PY3: - auto = default_storable(_type) + spatial_peek = lambda _, exposes: default_peek(_type, exposes, excess_attributes=check) + auto = default_storable(_type, peek=spatial_peek) assert not auto.handlers[1:] assert handlers[0].version[0] < auto.handlers[0].version[0] handlers.append(auto.handlers[0]) elif six.PY2 and v1_exposes: handlers.append(handler(_init(v1_exposes), v1_exposes, version=(1,))) - return ScipySpatialStorable(_type, handlers=handlers) + return ScipySpatialStorable(_type, + key='Python.scipy.spatial._qhull.' + _type.__name__, + handlers=handlers) spatial_storables += \ [ scipy_spatial_storable(*_specs) for _specs in _scipy_spatial_types ] diff --git a/setup.py b/setup.py index 79766e0..e8adb8b 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( name = 'rwa-python', - version = '0.9.1', + version = '0.9.2', description = 'HDF5-based serialization library for Python datatypes', long_description = long_description, url = 'https://github.com/DecBayComp/RWA-python', @@ -33,8 +33,6 @@ license = 'Apache 2.0', classifiers = [ 'License :: OSI Approved :: Apache Software License', - #'Programming Language :: Python :: 2', - #'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -42,6 +40,7 @@ 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], packages = ['rwa'], install_requires = install_requires, diff --git a/tests/multi-py_compat.sh b/tests/multi-py_compat.sh index 2f5df76..64bfbbf 100755 --- a/tests/multi-py_compat.sh +++ b/tests/multi-py_compat.sh @@ -1,6 +1,6 @@ #!/bin/sh -versions="2.7 3.5 3.6 3.7 3.8 3.9 3.10" +versions="3.5 3.6 3.7 3.8 3.9 3.10 3.11" #versions="3.6 2.7" if [ "$(pwd | rev | cut -d/ -f1 | rev)" = "tests" ]; then @@ -17,17 +17,22 @@ fi if ! [ -f "$container" -o -h "$container" ]; then cd ../containers # if this crashes, $0 is not run from the tests directory as it should be echo "No container found; building one..." - echo "singularity build --fakeroot rwa-openmpi-dev.sif rwa-openmpi-dev" - singularity build --fakeroot rwa-openmpi-dev.sif rwa-openmpi-dev || exit + if [ -z "$(which apptainer)" ]; then + echo "singularity build --fakeroot rwa-openmpi-dev.sif rwa-focal" + singularity build --fakeroot rwa-openmpi-dev.sif rwa-focal || exit + else + echo "apptainer build rwa-openmpi-dev.sif rwa-jammy" + apptainer build rwa-openmpi-dev.sif rwa-jammy || exit + fi echo "======================================" echo "Container ready; starting the tests..." echo "======================================" cd ../tests fi -hdf5_file=$(tempfile) || exit -poke_script=$(tempfile) || exit -peek_script=$(tempfile) || exit +hdf5_file=$(mktemp) || exit +poke_script=$(mktemp) || exit +peek_script=$(mktemp) || exit trap "rm -f -- '$hdf_file' '$poke_script' '$peek_script'" EXIT diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 5c57322..9982404 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -7,16 +7,28 @@ from rwa.generic import * from rwa.hdf5 import HDF5Store -import os.path +import os import numpy as np -from pandas import Index, Int64Index, Float64Index, MultiIndex, Series, DataFrame, \ - Categorical, CategoricalIndex +from pandas import Index, MultiIndex, Series, DataFrame, Categorical, \ + CategoricalIndex try: from pandas import UInt64Index except ImportError: _test_uint64index = False else: _test_uint64index = True +try: + from pandas import Int64Index +except ImportError: + _test_int64index = False +else: + _test_int64index = True +try: + from pandas import Float64Index +except ImportError: + _test_float64index = False +else: + _test_float64index = True try: from pandas import RangeIndex except ImportError: @@ -47,11 +59,13 @@ def test_num_indices(self, tmpdir): # test values array = np.r_[np.arange(1,4), 5, np.arange(7,10)] data = {'index0': Index(array, name=b'i0'), - 'index1': Index(list(b'abde')), - 'int64index': Int64Index(array.astype(np.int64), name=b'i64'), - 'float64index': Float64Index(array.astype(np.float64))} + 'index1': Index(list(b'abde'))} if _test_uint64index: data['uint64index'] = UInt64Index(array.astype(np.uint64)) + if _test_int64index: + data['int64index'] = Int64Index(array.astype(np.int64), name=b'i64') + if _test_float64index: + data['float64index'] = Float64Index(array.astype(np.float64)) # # write store = HDF5Store(test_file, 'w') @@ -67,7 +81,8 @@ def test_num_indices(self, tmpdir): print(t) val = store.peek(t) assert type(val) is type(data[t]) - assert val.dtype == data[t].dtype + if os.name != 'nt': + assert val.dtype == data[t].dtype assert np.all(val.values == data[t].values) assert val.name == as_unicode(data[t].name) finally: @@ -92,9 +107,14 @@ def test_rangeindex(self, tmpdir): try: val = store.peek(t) assert type(val) is type(data) - assert val._start == data._start - assert val._stop == data._stop - assert val._step == data._step + if hasattr(val, '_start'): + assert val._start == data._start + assert val._stop == data._stop + assert val._step == data._step + else: + assert val.start == data.start + assert val.stop == data.stop + assert val.step == data.step assert val.name == as_unicode(data.name) finally: store.close() diff --git a/tests/test_scipy.py b/tests/test_scipy.py index 19b7825..8d9b43d 100644 --- a/tests/test_scipy.py +++ b/tests/test_scipy.py @@ -11,6 +11,19 @@ import numpy as np from scipy import sparse, spatial +try: + ConvexHull = spatial.ConvexHull +except AttributeError: + ConvexHull = spatial.qhull.ConvexHull +try: + Delaunay = spatial.Delaunay +except AttributeError: + Delaunay = spatial.qhull.Delaunay +try: + Voronoi = spatial.Voronoi +except AttributeError: + Voronoi = spatial.qhull.Voronoi + class TestSciPyTypes(object): @@ -52,11 +65,11 @@ def test_sparse_types(self, tmpdir): def test_spatial_types(self, tmpdir): test_file = os.path.join(tmpdir.strpath, 'test.h5') # test values - data = {'convexhull': spatial.qhull.ConvexHull(np.random.rand(5,2)), - 'convexhull_with_options': spatial.qhull.ConvexHull(np.random.rand(5,2), qhull_options='QbB'), - 'delaunay': spatial.qhull.Delaunay(np.random.rand(5,2)), - 'voronoi': spatial.qhull.Voronoi(np.random.rand(5,2)), - 'voronoi_with_options': spatial.qhull.Voronoi(np.random.rand(5,2), True, qhull_options='Qbb Qz'), + data = {'convexhull': ConvexHull(np.random.rand(5,2)), + 'convexhull_with_options': ConvexHull(np.random.rand(5,2), qhull_options='QbB'), + 'delaunay': Delaunay(np.random.rand(5,2)), + 'voronoi': Voronoi(np.random.rand(5,2)), + 'voronoi_with_options': Voronoi(np.random.rand(5,2), True, qhull_options='Qbb Qz'), } # check = [('vertices', True),