[Python-checkins] gh-91896: Improve visibility of `ByteString` deprecation warnings (#104294)

hauntsaninja webhook-mailer at python.org
Fri May 12 02:01:38 EDT 2023


https://github.com/python/cpython/commit/f0f5bb32043e1223d8c413e763cd93061d4f9fac
commit: f0f5bb32043e1223d8c413e763cd93061d4f9fac
branch: main
author: Alex Waygood <Alex.Waygood at Gmail.com>
committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com>
date: 2023-05-11T23:01:31-07:00
summary:

gh-91896: Improve visibility of `ByteString` deprecation warnings (#104294)

files:
M Lib/collections/abc.py
M Lib/test/libregrtest/refleak.py
M Lib/test/test_collections.py
M Lib/test/test_typing.py
M Lib/typing.py

diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py
index 86ca8b8a8414..60b1eb60fa6a 100644
--- a/Lib/collections/abc.py
+++ b/Lib/collections/abc.py
@@ -1,3 +1,12 @@
 from _collections_abc import *
 from _collections_abc import __all__
 from _collections_abc import _CallableGenericAlias
+
+_deprecated_ByteString = globals().pop("ByteString")
+
+def __getattr__(attr):
+    if attr == "ByteString":
+        import warnings
+        warnings._deprecated("collections.abc.ByteString", remove=(3, 14))
+        return _deprecated_ByteString
+    raise AttributeError(f"module 'collections.abc' has no attribute {attr!r}")
diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py
index 2de8c6cfbc61..776a9e9b587d 100644
--- a/Lib/test/libregrtest/refleak.py
+++ b/Lib/test/libregrtest/refleak.py
@@ -48,11 +48,13 @@ def dash_R(ns, test_name, test_func):
     else:
         zdc = zipimport._zip_directory_cache.copy()
     abcs = {}
-    for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]:
-        if not isabstract(abc):
-            continue
-        for obj in abc.__subclasses__() + [abc]:
-            abcs[obj] = _get_dump(obj)[0]
+    # catch and ignore collections.abc.ByteString deprecation
+    with warnings.catch_warnings(action='ignore', category=DeprecationWarning):
+        for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]:
+            if not isabstract(abc):
+                continue
+            for obj in abc.__subclasses__() + [abc]:
+                abcs[obj] = _get_dump(obj)[0]
 
     # bpo-31217: Integer pool to get a single integer object for the same
     # value. The pool is used to prevent false alarm when checking for memory
@@ -173,7 +175,9 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
         zipimport._zip_directory_cache.update(zdc)
 
     # Clear ABC registries, restoring previously saved ABC registries.
-    abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__]
+    # ignore deprecation warning for collections.abc.ByteString
+    with warnings.catch_warnings(action='ignore', category=DeprecationWarning):
+        abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__]
     abs_classes = filter(isabstract, abs_classes)
     for abc in abs_classes:
         for obj in abc.__subclasses__() + [abc]:
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index bb8b352518ef..f0736b8081fe 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -11,6 +11,7 @@
 import string
 import sys
 from test import support
+from test.support.import_helper import import_fresh_module
 import types
 import unittest
 
@@ -25,7 +26,7 @@
 from collections.abc import Set, MutableSet
 from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
 from collections.abc import Sequence, MutableSequence
-from collections.abc import ByteString, Buffer
+from collections.abc import Buffer
 
 
 class TestUserObjects(unittest.TestCase):
@@ -1939,6 +1940,8 @@ def assert_index_same(seq1, seq2, index_args):
                             nativeseq, seqseq, (letter, start, stop))
 
     def test_ByteString(self):
+        with self.assertWarns(DeprecationWarning):
+            from collections.abc import ByteString
         for sample in [bytes, bytearray]:
             with self.assertWarns(DeprecationWarning):
                 self.assertIsInstance(sample(), ByteString)
@@ -1960,6 +1963,11 @@ class X(ByteString): pass
             # No metaclass conflict
             class Z(ByteString, Awaitable): pass
 
+    def test_ByteString_attribute_access(self):
+        collections_abc = import_fresh_module("collections.abc")
+        with self.assertWarns(DeprecationWarning):
+            collections_abc.ByteString
+
     def test_Buffer(self):
         for sample in [bytes, bytearray, memoryview]:
             self.assertIsInstance(sample(b"x"), Buffer)
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index f162e587810a..3422dc1ed3f5 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -8,6 +8,7 @@
 import re
 import sys
 import warnings
+from test.support.import_helper import import_fresh_module
 from unittest import TestCase, main, skipUnless, skip
 from unittest.mock import patch
 from copy import copy, deepcopy
@@ -3908,7 +3909,14 @@ class MyChain(typing.ChainMap[str, T]): ...
         self.assertEqual(MyChain[int]().__orig_class__, MyChain[int])
 
     def test_all_repr_eq_any(self):
-        objs = (getattr(typing, el) for el in typing.__all__)
+        typing = import_fresh_module("typing")
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.filterwarnings('always', '', DeprecationWarning)
+            objs = [getattr(typing, el) for el in typing.__all__]
+        self.assertEqual(
+            [str(w.message) for w in wlog],
+            ["'typing.ByteString' is deprecated and slated for removal in Python 3.14"]
+        )
         for obj in objs:
             self.assertNotEqual(repr(obj), '')
             self.assertEqual(obj, obj)
@@ -5996,8 +6004,16 @@ def test_mutablesequence(self):
         self.assertNotIsInstance((), typing.MutableSequence)
 
     def test_bytestring(self):
-        self.assertIsInstance(b'', typing.ByteString)
-        self.assertIsInstance(bytearray(b''), typing.ByteString)
+        with self.assertWarns(DeprecationWarning):
+            from typing import ByteString
+        with self.assertWarns(DeprecationWarning):
+            self.assertIsInstance(b'', ByteString)
+        with self.assertWarns(DeprecationWarning):
+            self.assertIsInstance(bytearray(b''), ByteString)
+        with self.assertWarns(DeprecationWarning):
+            class Foo(ByteString): ...
+        with self.assertWarns(DeprecationWarning):
+            class Bar(ByteString, typing.Awaitable): ...
 
     def test_list(self):
         self.assertIsSubclass(list, typing.List)
@@ -8293,6 +8309,10 @@ def test_no_isinstance(self):
 class SpecialAttrsTests(BaseTestCase):
 
     def test_special_attrs(self):
+        with warnings.catch_warnings(
+            action='ignore', category=DeprecationWarning
+        ):
+            typing_ByteString = typing.ByteString
         cls_to_check = {
             # ABC classes
             typing.AbstractSet: 'AbstractSet',
@@ -8301,7 +8321,7 @@ def test_special_attrs(self):
             typing.AsyncIterable: 'AsyncIterable',
             typing.AsyncIterator: 'AsyncIterator',
             typing.Awaitable: 'Awaitable',
-            typing.ByteString: 'ByteString',
+            typing_ByteString: 'ByteString',
             typing.Callable: 'Callable',
             typing.ChainMap: 'ChainMap',
             typing.Collection: 'Collection',
@@ -8626,6 +8646,8 @@ def test_all_exported_names(self):
                 getattr(v, '__module__', None) == typing.__name__
             )
         }
+        # Deprecated; added dynamically via module __getattr__
+        computed_all.add("ByteString")
         self.assertSetEqual(computed_all, actual_all)
 
 
diff --git a/Lib/typing.py b/Lib/typing.py
index 62c7dd31a629..bf7bd241972a 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1599,6 +1599,22 @@ def __or__(self, right):
     def __ror__(self, left):
         return Union[left, self]
 
+
+class _DeprecatedGenericAlias(_SpecialGenericAlias, _root=True):
+    def __init__(
+        self, origin, nparams, *, removal_version, inst=True, name=None
+    ):
+        super().__init__(origin, nparams, inst=inst, name=name)
+        self._removal_version = removal_version
+
+    def __instancecheck__(self, inst):
+        import warnings
+        warnings._deprecated(
+            f"{self.__module__}.{self._name}", remove=self._removal_version
+        )
+        return super().__instancecheck__(inst)
+
+
 class _CallableGenericAlias(_NotIterable, _GenericAlias, _root=True):
     def __repr__(self):
         assert self._name == 'Callable'
@@ -2756,7 +2772,6 @@ class Other(Leaf):  # Error reported by type checker
 MutableMapping = _alias(collections.abc.MutableMapping, 2)
 Sequence = _alias(collections.abc.Sequence, 1)
 MutableSequence = _alias(collections.abc.MutableSequence, 1)
-ByteString = _alias(collections.abc.ByteString, 0)  # Not generic
 # Tuple accepts variable number of parameters.
 Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')
 Tuple.__doc__ = \
@@ -3556,3 +3571,18 @@ def method(self) -> None:
         # read-only property, TypeError if it's a builtin class.
         pass
     return method
+
+
+def __getattr__(attr):
+    if attr == "ByteString":
+        import warnings
+        warnings._deprecated("typing.ByteString", remove=(3, 14))
+        with warnings.catch_warnings(
+            action="ignore", category=DeprecationWarning
+        ):
+            # Not generic
+            ByteString = globals()["ByteString"] = _DeprecatedGenericAlias(
+                collections.abc.ByteString, 0, removal_version=(3, 14)
+            )
+        return ByteString
+    raise AttributeError(f"module 'typing' has no attribute {attr!r}")



More information about the Python-checkins mailing list