From 57cddfff572fd18bbf95021759f2820c6097cfe7 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 3 Dec 2024 08:46:38 +0000 Subject: [PATCH] Error at runtime if zfpy isn't present --- numcodecs/__init__.py | 5 +- numcodecs/zfpy.py | 204 +++++++++++++++++++++--------------------- 2 files changed, 104 insertions(+), 105 deletions(-) diff --git a/numcodecs/__init__.py b/numcodecs/__init__.py index 3e44085c..898992e3 100644 --- a/numcodecs/__init__.py +++ b/numcodecs/__init__.py @@ -125,10 +125,9 @@ register_codec(Fletcher32) # Optional depenedencies -with suppress(ImportError): - from numcodecs.zfpy import ZFPY +from numcodecs.zfpy import ZFPY - register_codec(ZFPY) +register_codec(ZFPY) with suppress(ImportError): from numcodecs.msgpacks import MsgPack diff --git a/numcodecs/zfpy.py b/numcodecs/zfpy.py index eda4bfc2..e70b4ea7 100644 --- a/numcodecs/zfpy.py +++ b/numcodecs/zfpy.py @@ -1,113 +1,113 @@ -import warnings -from contextlib import suppress -from importlib.metadata import PackageNotFoundError, version +import importlib.util +from importlib.metadata import version from types import ModuleType from typing import Optional -_zfpy: Optional[ModuleType] = None +import numpy as np -_zfpy_version: tuple = () -with suppress(PackageNotFoundError): - _zfpy_version = tuple(map(int, version("zfpy").split("."))) +from .abc import Codec +from .compat import ensure_bytes, ensure_contiguous_ndarray, ndarray_copy + +_zfpy: Optional[ModuleType] +_zfpy_spec = importlib.util.find_spec("zfpy") + +if _zfpy_spec is None: + _zfpy = None +else: + import zfpy as _zfpy # type: ignore[no-redef] -if _zfpy_version: - # Check NumPy version - _numpy_version: tuple = tuple(map(int, version("numpy").split('.'))) + +def _check_compatible_numpy() -> None: + _numpy_version = tuple(map(int, version("numpy").split('.'))) + _zfpy_version = tuple(map(int, version("zfpy").split("."))) if _numpy_version >= (2, 0, 0) and _zfpy_version <= (1, 0, 1): # pragma: no cover _zfpy_version = () - warnings.warn( + raise RuntimeError( "NumPy version >= 2.0.0 detected. The zfpy library is incompatible with this version of NumPy. " "Please downgrade to NumPy < 2.0.0 or wait for an update from zfpy.", - UserWarning, - stacklevel=2, ) - else: - with suppress(ImportError): - import zfpy as _zfpy # type: ignore[no-redef] - -if _zfpy: - import numpy as np - - from .abc import Codec - from .compat import ensure_bytes, ensure_contiguous_ndarray, ndarray_copy - - # noinspection PyShadowingBuiltins - class ZFPY(Codec): - """Codec providing compression using zfpy via the Python standard - library. - - Parameters - ---------- - mode : integer - One of the zfpy mode choice, e.g., ``zfpy.mode_fixed_accuracy``. - tolerance : double, optional - A double-precision number, specifying the compression accuracy needed. - rate : double, optional - A double-precision number, specifying the compression rate needed. - precision : int, optional - A integer number, specifying the compression precision needed. - - """ - - codec_id = "zfpy" - - def __init__( - self, - mode=_zfpy.mode_fixed_accuracy, - tolerance=-1, - rate=-1, - precision=-1, - compression_kwargs=None, - ): - self.mode = mode - if mode == _zfpy.mode_fixed_accuracy: - self.compression_kwargs = {"tolerance": tolerance} - elif mode == _zfpy.mode_fixed_rate: - self.compression_kwargs = {"rate": rate} - elif mode == _zfpy.mode_fixed_precision: - self.compression_kwargs = {"precision": precision} - - self.tolerance = tolerance - self.rate = rate - self.precision = precision - - def encode(self, buf): - # not flatten c-order array and raise exception for f-order array - if not isinstance(buf, np.ndarray): - raise TypeError( - "The zfp codec does not support none numpy arrays." - f" Your buffers were {type(buf)}." - ) - if buf.flags.c_contiguous: - flatten = False - else: - raise ValueError( - "The zfp codec does not support F order arrays. " - f"Your arrays flags were {buf.flags}." - ) - buf = ensure_contiguous_ndarray(buf, flatten=flatten) - - # do compression - return _zfpy.compress_numpy(buf, write_header=True, **self.compression_kwargs) - - def decode(self, buf, out=None): - # normalise inputs - buf = ensure_bytes(buf) - if out is not None: - out = ensure_contiguous_ndarray(out) - - # do decompression - dec = _zfpy.decompress_numpy(buf) - - # handle destination - if out is not None: - return ndarray_copy(dec, out) - else: - return dec - - def __repr__(self): - return ( - f"{type(self).__name__}(mode={self.mode!r}, " - f"tolerance={self.tolerance}, rate={self.rate}, " - f"precision={self.precision})" + + +class ZFPY(Codec): + """Codec providing compression using zfpy via the Python standard + library. + + Parameters + ---------- + mode : integer + One of the zfpy mode choice, e.g., ``zfpy.mode_fixed_accuracy``. + tolerance : double, optional + A double-precision number, specifying the compression accuracy needed. + rate : double, optional + A double-precision number, specifying the compression rate needed. + precision : int, optional + A integer number, specifying the compression precision needed. + + """ + + codec_id = "zfpy" + + def __init__( + self, + mode=_zfpy.mode_fixed_accuracy, + tolerance=-1, + rate=-1, + precision=-1, + compression_kwargs=None, + ): + if _zfpy is None: + raise RuntimeError("The ZFPY codec requires the 'zfpy' package to be installed.") + _check_compatible_numpy() + + self.mode = mode + if mode == _zfpy.mode_fixed_accuracy: + self.compression_kwargs = {"tolerance": tolerance} + elif mode == _zfpy.mode_fixed_rate: + self.compression_kwargs = {"rate": rate} + elif mode == _zfpy.mode_fixed_precision: + self.compression_kwargs = {"precision": precision} + + self.tolerance = tolerance + self.rate = rate + self.precision = precision + + def encode(self, buf): + # not flatten c-order array and raise exception for f-order array + if not isinstance(buf, np.ndarray): + raise TypeError( + "The zfp codec does not support none numpy arrays." + f" Your buffers were {type(buf)}." ) + if buf.flags.c_contiguous: + flatten = False + else: + raise ValueError( + "The zfp codec does not support F order arrays. " + f"Your arrays flags were {buf.flags}." + ) + buf = ensure_contiguous_ndarray(buf, flatten=flatten) + + # do compression + return _zfpy.compress_numpy(buf, write_header=True, **self.compression_kwargs) + + def decode(self, buf, out=None): + # normalise inputs + buf = ensure_bytes(buf) + if out is not None: + out = ensure_contiguous_ndarray(out) + + # do decompression + dec = _zfpy.decompress_numpy(buf) + + # handle destination + if out is not None: + return ndarray_copy(dec, out) + else: + return dec + + def __repr__(self): + return ( + f"{type(self).__name__}(mode={self.mode!r}, " + f"tolerance={self.tolerance}, rate={self.rate}, " + f"precision={self.precision})" + )