From 63b5edcbaaf1af4a488a1856929989042feb8192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= Date: Sun, 28 May 2023 14:49:37 +0200 Subject: [PATCH 1/3] API: deprecate compat and selected lib utils --- numpy/__init__.py | 37 +++++++++++++++++++++------ numpy/__init__.pyi | 3 --- numpy/compat/__init__.py | 9 +++++++ numpy/core/tests/test_deprecations.py | 20 +++++++++++++++ numpy/lib/utils.py | 3 +++ pytest.ini | 1 + 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/numpy/__init__.py b/numpy/__init__.py index ae86313874eb..6e5f1b3b720f 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -135,10 +135,10 @@ # mapping of {name: (value, deprecation_msg)} __deprecated_attrs__ = {} + __expired_functions__ = {} from . import core from .core import * - from . import compat from . import exceptions from . import dtypes from . import lib @@ -155,6 +155,15 @@ from . import matrixlib as _mat from .matrixlib import * + # Deprecations introduced in 1.25.0, 2023-05-xx TODO update day + from . import compat + + __deprecated_attrs__["compat"] = (compat, + "`np.compat`, which was used during the Python 2 to 3 transition," + " is deprecated since 1.25.0, and will be removed") + + del compat + # Deprecations introduced in NumPy 1.20.0, 2020-06-06 import builtins as _builtins @@ -232,9 +241,20 @@ __all__.extend(['__version__', 'show_config']) __all__.extend(core.__all__) __all__.extend(_mat.__all__) - __all__.extend(lib.__all__) __all__.extend(['linalg', 'fft', 'random', 'ctypeslib', 'ma']) + _lib_expired_utils_names = ["byte_bounds", "safe_eval", "who"] + lib__all__ = lib.__all__[:] + for name in _lib_expired_utils_names: + lib__all__.remove(name) + __expired_functions__[name] = ( + f"{name} is deprecated and removed from the " + f"main namespace. If you still want to use it " + f"please use it directly from np.lib or np.lib.utils" + ) + del byte_bounds, safe_eval, who + __all__.extend(lib__all__) + # Remove one of the two occurrences of `issubdtype`, which is exposed as # both `numpy.core.issubdtype` and `numpy.lib.issubdtype`. __all__.remove('issubdtype') @@ -259,12 +279,13 @@ # a warning, and calling the function will raise an exception. _financial_names = ['fv', 'ipmt', 'irr', 'mirr', 'nper', 'npv', 'pmt', 'ppmt', 'pv', 'rate'] - __expired_functions__ = { - name: (f'In accordance with NEP 32, the function {name} was removed ' - 'from NumPy version 1.20. A replacement for this function ' - 'is available in the numpy_financial library: ' - 'https://pypi.org/project/numpy-financial') - for name in _financial_names} + for name in _financial_names: + __expired_functions__[name] = ( + f'In accordance with NEP 32, the function {name} was removed ' + 'from NumPy version 1.20. A replacement for this function ' + 'is available in the numpy_financial library: ' + 'https://pypi.org/project/numpy-financial' + ) # Filter out Cython harmless warnings warnings.filterwarnings("ignore", message="numpy.dtype size changed") diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index da1e98dd672c..01e8e9639a77 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -626,10 +626,7 @@ from numpy.lib.utils import ( get_include as get_include, info as info, source as source, - who as who, lookfor as lookfor, - byte_bounds as byte_bounds, - safe_eval as safe_eval, show_runtime as show_runtime, ) diff --git a/numpy/compat/__init__.py b/numpy/compat/__init__.py index 504f8b0030ba..7724703e96c8 100644 --- a/numpy/compat/__init__.py +++ b/numpy/compat/__init__.py @@ -7,8 +7,17 @@ * compatibility * we may only need a small subset of the copied library/module +This module is deprecated since 1.25.0 and will be removed in future versions. + """ +import warnings + +warnings.warn( + "`np.compat`, which was used during the Python 2 to 3 transition," + " is deprecated since 1.25.0, and will be removed", stacklevel=2 +) + from .._utils import _inspect from .._utils._inspect import getargspec, formatargspec from . import py3k diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 3ada39e909af..9f3a285d2306 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -815,3 +815,23 @@ def test_deprecated_np_math(self): def test_deprecated_np_lib_math(self): self.assert_deprecated(lambda: np.lib.math) + + +class TestCompatImport(_DeprecationTestCase): + # Deprecated in Numpy 1.25.0, 2023-05-xx TODO update day + def test_deprecated_np_compat(self): + self.assert_deprecated(lambda: np.compat) + + +class TestLibImports(_DeprecationTestCase): + # Deprecated in Numpy 1.25.0, 2023-05-xx TODO update day + def test_lib_functions_deprecation(self): + self.assert_deprecated(lambda: np.byte_bounds) + self.assert_deprecated(lambda: np.safe_eval) + self.assert_deprecated(lambda: np.who) + + def test_lib_functions_deprecation_call(self): + from numpy.lib import byte_bounds, safe_eval, who + self.assert_deprecated(lambda: byte_bounds(np.array([1]))) + self.assert_deprecated(lambda: safe_eval("None")) + self.assert_deprecated(lambda: who()) diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index 6174c8d08764..f1dbe126f726 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -267,6 +267,7 @@ def deprecate_with_doc(msg): # Determine if two arrays can share memory #-------------------------------------------- +@deprecate def byte_bounds(a): """ Returns pointers to the end-points of an array. @@ -324,6 +325,7 @@ def byte_bounds(a): #----------------------------------------------------------------------------- +@deprecate def who(vardict=None): """ Print the NumPy arrays in the given dictionary. @@ -1024,6 +1026,7 @@ def _getmembers(item): return members +@deprecate def safe_eval(source): """ Protected string evaluation. diff --git a/pytest.ini b/pytest.ini index 29ec1d1a4fce..f4317c790a41 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,6 +13,7 @@ filterwarnings = ignore::UserWarning:cpuinfo, ignore: divide by zero encountered in log ignore: invalid value encountered in log + ignore:`np.compat`, which was used during the Python 2 to 3 transition, is deprecated # Matrix PendingDeprecationWarning. ignore:the matrix subclass is not ignore:Importing from numpy.matlib is From 2f36ac5a60871ca7f5f17e7c04ca159fba4c5bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= Date: Sat, 10 Jun 2023 17:03:52 +0200 Subject: [PATCH 2/3] API: remove np.compat usage from codebase --- numpy/__init__.py | 25 +++------------ numpy/__init__.pyi | 3 ++ numpy/_utils/_convertions.py | 18 +++++++++++ numpy/compat/__init__.py | 13 ++++---- numpy/compat/tests/test_compat.py | 22 -------------- numpy/core/_methods.py | 6 ++-- numpy/core/_type_aliases.py | 10 +++--- numpy/core/defchararray.py | 3 +- numpy/core/memmap.py | 8 +++-- numpy/core/numerictypes.py | 1 - numpy/core/records.py | 4 +-- numpy/core/setup.py | 1 - numpy/core/src/multiarray/methods.h | 4 +-- numpy/core/tests/test_datetime.py | 7 +++-- numpy/core/tests/test_deprecations.py | 13 +------- numpy/core/tests/test_dtype.py | 7 ++--- numpy/core/tests/test_multiarray.py | 4 +-- numpy/core/tests/test_overrides.py | 5 +-- numpy/core/tests/test_records.py | 3 +- numpy/core/tests/test_regression.py | 3 +- numpy/core/tests/test_ufunc.py | 2 +- numpy/f2py/tests/util.py | 6 ++-- numpy/lib/_iotools.py | 2 +- numpy/lib/format.py | 25 +++++++++++---- numpy/lib/npyio.py | 32 +++++++------------- numpy/lib/tests/test_io.py | 9 +++--- numpy/ma/core.py | 4 +-- numpy/ma/tests/test_core.py | 7 ++--- numpy/ma/tests/test_mrecords.py | 3 +- numpy/ma/tests/test_old_ma.py | 2 +- numpy/matrixlib/tests/test_masked_matrix.py | 3 +- numpy/random/tests/test_generator_mt19937.py | 2 +- numpy/random/tests/test_random.py | 2 +- numpy/random/tests/test_randomstate.py | 2 +- numpy/tests/test_public_api.py | 2 ++ numpy/tests/test_reloading.py | 15 ++++----- numpy/typing/tests/data/fail/lib_utils.pyi | 6 ---- numpy/typing/tests/data/pass/lib_utils.py | 3 -- numpy/typing/tests/data/reveal/lib_utils.pyi | 8 ----- pytest.ini | 5 ++- 40 files changed, 132 insertions(+), 168 deletions(-) create mode 100644 numpy/_utils/_convertions.py delete mode 100644 numpy/compat/tests/test_compat.py diff --git a/numpy/__init__.py b/numpy/__init__.py index 6e5f1b3b720f..e141155daccf 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -155,15 +155,6 @@ from . import matrixlib as _mat from .matrixlib import * - # Deprecations introduced in 1.25.0, 2023-05-xx TODO update day - from . import compat - - __deprecated_attrs__["compat"] = (compat, - "`np.compat`, which was used during the Python 2 to 3 transition," - " is deprecated since 1.25.0, and will be removed") - - del compat - # Deprecations introduced in NumPy 1.20.0, 2020-06-06 import builtins as _builtins @@ -241,20 +232,9 @@ __all__.extend(['__version__', 'show_config']) __all__.extend(core.__all__) __all__.extend(_mat.__all__) + __all__.extend(lib.__all__) __all__.extend(['linalg', 'fft', 'random', 'ctypeslib', 'ma']) - _lib_expired_utils_names = ["byte_bounds", "safe_eval", "who"] - lib__all__ = lib.__all__[:] - for name in _lib_expired_utils_names: - lib__all__.remove(name) - __expired_functions__[name] = ( - f"{name} is deprecated and removed from the " - f"main namespace. If you still want to use it " - f"please use it directly from np.lib or np.lib.utils" - ) - del byte_bounds, safe_eval, who - __all__.extend(lib__all__) - # Remove one of the two occurrences of `issubdtype`, which is exposed as # both `numpy.core.issubdtype` and `numpy.lib.issubdtype`. __all__.remove('issubdtype') @@ -339,6 +319,9 @@ def _expired(*args, **kwds): elif attr == 'Tester': "Removed in NumPy 1.25.0" raise RuntimeError("Tester was removed in NumPy 1.25.") + elif attr == "compat": + import numpy.compat as compat + return compat raise AttributeError("module {!r} has no attribute " "{!r}".format(__name__, attr)) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 01e8e9639a77..da1e98dd672c 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -626,7 +626,10 @@ from numpy.lib.utils import ( get_include as get_include, info as info, source as source, + who as who, lookfor as lookfor, + byte_bounds as byte_bounds, + safe_eval as safe_eval, show_runtime as show_runtime, ) diff --git a/numpy/_utils/_convertions.py b/numpy/_utils/_convertions.py new file mode 100644 index 000000000000..ab15a8ba019f --- /dev/null +++ b/numpy/_utils/_convertions.py @@ -0,0 +1,18 @@ +""" +A set of methods retained from np.compat module that +are still used across codebase. +""" + +__all__ = ["asunicode", "asbytes"] + + +def asunicode(s): + if isinstance(s, bytes): + return s.decode('latin1') + return str(s) + + +def asbytes(s): + if isinstance(s, bytes): + return s + return str(s).encode('latin1') diff --git a/numpy/compat/__init__.py b/numpy/compat/__init__.py index 7724703e96c8..729265aa9c27 100644 --- a/numpy/compat/__init__.py +++ b/numpy/compat/__init__.py @@ -7,22 +7,23 @@ * compatibility * we may only need a small subset of the copied library/module -This module is deprecated since 1.25.0 and will be removed in future versions. +This module is deprecated since 1.26.0 and will be removed in future versions. """ import warnings -warnings.warn( - "`np.compat`, which was used during the Python 2 to 3 transition," - " is deprecated since 1.25.0, and will be removed", stacklevel=2 -) - from .._utils import _inspect from .._utils._inspect import getargspec, formatargspec from . import py3k from .py3k import * +warnings.warn( + "`np.compat`, which was used during the Python 2 to 3 transition," + " is deprecated since 1.26.0, and will be removed", + DeprecationWarning, stacklevel=2 +) + __all__ = [] __all__.extend(_inspect.__all__) __all__.extend(py3k.__all__) diff --git a/numpy/compat/tests/test_compat.py b/numpy/compat/tests/test_compat.py deleted file mode 100644 index d4391565ee07..000000000000 --- a/numpy/compat/tests/test_compat.py +++ /dev/null @@ -1,22 +0,0 @@ -from os.path import join -from io import BufferedReader, BytesIO - -from numpy.compat import isfileobj -from numpy.testing import assert_ -from numpy.testing import tempdir - - -def test_isfileobj(): - with tempdir(prefix="numpy_test_compat_") as folder: - filename = join(folder, 'a.bin') - - with open(filename, 'wb') as f: - assert_(isfileobj(f)) - - with open(filename, 'ab') as f: - assert_(isfileobj(f)) - - with open(filename, 'rb') as f: - assert_(isfileobj(f)) - - assert_(isfileobj(BufferedReader(BytesIO())) is False) diff --git a/numpy/core/_methods.py b/numpy/core/_methods.py index 0fc070b34c38..cbcb1df59682 100644 --- a/numpy/core/_methods.py +++ b/numpy/core/_methods.py @@ -3,6 +3,8 @@ and the Python code for the NumPy-namespace function """ +import os +import pickle import warnings from contextlib import nullcontext @@ -10,10 +12,8 @@ from numpy.core import umath as um from numpy.core.multiarray import asanyarray from numpy.core import numerictypes as nt -from numpy.core import _exceptions from numpy.core._ufunc_config import _no_nep50_warning from numpy._globals import _NoValue -from numpy.compat import pickle, os_fspath # save those O(100) nanoseconds! umr_maximum = um.maximum.reduce @@ -226,7 +226,7 @@ def _dump(self, file, protocol=2): if hasattr(file, 'write'): ctx = nullcontext(file) else: - ctx = open(os_fspath(file), "wb") + ctx = open(os.fspath(file), "wb") with ctx as f: pickle.dump(self, f, protocol=protocol) diff --git a/numpy/core/_type_aliases.py b/numpy/core/_type_aliases.py index 38f1a099e9e2..cef5b1d81562 100644 --- a/numpy/core/_type_aliases.py +++ b/numpy/core/_type_aliases.py @@ -17,7 +17,6 @@ """ -from numpy.compat import unicode from numpy.core._string_helpers import english_lower from numpy.core.multiarray import typeinfo, dtype from numpy.core._dtype import _kind_name @@ -195,10 +194,11 @@ def _set_up_aliases(): sctypes = {'int': [], - 'uint':[], - 'float':[], - 'complex':[], - 'others':[bool, object, bytes, unicode, void]} + 'uint': [], + 'float': [], + 'complex': [], + 'others': [bool, object, bytes, str, void]} + def _add_array_type(typename, bits): try: diff --git a/numpy/core/defchararray.py b/numpy/core/defchararray.py index 11c5a30bff70..fa75d69cd2a1 100644 --- a/numpy/core/defchararray.py +++ b/numpy/core/defchararray.py @@ -24,7 +24,7 @@ from .numeric import array as narray from numpy.core.multiarray import _vec_string from numpy.core import overrides -from numpy.compat import asbytes +from numpy._utils._convertions import asbytes import numpy __all__ = [ @@ -86,6 +86,7 @@ def _clean_args(*args): newargs.append(chk) return newargs + def _get_num_chars(a): """ Helper function that returns the number of characters per field in diff --git a/numpy/core/memmap.py b/numpy/core/memmap.py index 79c695455d34..b75a601bd442 100644 --- a/numpy/core/memmap.py +++ b/numpy/core/memmap.py @@ -3,7 +3,6 @@ import numpy as np from .._utils import set_module from .numeric import uint8, ndarray, dtype -from numpy.compat import os_fspath, is_pathlib_path __all__ = ['memmap'] @@ -226,7 +225,10 @@ def __new__(subtype, filename, dtype=uint8, mode='r+', offset=0, if hasattr(filename, 'read'): f_ctx = nullcontext(filename) else: - f_ctx = open(os_fspath(filename), ('r' if mode == 'c' else mode)+'b') + f_ctx = open( + os.fspath(filename), + ('r' if mode == 'c' else mode)+'b' + ) with f_ctx as fid: fid.seek(0, 2) @@ -273,7 +275,7 @@ def __new__(subtype, filename, dtype=uint8, mode='r+', offset=0, self.offset = offset self.mode = mode - if is_pathlib_path(filename): + if isinstance(filename, os.PathLike): # special case - if we were constructed with a pathlib.path, # then filename is a path object, not a string self.filename = filename.resolve() diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index aea41bc2eacc..a809c10becb2 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -116,7 +116,6 @@ # we don't export these for import *, but we do want them accessible # as numerictypes.bool, etc. from builtins import bool, int, float, complex, object, str, bytes -from numpy.compat import long, unicode # We use this later diff --git a/numpy/core/records.py b/numpy/core/records.py index 0fb49e8f70f1..b5f56b1e8241 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -33,6 +33,7 @@ array([2., 2.]) """ +import os import warnings from collections import Counter from contextlib import nullcontext @@ -40,7 +41,6 @@ from .._utils import set_module from . import numeric as sb from . import numerictypes as nt -from numpy.compat import os_fspath from .arrayprint import _get_legacy_print_mode # All of the functions allow formats to be a dtype @@ -913,7 +913,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, ctx = nullcontext(fd) else: # open file - ctx = open(os_fspath(fd), 'rb') + ctx = open(os.fspath(fd), 'rb') with ctx as fd: if offset > 0: diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 8ef012f70f73..6f913c102eb0 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -12,7 +12,6 @@ from numpy.distutils.msvccompiler import lib_opts_if_msvc from distutils.dep_util import newer from sysconfig import get_config_var -from numpy.compat import npy_load_module from setup_common import * # noqa: F403 # Set to True to enable relaxed strides checking. This (mostly) means diff --git a/numpy/core/src/multiarray/methods.h b/numpy/core/src/multiarray/methods.h index bcada0fea897..9d06794de2aa 100644 --- a/numpy/core/src/multiarray/methods.h +++ b/numpy/core/src/multiarray/methods.h @@ -15,11 +15,11 @@ NpyPath_PathlikeToFspath(PyObject *file) { static PyObject *os_PathLike = NULL; static PyObject *os_fspath = NULL; - npy_cache_import("numpy.compat", "os_PathLike", &os_PathLike); + npy_cache_import("os", "PathLike", &os_PathLike); if (os_PathLike == NULL) { return NULL; } - npy_cache_import("numpy.compat", "os_fspath", &os_fspath); + npy_cache_import("os", "fspath", &os_fspath); if (os_fspath == NULL) { return NULL; } diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index 547ebf9d6746..5e1982ec262c 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -1,14 +1,15 @@ +import datetime +import pickle + +import pytest import numpy import numpy as np -import datetime -import pytest from numpy.testing import ( IS_WASM, assert_, assert_equal, assert_raises, assert_warns, suppress_warnings, assert_raises_regex, assert_array_equal, ) -from numpy.compat import pickle # Use pytz to test out various time zones if available try: diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 9f3a285d2306..6c94408cbbb5 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -817,19 +817,8 @@ def test_deprecated_np_lib_math(self): self.assert_deprecated(lambda: np.lib.math) -class TestCompatImport(_DeprecationTestCase): - # Deprecated in Numpy 1.25.0, 2023-05-xx TODO update day - def test_deprecated_np_compat(self): - self.assert_deprecated(lambda: np.compat) - - class TestLibImports(_DeprecationTestCase): - # Deprecated in Numpy 1.25.0, 2023-05-xx TODO update day - def test_lib_functions_deprecation(self): - self.assert_deprecated(lambda: np.byte_bounds) - self.assert_deprecated(lambda: np.safe_eval) - self.assert_deprecated(lambda: np.who) - + # Deprecated in Numpy 1.26.0, 2023-09 def test_lib_functions_deprecation_call(self): from numpy.lib import byte_bounds, safe_eval, who self.assert_deprecated(lambda: byte_bounds(np.array([1]))) diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 57831f46f431..454bcfa7cc18 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -5,6 +5,7 @@ import gc import types from typing import Any +import pickle import numpy as np import numpy.dtypes @@ -13,7 +14,6 @@ from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT, IS_PYSTON, _OLD_PROMOTION) -from numpy.compat import pickle from itertools import permutations import random @@ -33,8 +33,7 @@ def assert_dtype_not_equal(a, b): "two different types hash to the same value !") class TestBuiltin: - @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object, - np.compat.unicode]) + @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object]) def test_run(self, t): """Only test hash runs at all.""" dt = np.dtype(t) @@ -1299,7 +1298,7 @@ def check_pickling(self, dtype): assert_equal(x[0], y[0]) @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object, - np.compat.unicode, bool]) + bool]) def test_builtin(self, t): self.check_pickling(np.dtype(t)) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 196c2dc13783..28b42ab5e563 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -15,9 +15,7 @@ import weakref import pytest from contextlib import contextmanager - -from numpy.compat import pickle - +import pickle import pathlib import builtins from decimal import Decimal diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py index 5924358eaba9..104eaf285221 100644 --- a/numpy/core/tests/test_overrides.py +++ b/numpy/core/tests/test_overrides.py @@ -4,6 +4,9 @@ import tempfile from io import StringIO from unittest import mock +import pickle + +import pytest import numpy as np from numpy.testing import ( @@ -11,8 +14,6 @@ from numpy.core.overrides import ( _get_implementing_args, array_function_dispatch, verify_matching_signatures) -from numpy.compat import pickle -import pytest def _return_not_implemented(self, *args, **kwargs): diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py index a76ae2d99978..b7867a67c6ca 100644 --- a/numpy/core/tests/test_records.py +++ b/numpy/core/tests/test_records.py @@ -3,6 +3,8 @@ from io import BytesIO from os import path from pathlib import Path +import pickle + import pytest import numpy as np @@ -10,7 +12,6 @@ assert_, assert_equal, assert_array_equal, assert_array_almost_equal, assert_raises, temppath, ) -from numpy.compat import pickle class TestFromrecords: diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index 841144790e31..e415ecccf9f3 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -6,6 +6,7 @@ from os import path from io import BytesIO from itertools import chain +import pickle import numpy as np from numpy.testing import ( @@ -15,7 +16,7 @@ _assert_valid_refcount, HAS_REFCOUNT, IS_PYSTON, IS_WASM ) from numpy.testing._private.utils import _no_tracing, requires_memory -from numpy.compat import asbytes, asunicode, pickle +from numpy._utils._convertions import asbytes, asunicode class TestRegression: diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 2ce6b4b69ea6..f106556f02e0 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -2,6 +2,7 @@ import itertools import sys import ctypes as ct +import pickle import pytest from pytest import param @@ -17,7 +18,6 @@ assert_allclose, HAS_REFCOUNT, suppress_warnings, IS_WASM, IS_PYPY, ) from numpy.testing._private.utils import requires_memory -from numpy.compat import pickle UNARY_UFUNCS = [obj for obj in np.core.umath.__dict__.values() diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 26fa7e49d14e..81576c3e749d 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -20,7 +20,7 @@ import numpy from pathlib import Path -from numpy.compat import asbytes, asstr +from numpy._utils._convertions import asunicode from numpy.testing import temppath, IS_WASM from importlib import import_module @@ -144,7 +144,7 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): out, err = p.communicate() if p.returncode != 0: raise RuntimeError("Running f2py failed: %s\n%s" % - (cmd[4:], asstr(out))) + (cmd[4:], asunicode(out))) finally: os.chdir(cwd) @@ -318,7 +318,7 @@ def configuration(parent_name='',top_path=None): script = os.path.join(d, get_temp_module_name() + ".py") dst_sources.append(script) with open(script, "wb") as f: - f.write(asbytes(code)) + f.write(code.encode('latin1')) # Build cwd = os.getcwd() diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py index 534d1b3eea63..06332d28c926 100644 --- a/numpy/lib/_iotools.py +++ b/numpy/lib/_iotools.py @@ -5,7 +5,7 @@ import numpy as np import numpy.core.numeric as nx -from numpy.compat import asbytes, asunicode +from numpy._utils._convertions import asbytes, asunicode def _decode_line(line, encoding=None): diff --git a/numpy/lib/format.py b/numpy/lib/format.py index d5b3fbac23ab..ed344d2f3c06 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -161,12 +161,13 @@ evolved with time and this document is more current. """ -import numpy +import io +import os +import pickle import warnings + +import numpy from numpy.lib.utils import safe_eval, drop_metadata -from numpy.compat import ( - isfileobj, os_fspath, pickle - ) __all__ = [] @@ -916,12 +917,12 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, shape=shape, ) # If we got here, then it should be safe to create the file. - with open(os_fspath(filename), mode+'b') as fp: + with open(os.fspath(filename), mode+'b') as fp: _write_array_header(fp, d, version) offset = fp.tell() else: # Read the header of the file first. - with open(os_fspath(filename), 'rb') as fp: + with open(os.fspath(filename), 'rb') as fp: version = read_magic(fp) _check_version(version) @@ -974,3 +975,15 @@ def _read_bytes(fp, size, error_template="ran out of data"): raise ValueError(msg % (error_template, size, len(data))) else: return data + + +def isfileobj(f): + if not isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)): + return False + try: + # BufferedReader/Writer may raise OSError when + # fetching `fileno()` (e.g. when wrapping BytesIO). + f.fileno() + return True + except OSError: + return False diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 339b1dc62113..57596dab62c1 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -8,6 +8,7 @@ import operator from operator import itemgetter, index as opindex, methodcaller from collections.abc import Mapping +import pickle import numpy as np from . import format @@ -21,11 +22,7 @@ ConverterLockError, ConversionWarning, _is_string_like, has_nested_fields, flatten_dtype, easy_dtype, _decode_line ) - -from numpy.compat import ( - asbytes, asstr, asunicode, os_fspath, os_PathLike, - pickle - ) +from numpy._utils._convertions import asunicode, asbytes __all__ = [ @@ -97,7 +94,7 @@ def zipfile_factory(file, *args, **kwargs): constructor. """ if not hasattr(file, 'read'): - file = os_fspath(file) + file = os.fspath(file) import zipfile kwargs['allowZip64'] = True return zipfile.ZipFile(file, *args, **kwargs) @@ -424,7 +421,7 @@ def load(file, mmap_mode=None, allow_pickle=False, fix_imports=True, fid = file own_fid = False else: - fid = stack.enter_context(open(os_fspath(file), "rb")) + fid = stack.enter_context(open(os.fspath(file), "rb")) own_fid = True # Code to distinguish from NumPy binary files and pickles. @@ -536,7 +533,7 @@ def save(file, arr, allow_pickle=True, fix_imports=True): if hasattr(file, 'write'): file_ctx = contextlib.nullcontext(file) else: - file = os_fspath(file) + file = os.fspath(file) if not file.endswith('.npy'): file = file + '.npy' file_ctx = open(file, "wb") @@ -716,7 +713,7 @@ def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None): import zipfile if not hasattr(file, 'write'): - file = os_fspath(file) + file = os.fspath(file) if not file.endswith('.npz'): file = file + '.npz' @@ -1510,11 +1507,6 @@ def savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', """ - # Py3 conversions first - if isinstance(fmt, bytes): - fmt = asstr(fmt) - delimiter = asstr(delimiter) - class WriteWrap: """Convert to bytes on bytestream inputs. @@ -1549,8 +1541,8 @@ def first_write(self, v): self.write = self.write_bytes own_fh = False - if isinstance(fname, os_PathLike): - fname = os_fspath(fname) + if isinstance(fname, os.PathLike): + fname = os.fspath(fname) if _is_string_like(fname): # datasource doesn't support creating a new file ... open(fname, 'wt').close() @@ -1587,7 +1579,7 @@ def first_write(self, v): if type(fmt) in (list, tuple): if len(fmt) != ncol: raise AttributeError('fmt has wrong shape. %s' % str(fmt)) - format = asstr(delimiter).join(map(asstr, fmt)) + format = delimiter.join(fmt) elif isinstance(fmt, str): n_fmt_chars = fmt.count('%') error = ValueError('fmt has wrong number of %% formats: %s' % fmt) @@ -1712,8 +1704,6 @@ def fromregex(file, regexp, dtype, encoding=None): content = file.read() if isinstance(content, bytes) and isinstance(regexp, str): regexp = asbytes(regexp) - elif isinstance(content, str) and isinstance(regexp, bytes): - regexp = asstr(regexp) if not hasattr(regexp, 'match'): regexp = re.compile(regexp) @@ -1974,8 +1964,8 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, byte_converters = False # Initialize the filehandle, the LineSplitter and the NameValidator - if isinstance(fname, os_PathLike): - fname = os_fspath(fname) + if isinstance(fname, os.PathLike): + fname = os.fspath(fname) if isinstance(fname, str): fid = np.lib._datasource.open(fname, 'rt', encoding=encoding) fid_ctx = contextlib.closing(fid) diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index c1032df8e1d3..d4c5aa0e8e48 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -19,7 +19,6 @@ import numpy as np import numpy.ma as ma from numpy.lib._iotools import ConverterError, ConversionWarning -from numpy.compat import asbytes from numpy.ma.testutils import assert_equal from numpy.testing import ( assert_warns, assert_, assert_raises_regex, assert_raises, @@ -28,6 +27,7 @@ break_cycles, IS_WASM ) from numpy.testing._private.utils import requires_memory +from numpy._utils._convertions import asbytes class TextIO(BytesIO): @@ -587,13 +587,12 @@ def test_unicode_stringstream(self): s.seek(0) assert_equal(s.read(), utf8 + '\n') - @pytest.mark.parametrize("fmt", ["%f", b"%f"]) @pytest.mark.parametrize("iotype", [StringIO, BytesIO]) - def test_unicode_and_bytes_fmt(self, fmt, iotype): + def test_unicode_and_bytes_fmt(self, iotype): # string type of fmt should not matter, see also gh-4053 a = np.array([1.]) s = iotype() - np.savetxt(s, a, fmt=fmt) + np.savetxt(s, a, fmt="%f") s.seek(0) if iotype is StringIO: assert_equal(s.read(), "%f\n" % 1.) @@ -1715,7 +1714,7 @@ def test_utf8_userconverters_with_explicit_dtype(self): with open(path, 'wb') as f: f.write(b'skip,skip,2001-01-01' + utf8 + b',1.0,skip') test = np.genfromtxt(path, delimiter=",", names=None, dtype=float, - usecols=(2, 3), converters={2: np.compat.unicode}, + usecols=(2, 3), converters={2: str}, encoding='UTF-8') control = np.array([('2001-01-01' + utf8.decode('UTF-8'), 1.)], dtype=[('', '|U11'), ('', float)]) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 2fe326885295..9cd3169f1ae8 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -35,11 +35,9 @@ from numpy import ndarray, amax, amin, iscomplexobj, bool_, _NoValue from numpy import array as narray from numpy.lib.function_base import angle -from numpy.compat import ( - getargspec, formatargspec, long, unicode, bytes - ) from numpy import expand_dims from numpy.core.numeric import normalize_axis_tuple +from numpy._utils._inspect import getargspec, formatargspec __all__ = [ diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 6ab1d7e4f1a6..134f9afc05c1 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -12,10 +12,10 @@ import operator import itertools import textwrap -import pytest - +import pickle from functools import reduce +import pytest import numpy as np import numpy.ma.core @@ -26,7 +26,7 @@ ) from numpy.testing._private.utils import requires_memory from numpy import ndarray -from numpy.compat import asbytes +from numpy._utils._convertions import asbytes from numpy.ma.testutils import ( assert_, assert_array_equal, assert_equal, assert_almost_equal, assert_equal_records, fail_if_equal, assert_not_equal, @@ -49,7 +49,6 @@ putmask, ravel, repeat, reshape, resize, shape, sin, sinh, sometrue, sort, sqrt, subtract, sum, take, tan, tanh, transpose, where, zeros, zeros_like, ) -from numpy.compat import pickle pi = np.pi diff --git a/numpy/ma/tests/test_mrecords.py b/numpy/ma/tests/test_mrecords.py index 77123c3cda94..761bd77cf0c3 100644 --- a/numpy/ma/tests/test_mrecords.py +++ b/numpy/ma/tests/test_mrecords.py @@ -5,6 +5,8 @@ :contact: pierregm_at_uga_dot_edu """ +import pickle + import numpy as np import numpy.ma as ma from numpy import recarray @@ -21,7 +23,6 @@ assert_, assert_equal, assert_equal_records, ) -from numpy.compat import pickle class TestMRecords: diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index 7b892ad2375f..57b5ed7f7df2 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -1,4 +1,5 @@ from functools import reduce +import pickle import pytest @@ -21,7 +22,6 @@ repeat, resize, shape, sin, sinh, sometrue, sort, sqrt, subtract, sum, take, tan, tanh, transpose, where, zeros, ) -from numpy.compat import pickle pi = np.pi diff --git a/numpy/matrixlib/tests/test_masked_matrix.py b/numpy/matrixlib/tests/test_masked_matrix.py index d0ce357aef27..1403fbca73e4 100644 --- a/numpy/matrixlib/tests/test_masked_matrix.py +++ b/numpy/matrixlib/tests/test_masked_matrix.py @@ -1,3 +1,5 @@ +import pickle + import numpy as np from numpy.testing import assert_warns from numpy.ma.testutils import (assert_, assert_equal, assert_raises, @@ -6,7 +8,6 @@ MaskType, getmask, MaskedArray, nomask, log, add, hypot, divide) from numpy.ma.extras import mr_ -from numpy.compat import pickle class MMatrix(MaskedArray, np.matrix,): diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py index 5c4c2cbf92fe..1c57b3fa5d8d 100644 --- a/numpy/random/tests/test_generator_mt19937.py +++ b/numpy/random/tests/test_generator_mt19937.py @@ -644,7 +644,7 @@ def test_respect_dtype_singleton(self, endpoint): sample = self.rfunc(lbnd, ubnd, endpoint=endpoint, dtype=dt) assert_equal(sample.dtype, dt) - for dt in (bool, int, np.compat.long): + for dt in (bool, int): lbnd = 0 if dt is bool else np.iinfo(dt).min ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 ubnd = ubnd - 1 if endpoint else ubnd diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py index 0f4e7925a501..e64ace711953 100644 --- a/numpy/random/tests/test_random.py +++ b/numpy/random/tests/test_random.py @@ -276,7 +276,7 @@ def test_respect_dtype_singleton(self): sample = self.rfunc(lbnd, ubnd, dtype=dt) assert_equal(sample.dtype, np.dtype(dt)) - for dt in (bool, int, np.compat.long): + for dt in (bool, int): lbnd = 0 if dt is bool else np.iinfo(dt).min ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 diff --git a/numpy/random/tests/test_randomstate.py b/numpy/random/tests/test_randomstate.py index 3a296109890e..3099853d2a8e 100644 --- a/numpy/random/tests/test_randomstate.py +++ b/numpy/random/tests/test_randomstate.py @@ -422,7 +422,7 @@ def test_respect_dtype_singleton(self): sample = self.rfunc(lbnd, ubnd, dtype=dt) assert_equal(sample.dtype, np.dtype(dt)) - for dt in (bool, int, np.compat.long): + for dt in (bool, int): lbnd = 0 if dt is bool else np.iinfo(dt).min ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index eaa89aa6f749..f4898acca33e 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -327,6 +327,8 @@ def is_unexpected(name): ] +# suppressing warnings from deprecated modules +@pytest.mark.filterwarnings("ignore:.*np.compat.*:DeprecationWarning") def test_all_modules_are_expected(): """ Test that we don't add anything that looks like a new public module by diff --git a/numpy/tests/test_reloading.py b/numpy/tests/test_reloading.py index a1f360089a54..c2b6e4da31f0 100644 --- a/numpy/tests/test_reloading.py +++ b/numpy/tests/test_reloading.py @@ -1,3 +1,11 @@ +import sys +import subprocess +import textwrap +from importlib import reload +import pickle + +import pytest + from numpy.testing import ( assert_raises, assert_warns, @@ -5,13 +13,6 @@ assert_equal, IS_WASM, ) -from numpy.compat import pickle - -import pytest -import sys -import subprocess -import textwrap -from importlib import reload def test_numpy_reloading(): diff --git a/numpy/typing/tests/data/fail/lib_utils.pyi b/numpy/typing/tests/data/fail/lib_utils.pyi index e16c926aa645..4a98b2b01ed5 100644 --- a/numpy/typing/tests/data/fail/lib_utils.pyi +++ b/numpy/typing/tests/data/fail/lib_utils.pyi @@ -4,10 +4,4 @@ np.deprecate(1) # E: No overload variant np.deprecate_with_doc(1) # E: incompatible type -np.byte_bounds(1) # E: incompatible type - -np.who(1) # E: incompatible type - np.lookfor(None) # E: incompatible type - -np.safe_eval(None) # E: incompatible type diff --git a/numpy/typing/tests/data/pass/lib_utils.py b/numpy/typing/tests/data/pass/lib_utils.py index 65640c28873d..b8ffa54d74c2 100644 --- a/numpy/typing/tests/data/pass/lib_utils.py +++ b/numpy/typing/tests/data/pass/lib_utils.py @@ -15,9 +15,6 @@ def func(a: int) -> bool: ... np.deprecate_with_doc("test") np.deprecate_with_doc(None) -np.byte_bounds(AR) -np.byte_bounds(np.float64()) - np.info(1, output=FILE) np.source(np.interp, output=FILE) diff --git a/numpy/typing/tests/data/reveal/lib_utils.pyi b/numpy/typing/tests/data/reveal/lib_utils.pyi index 9b1bf4123da7..510160395b7e 100644 --- a/numpy/typing/tests/data/reveal/lib_utils.pyi +++ b/numpy/typing/tests/data/reveal/lib_utils.pyi @@ -15,16 +15,8 @@ reveal_type(np.deprecate()) # E: _Deprecate reveal_type(np.deprecate_with_doc("test")) # E: _Deprecate reveal_type(np.deprecate_with_doc(None)) # E: _Deprecate -reveal_type(np.byte_bounds(AR)) # E: Tuple[builtins.int, builtins.int] -reveal_type(np.byte_bounds(np.float64())) # E: Tuple[builtins.int, builtins.int] - -reveal_type(np.who(None)) # E: None -reveal_type(np.who(AR_DICT)) # E: None - reveal_type(np.info(1, output=FILE)) # E: None reveal_type(np.source(np.interp, output=FILE)) # E: None reveal_type(np.lookfor("binary representation", output=FILE)) # E: None - -reveal_type(np.safe_eval("1 + 1")) # E: Any diff --git a/pytest.ini b/pytest.ini index f4317c790a41..7cc92c4e1a77 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,7 +13,6 @@ filterwarnings = ignore::UserWarning:cpuinfo, ignore: divide by zero encountered in log ignore: invalid value encountered in log - ignore:`np.compat`, which was used during the Python 2 to 3 transition, is deprecated # Matrix PendingDeprecationWarning. ignore:the matrix subclass is not ignore:Importing from numpy.matlib is @@ -28,3 +27,7 @@ filterwarnings = ignore:\n\n `numpy.distutils`:DeprecationWarning # Ignore mypy >= 0.971 DeprecationWarnings ignore:path is deprecated\. Use files\(\) instead:DeprecationWarning:mypy +# Ignore selected DeprecationWarnings from numpy.lib + ignore:.*`safe_eval` is deprecated!:DeprecationWarning + ignore:.*`byte_bounds` is deprecated!:DeprecationWarning + ignore:.*`who` is deprecated!:DeprecationWarning From 22d9ffb1d9d68ea5eafa9a348f547b4d3a9af72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= Date: Wed, 14 Jun 2023 09:03:33 +0200 Subject: [PATCH 3/3] API: remove selected numpy.lib warning suppressions --- numpy/core/_methods.py | 1 + numpy/lib/format.py | 7 ++++--- numpy/lib/tests/test_regression.py | 2 ++ numpy/lib/tests/test_utils.py | 2 ++ pytest.ini | 4 ---- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/numpy/core/_methods.py b/numpy/core/_methods.py index cbcb1df59682..12bbe8069961 100644 --- a/numpy/core/_methods.py +++ b/numpy/core/_methods.py @@ -12,6 +12,7 @@ from numpy.core import umath as um from numpy.core.multiarray import asanyarray from numpy.core import numerictypes as nt +from numpy.core import _exceptions from numpy.core._ufunc_config import _no_nep50_warning from numpy._globals import _NoValue diff --git a/numpy/lib/format.py b/numpy/lib/format.py index ed344d2f3c06..3db61f317230 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -167,7 +167,7 @@ import warnings import numpy -from numpy.lib.utils import safe_eval, drop_metadata +from numpy.lib.utils import drop_metadata __all__ = [] @@ -591,6 +591,7 @@ def _read_array_header(fp, version, max_header_size=_MAX_HEADER_SIZE): """ # Read an unsigned, little-endian short int which has the length of the # header. + import ast import struct hinfo = _header_size_info.get(version) if hinfo is None: @@ -621,12 +622,12 @@ def _read_array_header(fp, version, max_header_size=_MAX_HEADER_SIZE): # # For performance reasons, we try without _filter_header first though try: - d = safe_eval(header) + d = ast.literal_eval(header) except SyntaxError as e: if version <= (2, 0): header = _filter_header(header) try: - d = safe_eval(header) + d = ast.literal_eval(header) except SyntaxError as e2: msg = "Cannot parse header: {!r}" raise ValueError(msg.format(header)) from e2 diff --git a/numpy/lib/tests/test_regression.py b/numpy/lib/tests/test_regression.py index 55df2a6752c2..ed74273222a2 100644 --- a/numpy/lib/tests/test_regression.py +++ b/numpy/lib/tests/test_regression.py @@ -5,6 +5,7 @@ assert_, assert_equal, assert_array_equal, assert_array_almost_equal, assert_raises, _assert_valid_refcount, ) +import pytest class TestRegression: @@ -157,6 +158,7 @@ def test_void_coercion(self): x = np.zeros((1,), dt) assert_(np.r_[x, x].dtype == dt) + @pytest.mark.filterwarnings("ignore:.*who.*:DeprecationWarning") def test_who_with_0dim_array(self): # ticket #1243 import os diff --git a/numpy/lib/tests/test_utils.py b/numpy/lib/tests/test_utils.py index 45416b059773..1d45489a376b 100644 --- a/numpy/lib/tests/test_utils.py +++ b/numpy/lib/tests/test_utils.py @@ -120,11 +120,13 @@ def test_deprecate_module(): assert_(old_func.__module__ == __name__) +@pytest.mark.filterwarnings("ignore:.*safe_eval.*:DeprecationWarning") def test_safe_eval_nameconstant(): # Test if safe_eval supports Python 3.4 _ast.NameConstant utils.safe_eval('None') +@pytest.mark.filterwarnings("ignore:.*byte_bounds.*:DeprecationWarning") class TestByteBounds: def test_byte_bounds(self): diff --git a/pytest.ini b/pytest.ini index 7cc92c4e1a77..29ec1d1a4fce 100644 --- a/pytest.ini +++ b/pytest.ini @@ -27,7 +27,3 @@ filterwarnings = ignore:\n\n `numpy.distutils`:DeprecationWarning # Ignore mypy >= 0.971 DeprecationWarnings ignore:path is deprecated\. Use files\(\) instead:DeprecationWarning:mypy -# Ignore selected DeprecationWarnings from numpy.lib - ignore:.*`safe_eval` is deprecated!:DeprecationWarning - ignore:.*`byte_bounds` is deprecated!:DeprecationWarning - ignore:.*`who` is deprecated!:DeprecationWarning