Skip to content

Commit

Permalink
pythongh-91896: Improve visibility of ByteString deprecation warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed May 8, 2023
1 parent 1f56795 commit 424486a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 6 deletions.
9 changes: 9 additions & 0 deletions Lib/collections/abc.py
Original file line number Diff line number Diff line change
@@ -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}")
10 changes: 9 additions & 1 deletion Lib/test/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
30 changes: 26 additions & 4 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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):
def __len__(self): return 42
def __getitem__(self, n): return 42

def test_list(self):
self.assertIsSubclass(list, typing.List)
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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)


Expand Down
32 changes: 31 additions & 1 deletion Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,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'
Expand Down Expand Up @@ -2763,7 +2779,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__ = \
Expand Down Expand Up @@ -3563,3 +3578,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}")

0 comments on commit 424486a

Please sign in to comment.