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

This commit is contained in:
Alex Waygood 2023-05-12 07:01:31 +01:00 committed by GitHub
parent a0a98ddb31
commit f0f5bb3204
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 12 deletions

View file

@ -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}")

View file

@ -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]:

View file

@ -11,6 +11,7 @@ from itertools import product, chain, combinations
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 Sized, Container, Callable, Collection
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 @@ class TestCollectionABCs(ABCTestCase):
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 TestCollectionABCs(ABCTestCase):
# 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)

View file

@ -8,6 +8,7 @@ import pickle
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 GenericTests(BaseTestCase):
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 @@ class CollectionsAbcTests(BaseTestCase):
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 @@ SpecialAttrsT = typing.TypeVar('SpecialAttrsT', int, float, complex)
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 @@ class SpecialAttrsTests(BaseTestCase):
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 @@ class AllTests(BaseTestCase):
getattr(v, '__module__', None) == typing.__name__
)
}
# Deprecated; added dynamically via module __getattr__
computed_all.add("ByteString")
self.assertSetEqual(computed_all, actual_all)

View file

@ -1599,6 +1599,22 @@ class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True):
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 @@ Mapping = _alias(collections.abc.Mapping, 2)
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 override(method: F, /) -> F:
# 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}")