Skip to content

Commit 9ac3de0

Browse files
committed
gh-105578: Raise DeprecationWarning on typing.AnyStr usage
1 parent c35b0f2 commit 9ac3de0

6 files changed

Lines changed: 49 additions & 20 deletions

File tree

Doc/deprecations/pending-removal-in-3.18.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Pending removal in Python 3.18
1111
C implementation, has been deprecated since Python 3.13.
1212
(Contributed by Serhiy Storchaka in :gh:`89902`.)
1313

14+
* :mod:`typing`:
15+
16+
* :data:`typing.AnyStr` has been discouraged
17+
and deprecated since Python 3.16.
18+
Use :pep:`695` syntax instead.
19+
1420
* Deprecations defined by :pep:`829`:
1521

1622
* ``import`` lines in :file:`{name}.pth` files are silently ignored.

Doc/library/typing.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -899,8 +899,8 @@ using ``[]``.
899899
Use ``class A[T: (str, bytes)]: ...`` instead of importing ``AnyStr``. See
900900
:pep:`695` for more details.
901901

902-
In Python 3.16, ``AnyStr`` will be removed from ``typing.__all__``, and
903-
deprecation warnings will be emitted at runtime when it is accessed or
902+
Since Python 3.16, ``AnyStr`` is removed from ``typing.__all__``, and
903+
deprecation warnings is emitted at runtime when it is accessed or
904904
imported from ``typing``. ``AnyStr`` will be removed from ``typing``
905905
in Python 3.18.
906906

Doc/whatsnew/3.16.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ Deprecated
195195
3.9, now issues a deprecation warning on use. This property is slated for
196196
removal in 3.21. Use ``ast.Tuple.elts`` instead.
197197

198+
* :mod:`typing`:
199+
200+
* :data:`typing.AnyStr` now raises :exc:`DeprecationWarning` when used.
201+
198202
.. Add deprecations above alphabetically, not here at the end.
199203
200204

Lib/test/test_typing.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from typing import Any, NoReturn, Never, assert_never
2222
from typing import overload, get_overloads, clear_overloads
23-
from typing import TypeVar, TypeVarTuple, Unpack, AnyStr
23+
from typing import TypeVar, TypeVarTuple, Unpack
2424
from typing import T, KT, VT # Not in __all__.
2525
from typing import Union, Optional, Literal
2626
from typing import Tuple, List, Dict, MutableMapping
@@ -49,6 +49,11 @@
4949
import weakref
5050
import types
5151

52+
with warnings.catch_warnings():
53+
warnings.simplefilter("ignore", DeprecationWarning)
54+
55+
from typing import AnyStr
56+
5257
from test.support import (
5358
captured_stderr, cpython_only, requires_docstrings, import_helper, run_code,
5459
subTests, EqualToForwardRef,
@@ -9551,12 +9556,16 @@ def test_no_isinstance(self):
95519556
class IOTests(BaseTestCase):
95529557

95539558
def test_io(self):
9554-
95559559
def stuff(a: IO) -> AnyStr:
95569560
return a.readline()
95579561

95589562
a = stuff.__annotations__['a']
9559-
self.assertEqual(a.__parameters__, (AnyStr,))
9563+
self.assertEqual(len(a.__parameters__), 1)
9564+
any_str = a.__parameters__[0]
9565+
self.assertEqual(repr(any_str), 'AnyStr')
9566+
self.assertEqual(any_str.__bound__, None)
9567+
self.assertEqual(any_str.__constraints__, (bytes, str))
9568+
self.assertEqual(any_str.__constraints__, AnyStr.__constraints__)
95609569

95619570
def test_textio(self):
95629571

@@ -11176,13 +11185,20 @@ def test_all_exported_names(self):
1117611185
# there's a few types and metaclasses that aren't exported
1117711186
not k.endswith(('Meta', '_contra', '_co')) and
1117811187
not k.upper() == k and
11179-
k not in {"ByteString"} and
11188+
k not in {"ByteString", "AnyStr"} and
1118011189
# but export all other things that have __module__ == 'typing'
1118111190
getattr(v, '__module__', None) == typing.__name__
1118211191
)
1118311192
}
1118411193
self.assertSetEqual(computed_all, actual_all)
1118511194

11195+
def test_any_str_deprecated(self):
11196+
# gh-105578
11197+
fresh = import_helper.import_fresh_module('typing')
11198+
11199+
with self.assertWarnsRegex(DeprecationWarning, r'typing\.AnyStr'):
11200+
fresh.AnyStr
11201+
1118611202

1118711203
class TypeIterationTests(BaseTestCase):
1118811204
_UNITERABLE_TYPES = (

Lib/typing.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@
121121
'TextIO',
122122

123123
# One-off things.
124-
'AnyStr',
125124
'assert_type',
126125
'assert_never',
127126
'cast',
@@ -2815,11 +2814,6 @@ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error
28152814
CT_co = TypeVar('CT_co', covariant=True, bound=type)
28162815

28172816

2818-
# A useful type variable with constraints. This represents string types.
2819-
# (This one *is* for export!)
2820-
AnyStr = TypeVar('AnyStr', bytes, str)
2821-
2822-
28232817
# Various ABCs mimicking those in collections.abc.
28242818
_alias = _SpecialGenericAlias
28252819

@@ -3523,8 +3517,9 @@ def __ror__(self, other):
35233517
# Constant that's True when type checking, but False here.
35243518
TYPE_CHECKING = False
35253519

3526-
3527-
class IO(Generic[AnyStr]):
3520+
# Since `AnyStr` public API is deprecated since 3.16 and will be removed
3521+
# in 3.18, we need to keep `IO` protocol the same as it used to be:
3522+
class IO[AnyStr: (bytes, str)]:
35283523
"""Generic base class for TextIO and BinaryIO.
35293524
35303525
This is an abstract, generic version of the return of open().
@@ -3571,19 +3566,19 @@ def isatty(self) -> bool:
35713566
pass
35723567

35733568
@abstractmethod
3574-
def read(self, n: int = -1, /) -> AnyStr:
3569+
def read(self, n: int = -1, /) -> _AnyStr:
35753570
pass
35763571

35773572
@abstractmethod
35783573
def readable(self) -> bool:
35793574
pass
35803575

35813576
@abstractmethod
3582-
def readline(self, limit: int = -1, /) -> AnyStr:
3577+
def readline(self, limit: int = -1, /) -> _AnyStr:
35833578
pass
35843579

35853580
@abstractmethod
3586-
def readlines(self, hint: int = -1, /) -> list[AnyStr]:
3581+
def readlines(self, hint: int = -1, /) -> list[_AnyStr]:
35873582
pass
35883583

35893584
@abstractmethod
@@ -3607,15 +3602,15 @@ def writable(self) -> bool:
36073602
pass
36083603

36093604
@abstractmethod
3610-
def write(self, s: AnyStr, /) -> int:
3605+
def write(self, s: _AnyStr, /) -> int:
36113606
pass
36123607

36133608
@abstractmethod
3614-
def writelines(self, lines: list[AnyStr], /) -> None:
3609+
def writelines(self, lines: list[_AnyStr], /) -> None:
36153610
pass
36163611

36173612
@abstractmethod
3618-
def __enter__(self) -> IO[AnyStr]:
3613+
def __enter__(self) -> IO[_AnyStr]:
36193614
pass
36203615

36213616
@abstractmethod
@@ -3924,6 +3919,12 @@ def __subclasscheck__(self, cls):
39243919
)
39253920

39263921
return ByteString
3922+
elif attr == "AnyStr":
3923+
import warnings
3924+
3925+
warnings._deprecated("typing.AnyStr", remove=(3, 18))
3926+
AnyStr = globals()["AnyStr"] = TypeVar('AnyStr', bytes, str)
3927+
return AnyStr
39273928
else:
39283929
raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
39293930
globals()[attr] = obj
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deprecate :data:`typing.AnyStr` type variable and raise a warning, when it
2+
is imported.

0 commit comments

Comments
 (0)