Skip to content

Commit f0f5bb3

Browse files
authored
gh-91896: Improve visibility of ByteString deprecation warnings (#104294)
1 parent a0a98dd commit f0f5bb3

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

Lib/collections/abc.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
from _collections_abc import *
22
from _collections_abc import __all__
33
from _collections_abc import _CallableGenericAlias
4+
5+
_deprecated_ByteString = globals().pop("ByteString")
6+
7+
def __getattr__(attr):
8+
if attr == "ByteString":
9+
import warnings
10+
warnings._deprecated("collections.abc.ByteString", remove=(3, 14))
11+
return _deprecated_ByteString
12+
raise AttributeError(f"module 'collections.abc' has no attribute {attr!r}")

Lib/test/libregrtest/refleak.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ def dash_R(ns, test_name, test_func):
4848
else:
4949
zdc = zipimport._zip_directory_cache.copy()
5050
abcs = {}
51-
for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]:
52-
if not isabstract(abc):
53-
continue
54-
for obj in abc.__subclasses__() + [abc]:
55-
abcs[obj] = _get_dump(obj)[0]
51+
# catch and ignore collections.abc.ByteString deprecation
52+
with warnings.catch_warnings(action='ignore', category=DeprecationWarning):
53+
for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]:
54+
if not isabstract(abc):
55+
continue
56+
for obj in abc.__subclasses__() + [abc]:
57+
abcs[obj] = _get_dump(obj)[0]
5658

5759
# bpo-31217: Integer pool to get a single integer object for the same
5860
# 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):
173175
zipimport._zip_directory_cache.update(zdc)
174176

175177
# Clear ABC registries, restoring previously saved ABC registries.
176-
abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__]
178+
# ignore deprecation warning for collections.abc.ByteString
179+
with warnings.catch_warnings(action='ignore', category=DeprecationWarning):
180+
abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__]
177181
abs_classes = filter(isabstract, abs_classes)
178182
for abc in abs_classes:
179183
for obj in abc.__subclasses__() + [abc]:

Lib/test/test_collections.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import string
1212
import sys
1313
from test import support
14+
from test.support.import_helper import import_fresh_module
1415
import types
1516
import unittest
1617

@@ -25,7 +26,7 @@
2526
from collections.abc import Set, MutableSet
2627
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
2728
from collections.abc import Sequence, MutableSequence
28-
from collections.abc import ByteString, Buffer
29+
from collections.abc import Buffer
2930

3031

3132
class TestUserObjects(unittest.TestCase):
@@ -1939,6 +1940,8 @@ def assert_index_same(seq1, seq2, index_args):
19391940
nativeseq, seqseq, (letter, start, stop))
19401941

19411942
def test_ByteString(self):
1943+
with self.assertWarns(DeprecationWarning):
1944+
from collections.abc import ByteString
19421945
for sample in [bytes, bytearray]:
19431946
with self.assertWarns(DeprecationWarning):
19441947
self.assertIsInstance(sample(), ByteString)
@@ -1960,6 +1963,11 @@ class X(ByteString): pass
19601963
# No metaclass conflict
19611964
class Z(ByteString, Awaitable): pass
19621965

1966+
def test_ByteString_attribute_access(self):
1967+
collections_abc = import_fresh_module("collections.abc")
1968+
with self.assertWarns(DeprecationWarning):
1969+
collections_abc.ByteString
1970+
19631971
def test_Buffer(self):
19641972
for sample in [bytes, bytearray, memoryview]:
19651973
self.assertIsInstance(sample(b"x"), Buffer)

Lib/test/test_typing.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
import sys
1010
import warnings
11+
from test.support.import_helper import import_fresh_module
1112
from unittest import TestCase, main, skipUnless, skip
1213
from unittest.mock import patch
1314
from copy import copy, deepcopy
@@ -3908,7 +3909,14 @@ class MyChain(typing.ChainMap[str, T]): ...
39083909
self.assertEqual(MyChain[int]().__orig_class__, MyChain[int])
39093910

39103911
def test_all_repr_eq_any(self):
3911-
objs = (getattr(typing, el) for el in typing.__all__)
3912+
typing = import_fresh_module("typing")
3913+
with warnings.catch_warnings(record=True) as wlog:
3914+
warnings.filterwarnings('always', '', DeprecationWarning)
3915+
objs = [getattr(typing, el) for el in typing.__all__]
3916+
self.assertEqual(
3917+
[str(w.message) for w in wlog],
3918+
["'typing.ByteString' is deprecated and slated for removal in Python 3.14"]
3919+
)
39123920
for obj in objs:
39133921
self.assertNotEqual(repr(obj), '')
39143922
self.assertEqual(obj, obj)
@@ -5996,8 +6004,16 @@ def test_mutablesequence(self):
59966004
self.assertNotIsInstance((), typing.MutableSequence)
59976005

59986006
def test_bytestring(self):
5999-
self.assertIsInstance(b'', typing.ByteString)
6000-
self.assertIsInstance(bytearray(b''), typing.ByteString)
6007+
with self.assertWarns(DeprecationWarning):
6008+
from typing import ByteString
6009+
with self.assertWarns(DeprecationWarning):
6010+
self.assertIsInstance(b'', ByteString)
6011+
with self.assertWarns(DeprecationWarning):
6012+
self.assertIsInstance(bytearray(b''), ByteString)
6013+
with self.assertWarns(DeprecationWarning):
6014+
class Foo(ByteString): ...
6015+
with self.assertWarns(DeprecationWarning):
6016+
class Bar(ByteString, typing.Awaitable): ...
60016017

60026018
def test_list(self):
60036019
self.assertIsSubclass(list, typing.List)
@@ -8293,6 +8309,10 @@ def test_no_isinstance(self):
82938309
class SpecialAttrsTests(BaseTestCase):
82948310

82958311
def test_special_attrs(self):
8312+
with warnings.catch_warnings(
8313+
action='ignore', category=DeprecationWarning
8314+
):
8315+
typing_ByteString = typing.ByteString
82968316
cls_to_check = {
82978317
# ABC classes
82988318
typing.AbstractSet: 'AbstractSet',
@@ -8301,7 +8321,7 @@ def test_special_attrs(self):
83018321
typing.AsyncIterable: 'AsyncIterable',
83028322
typing.AsyncIterator: 'AsyncIterator',
83038323
typing.Awaitable: 'Awaitable',
8304-
typing.ByteString: 'ByteString',
8324+
typing_ByteString: 'ByteString',
83058325
typing.Callable: 'Callable',
83068326
typing.ChainMap: 'ChainMap',
83078327
typing.Collection: 'Collection',
@@ -8626,6 +8646,8 @@ def test_all_exported_names(self):
86268646
getattr(v, '__module__', None) == typing.__name__
86278647
)
86288648
}
8649+
# Deprecated; added dynamically via module __getattr__
8650+
computed_all.add("ByteString")
86298651
self.assertSetEqual(computed_all, actual_all)
86308652

86318653

Lib/typing.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,22 @@ def __or__(self, right):
15991599
def __ror__(self, left):
16001600
return Union[left, self]
16011601

1602+
1603+
class _DeprecatedGenericAlias(_SpecialGenericAlias, _root=True):
1604+
def __init__(
1605+
self, origin, nparams, *, removal_version, inst=True, name=None
1606+
):
1607+
super().__init__(origin, nparams, inst=inst, name=name)
1608+
self._removal_version = removal_version
1609+
1610+
def __instancecheck__(self, inst):
1611+
import warnings
1612+
warnings._deprecated(
1613+
f"{self.__module__}.{self._name}", remove=self._removal_version
1614+
)
1615+
return super().__instancecheck__(inst)
1616+
1617+
16021618
class _CallableGenericAlias(_NotIterable, _GenericAlias, _root=True):
16031619
def __repr__(self):
16041620
assert self._name == 'Callable'
@@ -2756,7 +2772,6 @@ class Other(Leaf): # Error reported by type checker
27562772
MutableMapping = _alias(collections.abc.MutableMapping, 2)
27572773
Sequence = _alias(collections.abc.Sequence, 1)
27582774
MutableSequence = _alias(collections.abc.MutableSequence, 1)
2759-
ByteString = _alias(collections.abc.ByteString, 0) # Not generic
27602775
# Tuple accepts variable number of parameters.
27612776
Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')
27622777
Tuple.__doc__ = \
@@ -3556,3 +3571,18 @@ def method(self) -> None:
35563571
# read-only property, TypeError if it's a builtin class.
35573572
pass
35583573
return method
3574+
3575+
3576+
def __getattr__(attr):
3577+
if attr == "ByteString":
3578+
import warnings
3579+
warnings._deprecated("typing.ByteString", remove=(3, 14))
3580+
with warnings.catch_warnings(
3581+
action="ignore", category=DeprecationWarning
3582+
):
3583+
# Not generic
3584+
ByteString = globals()["ByteString"] = _DeprecatedGenericAlias(
3585+
collections.abc.ByteString, 0, removal_version=(3, 14)
3586+
)
3587+
return ByteString
3588+
raise AttributeError(f"module 'typing' has no attribute {attr!r}")

0 commit comments

Comments
 (0)