From ced19c462a8e17d8c36016d46e9a396dc812974a Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 09:59:41 +0200 Subject: [PATCH 1/6] DeprecationWarning when importing typing.{io,re} --- Lib/typing.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 8fadb571f41dc2..f680617d9c9a0e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2512,7 +2512,7 @@ def __enter__(self) -> 'TextIO': pass -class io: +class _io: """Wrapper namespace for IO generic classes.""" __all__ = ['IO', 'TextIO', 'BinaryIO'] @@ -2521,13 +2521,13 @@ class io: BinaryIO = BinaryIO -io.__name__ = __name__ + '.io' -sys.modules[io.__name__] = io +_io.__name__ = __name__ + '.io' +sys.modules[_io.__name__] = _io Pattern = _alias(stdlib_re.Pattern, 1) Match = _alias(stdlib_re.Match, 1) -class re: +class _re: """Wrapper namespace for re type aliases.""" __all__ = ['Pattern', 'Match'] @@ -2535,5 +2535,21 @@ class re: Match = Match -re.__name__ = __name__ + '.re' -sys.modules[re.__name__] = re +_re.__name__ = __name__ + '.re' +sys.modules[_re.__name__] = _re + +def __getattr__(name): + import warnings + if name in ["io", "re"]: + warnings.warn( + f"typing.{name} module is deprecated, import directly " + f"from typing instead. typing.{name} will be removed " + "in Python 3.12.", + DeprecationWarning, + stacklevel=2 + ) + if name == "io": + return _io + elif name == "re": + return _re + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") From 18cbc2a47a47bd7fea6b5aac8474e490312c8d1e Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 14:19:15 +0200 Subject: [PATCH 2/6] Add NEWS entry --- .../NEWS.d/next/Library/2021-06-14-14-19-11.bpo-38291.ee4cSX.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2021-06-14-14-19-11.bpo-38291.ee4cSX.rst diff --git a/Misc/NEWS.d/next/Library/2021-06-14-14-19-11.bpo-38291.ee4cSX.rst b/Misc/NEWS.d/next/Library/2021-06-14-14-19-11.bpo-38291.ee4cSX.rst new file mode 100644 index 00000000000000..7fb891dd4bc0c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-06-14-14-19-11.bpo-38291.ee4cSX.rst @@ -0,0 +1 @@ +Importing typing.io or typing.re now prints a `DeprecationWarning`. From efceb28d760a054121e197334c44d9013a4db3d9 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 14:55:34 +0200 Subject: [PATCH 3/6] Fix warning handling --- Lib/typing.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index f680617d9c9a0e..00a0df591cbfcc 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -28,6 +28,7 @@ import re as stdlib_re # Avoid confusion with the re we export. import sys import types +import warnings from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias # Please keep __all__ alphabetized within each category. @@ -2512,7 +2513,20 @@ def __enter__(self) -> 'TextIO': pass -class _io: +class _DeprecatedType(type): + def __getattribute__(cls, name): + if name != "__dict__" and name in cls.__dict__: + warnings.warn( + f"{cls.__name__} is deprecated, import directly " + f"from typing instead. {cls.__name__} will be removed " + "in Python 3.12.", + DeprecationWarning, + stacklevel=2, + ) + return super().__getattribute__(name) + + +class io(metaclass=_DeprecatedType): """Wrapper namespace for IO generic classes.""" __all__ = ['IO', 'TextIO', 'BinaryIO'] @@ -2521,13 +2535,13 @@ class _io: BinaryIO = BinaryIO -_io.__name__ = __name__ + '.io' -sys.modules[_io.__name__] = _io +io.__name__ = __name__ + '.io' +sys.modules[io.__name__] = io Pattern = _alias(stdlib_re.Pattern, 1) Match = _alias(stdlib_re.Match, 1) -class _re: +class re(metaclass=_DeprecatedType): """Wrapper namespace for re type aliases.""" __all__ = ['Pattern', 'Match'] @@ -2535,21 +2549,5 @@ class _re: Match = Match -_re.__name__ = __name__ + '.re' -sys.modules[_re.__name__] = _re - -def __getattr__(name): - import warnings - if name in ["io", "re"]: - warnings.warn( - f"typing.{name} module is deprecated, import directly " - f"from typing instead. typing.{name} will be removed " - "in Python 3.12.", - DeprecationWarning, - stacklevel=2 - ) - if name == "io": - return _io - elif name == "re": - return _re - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") +re.__name__ = __name__ + '.re' +sys.modules[re.__name__] = re From a84d6b1721abbc59397439476f5c30fa72bd9e24 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 15:16:30 +0200 Subject: [PATCH 4/6] Fix deprecated import --- Lib/importlib/resources.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/importlib/resources.py b/Lib/importlib/resources.py index 8a98663ff8e6d5..bb5c354d9f00ab 100644 --- a/Lib/importlib/resources.py +++ b/Lib/importlib/resources.py @@ -11,8 +11,7 @@ from pathlib import Path from types import ModuleType from typing import ContextManager, Iterable, Union -from typing import cast -from typing.io import BinaryIO, TextIO +from typing import cast, BinaryIO, TextIO from collections.abc import Sequence from functools import singledispatch From d5a555d1f04cc010e183964cebca5501e64d0578 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 15:18:58 +0200 Subject: [PATCH 5/6] Test the warnings --- Lib/test/test_typing.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 79c5c3a9104075..b7652585c24dc0 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3,6 +3,7 @@ import pickle import re import sys +import warnings from unittest import TestCase, main, skipUnless, skip from copy import copy, deepcopy @@ -1976,7 +1977,7 @@ def test_weakref_all(self): T = TypeVar('T') things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any], Optional[List[int]], typing.Mapping[int, str], - typing.re.Match[bytes], typing.Iterable['whatever']] + typing.Match[bytes], typing.Iterable['whatever']] for t in things: self.assertEqual(weakref.ref(t)(), t) @@ -3996,12 +3997,14 @@ def stuff(a: BinaryIO) -> bytes: self.assertEqual(a.__parameters__, ()) def test_io_submodule(self): - from typing.io import IO, TextIO, BinaryIO, __all__, __name__ - self.assertIs(IO, typing.IO) - self.assertIs(TextIO, typing.TextIO) - self.assertIs(BinaryIO, typing.BinaryIO) - self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO'])) - self.assertEqual(__name__, 'typing.io') + with warnings.catch_warnings() as w: + from typing.io import IO, TextIO, BinaryIO, __all__, __name__ + self.assertIs(IO, typing.IO) + self.assertIs(TextIO, typing.TextIO) + self.assertIs(BinaryIO, typing.BinaryIO) + self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO'])) + self.assertEqual(__name__, 'typing.io') + assert len(w) == 1 class RETests(BaseTestCase): @@ -4048,11 +4051,13 @@ def test_repr(self): self.assertEqual(repr(Match[bytes]), 'typing.Match[bytes]') def test_re_submodule(self): - from typing.re import Match, Pattern, __all__, __name__ - self.assertIs(Match, typing.Match) - self.assertIs(Pattern, typing.Pattern) - self.assertEqual(set(__all__), set(['Match', 'Pattern'])) - self.assertEqual(__name__, 'typing.re') + with warnings.catch_warnings() as w: + from typing.re import Match, Pattern, __all__, __name__ + self.assertIs(Match, typing.Match) + self.assertIs(Pattern, typing.Pattern) + self.assertEqual(set(__all__), set(['Match', 'Pattern'])) + self.assertEqual(__name__, 'typing.re') + assert len(w) == 1 def test_cannot_subclass(self): with self.assertRaises(TypeError) as ex: From 00656193c10ac55b7130ac4130a6d840a75f290c Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 14 Jun 2021 15:52:42 +0200 Subject: [PATCH 6/6] Fix tests --- Lib/test/test_typing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b7652585c24dc0..06df3e23264cb2 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3997,14 +3997,14 @@ def stuff(a: BinaryIO) -> bytes: self.assertEqual(a.__parameters__, ()) def test_io_submodule(self): - with warnings.catch_warnings() as w: + with warnings.catch_warnings(record=True) as w: from typing.io import IO, TextIO, BinaryIO, __all__, __name__ self.assertIs(IO, typing.IO) self.assertIs(TextIO, typing.TextIO) self.assertIs(BinaryIO, typing.BinaryIO) self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO'])) self.assertEqual(__name__, 'typing.io') - assert len(w) == 1 + self.assertEqual(len(w), 1) class RETests(BaseTestCase): @@ -4051,13 +4051,13 @@ def test_repr(self): self.assertEqual(repr(Match[bytes]), 'typing.Match[bytes]') def test_re_submodule(self): - with warnings.catch_warnings() as w: + with warnings.catch_warnings(record=True) as w: from typing.re import Match, Pattern, __all__, __name__ self.assertIs(Match, typing.Match) self.assertIs(Pattern, typing.Pattern) self.assertEqual(set(__all__), set(['Match', 'Pattern'])) self.assertEqual(__name__, 'typing.re') - assert len(w) == 1 + self.assertEqual(len(w), 1) def test_cannot_subclass(self): with self.assertRaises(TypeError) as ex: