summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2020-06-10 09:16:22 +0100
committerEric Wieser <wieser.eric@gmail.com>2020-06-12 15:04:16 +0100
commit70f4526b16b1f5815c7a2d46a2b10f1961166463 (patch)
treed701b6cf02453b9e71c440209b8a8432faeb556b
parent4be513d47212a28c000107d7c532c0a5a4312953 (diff)
downloadnumpy-70f4526b16b1f5815c7a2d46a2b10f1961166463.tar.gz
DEP: Deprecate aliases of builtin types in python 3.7+
This: * Makes accessing these attributes emit a deprecation warning * Removes them from `dir(numpy)`, so as not to emit warnings for user of `inspect.getmembers` These aliases are a continual source of confusion for beginners, and are still often used by accident by experts.
-rw-r--r--doc/release/upcoming_changes/14882.deprecation.rst30
-rw-r--r--numpy/__init__.py78
-rw-r--r--numpy/core/tests/test_deprecations.py20
-rw-r--r--numpy/tests/test_public_api.py28
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