diff options
-rw-r--r-- | doc/release/upcoming_changes/14882.deprecation.rst | 30 | ||||
-rw-r--r-- | numpy/__init__.py | 78 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 20 | ||||
-rw-r--r-- | numpy/tests/test_public_api.py | 28 |
4 files changed, 127 insertions, 29 deletions
diff --git a/doc/release/upcoming_changes/14882.deprecation.rst b/doc/release/upcoming_changes/14882.deprecation.rst new file mode 100644 index 000000000..db3b39d4c --- /dev/null +++ b/doc/release/upcoming_changes/14882.deprecation.rst @@ -0,0 +1,30 @@ +Using the aliases of builtin types like ``np.int`` is deprecated +---------------------------------------------------------------- + +For a long time, ``np.int`` has been an alias of the builtin ``int``. This is +repeatedly a cause of confusion for newcomers, and is also simply not useful. + +These aliases have been deprecated. The table below shows the full list of +deprecated aliases, along with their exact meaning. Replacing uses of items in +the first column with the contents of the second column will work identically +and silence the deprecation warning. + +In many cases, it may have been intended to use the types from the third column. +Be aware that use of these types may result in subtle but desirable behavior +changes. + +================== ================================= ================================================================== +Deprecated name Identical to Possibly intended numpy type +================== ================================= ================================================================== +``numpy.bool`` ``bool`` `numpy.bool_` +``numpy.int`` ``int`` `numpy.int_` (default int dtype), `numpy.cint` (C ``int``) +``numpy.float`` ``float`` `numpy.float_`, `numpy.double` (equivalent) +``numpy.complex`` ``complex`` `numpy.complex_`, `numpy.cdouble` (equivalent) +``numpy.object`` ``object`` `numpy.object_` +``numpy.str`` ``str`` `numpy.str_` +``numpy.long`` ``int`` (``long`` on Python 2) `numpy.int_` (C ``long``), `numpy.longlong` (largest integer type) +``numpy.unicode`` ``str`` (``unicode`` on Python 2) `numpy.unicode_` +================== ================================= ================================================================== + +Note that for technical reasons these deprecation warnings will only be emitted +on Python 3.7 and above. diff --git a/numpy/__init__.py b/numpy/__init__.py index e6a24f0d1..bfd0185e1 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -136,6 +136,9 @@ else: __all__ = ['ModuleDeprecationWarning', 'VisibleDeprecationWarning'] + # mapping of {name: (value, deprecation_msg)} + __deprecated_attrs__ = {} + # Allow distributors to run custom init code from . import _distributor_init @@ -156,11 +159,35 @@ else: from . import matrixlib as _mat from .matrixlib import * - # Make these accessible from numpy name-space - # but not imported in from numpy import * - # TODO[gh-6103]: Deprecate these - from builtins import bool, int, float, complex, object, str - from .compat import long, unicode + # Deprecations introduced in NumPy 1.20.0, 2020-06-06 + import builtins as _builtins + __deprecated_attrs__.update({ + n: ( + getattr(_builtins, n), + "`np.{n}` is a deprecated alias for the builtin `{n}`. " + "Use `{n}` by itself, which is identical in behavior, to silence " + "this warning. " + "If you specifically wanted the numpy scalar type, use `np.{n}_` " + "here." + .format(n=n) + ) + for n in ["bool", "int", "float", "complex", "object", "str"] + }) + __deprecated_attrs__.update({ + n: ( + getattr(compat, n), + "`np.{n}` is a deprecated alias for `np.compat.{n}`. " + "Use `np.compat.{n}` by itself, which is identical in behavior, " + "to silence this warning. " + "In the likely event your code does not need to work on Python 2 " + "you can use the builtin ``{n2}`` for which ``np.compat.{n}`` is " + "itself an alias. " + "If you specifically wanted the numpy scalar type, use `np.{n2}_` " + "here." + .format(n=n, n2=n2) + ) + for n, n2 in [("long", "int"), ("unicode", "str")] + }) from .core import round, abs, max, min # now that numpy modules are imported, can initialize limits @@ -172,8 +199,10 @@ else: __all__.extend(lib.__all__) __all__.extend(['linalg', 'fft', 'random', 'ctypeslib', 'ma']) - # These are added by `from .core import *` and `core.__all__`, but we - # overwrite them above with builtins we do _not_ want to export. + # These are exported by np.core, but are replaced by the builtins below + # remove them to ensure that we don't end up with `np.long == np.int_`, + # which would be a breaking change. + del long, unicode __all__.remove('long') __all__.remove('unicode') @@ -196,25 +225,33 @@ else: numarray = 'removed' if sys.version_info[:2] >= (3, 7): - # Importing Tester requires importing all of UnitTest which is not a - # cheap import Since it is mainly used in test suits, we lazy import it - # here to save on the order of 10 ms of import time for most users - # - # The previous way Tester was imported also had a side effect of adding - # the full `numpy.testing` namespace - # # module level getattr is only supported in 3.7 onwards # https://www.python.org/dev/peps/pep-0562/ def __getattr__(attr): + # Emit warnings for deprecated attributes + try: + val, msg = __deprecated_attrs__[attr] + except KeyError: + pass + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return val + + # Importing Tester requires importing all of UnitTest which is not a + # cheap import Since it is mainly used in test suits, we lazy import it + # here to save on the order of 10 ms of import time for most users + # + # The previous way Tester was imported also had a side effect of adding + # the full `numpy.testing` namespace if attr == 'testing': import numpy.testing as testing return testing elif attr == 'Tester': from .testing import Tester return Tester - else: - raise AttributeError("module {!r} has no attribute " - "{!r}".format(__name__, attr)) + + raise AttributeError("module {!r} has no attribute " + "{!r}".format(__name__, attr)) def __dir__(): return list(globals().keys() | {'Tester', 'testing'}) @@ -224,6 +261,13 @@ else: # no-one else in the world is using it (though I hope not) from .testing import Tester + # We weren't able to emit a warning about these, so keep them around + globals().update({ + k: v + for k, (v, msg) in __deprecated_attrs__.items() + }) + + # Pytest testing from numpy._pytesttester import PytestTester test = PytestTester(__name__) diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 01924410f..887aef65f 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -9,6 +9,7 @@ import warnings import pytest import tempfile import re +import sys import numpy as np from numpy.testing import ( @@ -655,3 +656,22 @@ class TestNonExactMatchDeprecation(_DeprecationTestCase): self.assert_deprecated(lambda: np.ravel_multi_index(arr, (7, 6), mode='Cilp')) # using completely different word with first character as R self.assert_deprecated(lambda: np.searchsorted(arr[0], 4, side='Random')) + + +class TestDeprecatedGlobals(_DeprecationTestCase): + # 2020-06-06 + @pytest.mark.skipif( + sys.version_info < (3, 7), + reason='module-level __getattr__ not supported') + def test_type_aliases(self): + # from builtins + self.assert_deprecated(lambda: np.bool) + self.assert_deprecated(lambda: np.int) + self.assert_deprecated(lambda: np.float) + self.assert_deprecated(lambda: np.complex) + self.assert_deprecated(lambda: np.object) + self.assert_deprecated(lambda: np.str) + + # from np.compat + self.assert_deprecated(lambda: np.long) + self.assert_deprecated(lambda: np.unicode) diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index 7ce74bc43..9fa61951a 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -54,18 +54,22 @@ def test_numpy_namespace(): 'show_config': 'numpy.__config__.show', 'who': 'numpy.lib.utils.who', } - # These built-in types are re-exported by numpy. - builtins = { - 'bool': 'builtins.bool', - 'complex': 'builtins.complex', - 'float': 'builtins.float', - 'int': 'builtins.int', - 'long': 'builtins.int', - 'object': 'builtins.object', - 'str': 'builtins.str', - 'unicode': 'builtins.str', - } - whitelist = dict(undocumented, **builtins) + if sys.version_info < (3, 7): + # These built-in types are re-exported by numpy. + builtins = { + 'bool': 'builtins.bool', + 'complex': 'builtins.complex', + 'float': 'builtins.float', + 'int': 'builtins.int', + 'long': 'builtins.int', + 'object': 'builtins.object', + 'str': 'builtins.str', + 'unicode': 'builtins.str', + } + whitelist = dict(undocumented, **builtins) + else: + # after 3.7, we override dir to not show these members + whitelist = undocumented bad_results = check_dir(np) # pytest gives better error messages with the builtin assert than with # assert_equal |