Drop support for Python 3.7 (#267)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 78610e2..e221022 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -42,8 +42,6 @@
# For available versions, see:
# https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json
python-version:
- - "3.7"
- - "3.7.1"
- "3.8"
- "3.8.0"
- "3.9"
@@ -53,7 +51,6 @@
- "3.11"
- "3.11.0"
- "3.12"
- - "pypy3.7"
- "pypy3.8"
- "pypy3.9"
- "pypy3.10"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e490c5..f67da87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# Release 4.8.0 (???)
+
+- Drop support for Python 3.7 (including PyPy-3.7). Patch by Alex Waygood.
+
# Release 4.7.1 (July 2, 2023)
- Fix support for `TypedDict`, `NamedTuple` and `is_protocol` on PyPy-3.7 and
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9d07313..a118a40 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,8 +18,6 @@
standard library. Such features should already be specified in a PEP or merged into
CPython's `main` branch.
-`typing_extensions` supports Python versions 3.7 and up.
-
# Versioning scheme
Starting with version 4.0.0, `typing_extensions` uses
diff --git a/README.md b/README.md
index efd3a82..1eddb2a 100644
--- a/README.md
+++ b/README.md
@@ -26,8 +26,6 @@
on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,
where `x.y` is the first version that includes all features you need.
-`typing_extensions` supports Python versions 3.7 and higher.
-
## Included items
See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a
diff --git a/doc/index.rst b/doc/index.rst
index 5fd2b2e..7e68e27 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -135,7 +135,7 @@
Python version support
----------------------
-``typing_extensions`` currently supports Python versions 3.7 and higher. In the future,
+``typing_extensions`` currently supports Python versions 3.8 and higher. In the future,
support for older Python versions will be dropped some time after that version
reaches end of life.
diff --git a/pyproject.toml b/pyproject.toml
index 736e1e4..6c36b14 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,9 +7,9 @@
[project]
name = "typing_extensions"
version = "4.7.1"
-description = "Backported and Experimental Type Hints for Python 3.7+"
+description = "Backported and Experimental Type Hints for Python 3.8+"
readme = "README.md"
-requires-python = ">=3.7"
+requires-python = ">=3.8"
license = { file = "LICENSE" }
keywords = [
"annotations",
@@ -34,7 +34,6 @@
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
index c2ab6d7..78fe0c0 100644
--- a/src/test_typing_extensions.py
+++ b/src/test_typing_extensions.py
@@ -42,7 +42,6 @@
# Flags used to mark tests that only apply after a specific
# version of the typing module.
-TYPING_3_8_0 = sys.version_info[:3] >= (3, 8, 0)
TYPING_3_9_0 = sys.version_info[:3] >= (3, 9, 0)
TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0)
@@ -52,10 +51,6 @@
# 3.12 changes the representation of Unpack[] (PEP 692)
TYPING_3_12_0 = sys.version_info[:3] >= (3, 12, 0)
-only_with_typing_Protocol = skipUnless(
- hasattr(typing, "Protocol"), "Only relevant when typing.Protocol exists"
-)
-
# https://github.com/python/cpython/pull/27017 was backported into some 3.9 and 3.10
# versions, but not all
HAS_FORWARD_MODULE = "module" in inspect.signature(typing._type_check).parameters
@@ -246,13 +241,7 @@
def some_str(arg: 'NoReturn') -> 'typing.NoReturn': ...
expected = {'arg': NoReturn, 'return': NoReturn}
- targets = [some]
-
- # On 3.7.0 and 3.7.1, https://github.com/python/cpython/pull/10772
- # wasn't applied yet and NoReturn fails _type_check.
- if not ((3, 7, 0) <= sys.version_info < (3, 7, 2)):
- targets.append(some_str)
- for target in targets:
+ for target in some, some_str:
with self.subTest(target=target):
self.assertEqual(gth(target), expected)
@@ -595,15 +584,11 @@
Final[int][str]
def test_repr(self):
- if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
- mod_name = 'typing'
- else:
- mod_name = 'typing_extensions'
- self.assertEqual(repr(Final), mod_name + '.Final')
+ self.assertEqual(repr(Final), 'typing.Final')
cv = Final[int]
- self.assertEqual(repr(cv), mod_name + '.Final[int]')
+ self.assertEqual(repr(cv), 'typing.Final[int]')
cv = Final[Employee]
- self.assertEqual(repr(cv), mod_name + f'.Final[{__name__}.Employee]')
+ self.assertEqual(repr(cv), f'typing.Final[{__name__}.Employee]')
def test_cannot_subclass(self):
with self.assertRaises(TypeError):
@@ -1771,7 +1756,6 @@
self.assertNotIsInstance(D(), E)
self.assertNotIsInstance(E(), D)
- @only_with_typing_Protocol
def test_runtimecheckable_on_typing_dot_Protocol(self):
@runtime_checkable
class Foo(typing.Protocol):
@@ -1784,7 +1768,6 @@
self.assertIsInstance(Bar(), Foo)
self.assertNotIsInstance(object(), Foo)
- @only_with_typing_Protocol
def test_typing_dot_runtimecheckable_on_Protocol(self):
@typing.runtime_checkable
class Foo(Protocol):
@@ -1797,7 +1780,6 @@
self.assertIsInstance(Bar(), Foo)
self.assertNotIsInstance(object(), Foo)
- @only_with_typing_Protocol
def test_typing_Protocol_and_extensions_Protocol_can_mix(self):
class TypingProto(typing.Protocol):
x: int
@@ -3173,7 +3155,6 @@
with self.assertRaisesRegex(TypeError, "not a Protocol"):
get_protocol_members(ConcreteInherit())
- @only_with_typing_Protocol
def test_get_protocol_members_typing(self):
with self.assertRaisesRegex(TypeError, "not a Protocol"):
get_protocol_members(typing.Protocol)
@@ -3222,7 +3203,6 @@
# Protocol is not itself a protocol
self.assertFalse(is_protocol(Protocol))
- @only_with_typing_Protocol
def test_is_protocol_with_typing(self):
self.assertFalse(is_protocol(typing.Protocol))
@@ -3681,7 +3661,6 @@
if hasattr(typing, "TypedDict"):
self.assertIs(is_typeddict(typing.TypedDict), False)
- @skipUnless(TYPING_3_8_0, "Python 3.8+ required")
def test_is_typeddict_against_typeddict_from_typing(self):
Point = typing.TypedDict('Point', {'x': int, 'y': int})
@@ -3844,7 +3823,7 @@
def test_non_generic_subscript(self):
# For backward compatibility, subscription works
# on arbitrary TypedDict types.
- # (But we don't attempt to backport this misfeature onto 3.7 and 3.8.)
+ # (But we don't attempt to backport this misfeature onto 3.8.)
class TD(TypedDict):
a: T
A = TD[int]
@@ -4034,17 +4013,8 @@
classvar: Annotated[ClassVar[int], "a decoration"] = 4
const: Annotated[Final[int], "Const"] = 4
- if sys.version_info[:2] >= (3, 7):
- self.assertEqual(get_type_hints(C, globals())["classvar"], ClassVar[int])
- self.assertEqual(get_type_hints(C, globals())["const"], Final[int])
- else:
- self.assertEqual(
- get_type_hints(C, globals())["classvar"],
- Annotated[ClassVar[int], "a decoration"]
- )
- self.assertEqual(
- get_type_hints(C, globals())["const"], Annotated[Final[int], "Const"]
- )
+ self.assertEqual(get_type_hints(C, globals())["classvar"], ClassVar[int])
+ self.assertEqual(get_type_hints(C, globals())["const"], Final[int])
def test_cannot_subclass(self):
with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"):
@@ -5069,11 +5039,8 @@
'dataclass_transform',
'overload',
'ParamSpec',
- 'Text',
'TypeVar',
'TypeVarTuple',
- 'TYPE_CHECKING',
- 'Final',
'get_type_hints',
}
if sys.version_info < (3, 10):
@@ -5189,13 +5156,6 @@
x: int = 3
y: int
- @skipUnless(
- (
- TYPING_3_8_0
- or hasattr(CoolEmployeeWithDefault, '_field_defaults')
- ),
- '"_field_defaults" attribute was added in a micro version of 3.7'
- )
def test_field_defaults(self):
self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0))
@@ -5296,7 +5256,7 @@
self.assertEqual(a, (1, [2]))
@skipIf(TYPING_3_9_0, "Test isn't relevant to 3.9+")
- def test_non_generic_subscript_error_message_py38_minus(self):
+ def test_non_generic_subscript_error_message_py38(self):
class Group(NamedTuple):
key: T
group: List[T]
@@ -5389,10 +5349,7 @@
self.assertEqual(struct._fields, ())
self.assertEqual(struct.__annotations__, {})
self.assertIsInstance(struct(), struct)
- # Attribute was added in a micro version of 3.7
- # and is tested more fully elsewhere
- if hasattr(struct, "_field_defaults"):
- self.assertEqual(struct._field_defaults, {})
+ self.assertEqual(struct._field_defaults, {})
def test_namedtuple_errors(self):
with self.assertRaises(TypeError):
@@ -5429,15 +5386,6 @@
def test_docstring(self):
self.assertIsInstance(NamedTuple.__doc__, str)
- @skipUnless(TYPING_3_8_0, "NamedTuple had a bad signature on <=3.7")
- def test_signature_is_same_as_typing_NamedTuple(self):
- self.assertEqual(inspect.signature(NamedTuple), inspect.signature(typing.NamedTuple))
-
- @skipIf(TYPING_3_8_0, "tests are only relevant to <=3.7")
- def test_signature_on_37(self):
- self.assertIsInstance(inspect.signature(NamedTuple), inspect.Signature)
- self.assertFalse(hasattr(NamedTuple, "__text_signature__"))
-
@skipUnless(TYPING_3_9_0, "NamedTuple was a class on 3.8 and lower")
def test_same_as_typing_NamedTuple_39_plus(self):
self.assertEqual(
@@ -5592,7 +5540,7 @@
r"Bound must be a type\. Got \(1, 2\)\."):
TypeVar('X', bound=(1, 2))
- # Technically we could run it on later versions of 3.7 and 3.8,
+ # Technically we could run it on later versions of 3.8,
# but that's not worth the effort.
@skipUnless(TYPING_3_9_0, "Fix was not backported")
def test_missing__name__(self):
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
index 901f3b9..0978c5f 100644
--- a/src/typing_extensions.py
+++ b/src/typing_extensions.py
@@ -248,32 +248,7 @@
return 'typing_extensions.' + self._name
-# On older versions of typing there is an internal class named "Final".
-# 3.8+
-if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
- Final = typing.Final
-# 3.7
-else:
- class _FinalForm(_ExtensionsSpecialForm, _root=True):
- def __getitem__(self, parameters):
- item = typing._type_check(parameters,
- f'{self._name} accepts only a single type.')
- return typing._GenericAlias(self, (item,))
-
- Final = _FinalForm('Final',
- doc="""A special typing construct to indicate that a name
- cannot be re-assigned or overridden in a subclass.
- For example:
-
- MAX_SIZE: Final = 9000
- MAX_SIZE += 1 # Error reported by type checker
-
- class Connection:
- TIMEOUT: Final[int] = 10
- class FastConnector(Connection):
- TIMEOUT = 1 # Error reported by type checker
-
- There is no runtime checking of these properties.""")
+Final = typing.Final
if sys.version_info >= (3, 11):
final = typing.final
@@ -465,8 +440,6 @@
# Various ABCs mimicking those in collections.abc.
# A few are simply re-exported for completeness.
-
-
Awaitable = typing.Awaitable
Coroutine = typing.Coroutine
AsyncIterable = typing.AsyncIterable
@@ -475,14 +448,7 @@
ContextManager = typing.ContextManager
AsyncContextManager = typing.AsyncContextManager
DefaultDict = typing.DefaultDict
-
-# 3.7.2+
-if hasattr(typing, 'OrderedDict'):
- OrderedDict = typing.OrderedDict
-# 3.7.0-3.7.2
-else:
- OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
-
+OrderedDict = typing.OrderedDict
Counter = typing.Counter
ChainMap = typing.ChainMap
AsyncGenerator = typing.AsyncGenerator
@@ -508,12 +474,6 @@
"__protocol_attrs__", "__callable_proto_members_only__",
}
-if sys.version_info < (3, 8):
- _EXCLUDED_ATTRS |= {
- "_gorg", "__next_in_mro__", "__extra__", "__tree_hash__", "__args__",
- "__origin__"
- }
-
if sys.version_info >= (3, 9):
_EXCLUDED_ATTRS.add("__class_getitem__")
@@ -535,46 +495,6 @@
return attrs
-def _maybe_adjust_parameters(cls):
- """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
-
- The contents of this function are very similar
- to logic found in typing.Generic.__init_subclass__
- on the CPython main branch.
- """
- tvars = []
- if '__orig_bases__' in cls.__dict__:
- tvars = _collect_type_vars(cls.__orig_bases__)
- # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
- # If found, tvars must be a subset of it.
- # If not found, tvars is it.
- # Also check for and reject plain Generic,
- # and reject multiple Generic[...] and/or Protocol[...].
- gvars = None
- for base in cls.__orig_bases__:
- if (isinstance(base, typing._GenericAlias) and
- base.__origin__ in (typing.Generic, Protocol)):
- # for error messages
- the_base = base.__origin__.__name__
- if gvars is not None:
- raise TypeError(
- "Cannot inherit from Generic[...]"
- " and/or Protocol[...] multiple types.")
- gvars = base.__parameters__
- if gvars is None:
- gvars = tvars
- else:
- tvarset = set(tvars)
- gvarset = set(gvars)
- if not tvarset <= gvarset:
- s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
- s_args = ', '.join(str(g) for g in gvars)
- raise TypeError(f"Some type variables ({s_vars}) are"
- f" not listed in {the_base}[{s_args}]")
- tvars = gvars
- cls.__parameters__ = tuple(tvars)
-
-
def _caller(depth=2):
try:
return sys._getframe(depth).f_globals.get('__name__', '__main__')
@@ -598,17 +518,10 @@
if type(self)._is_protocol:
raise TypeError('Protocols cannot be instantiated')
- if sys.version_info >= (3, 8):
- # Inheriting from typing._ProtocolMeta isn't actually desirable,
- # but is necessary to allow typing.Protocol and typing_extensions.Protocol
- # to mix without getting TypeErrors about "metaclass conflict"
- _typing_Protocol = typing.Protocol
- _ProtocolMetaBase = type(_typing_Protocol)
- else:
- _typing_Protocol = _marker
- _ProtocolMetaBase = abc.ABCMeta
-
- class _ProtocolMeta(_ProtocolMetaBase):
+ # Inheriting from typing._ProtocolMeta isn't actually desirable,
+ # but is necessary to allow typing.Protocol and typing_extensions.Protocol
+ # to mix without getting TypeErrors about "metaclass conflict"
+ class _ProtocolMeta(type(typing.Protocol)):
# This metaclass is somewhat unfortunate,
# but is necessary for several reasons...
#
@@ -618,10 +531,10 @@
def __new__(mcls, name, bases, namespace, **kwargs):
if name == "Protocol" and len(bases) < 2:
pass
- elif {Protocol, _typing_Protocol} & set(bases):
+ elif {Protocol, typing.Protocol} & set(bases):
for base in bases:
if not (
- base in {object, typing.Generic, Protocol, _typing_Protocol}
+ base in {object, typing.Generic, Protocol, typing.Protocol}
or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
or is_protocol(base)
):
@@ -699,12 +612,10 @@
def __eq__(cls, other):
# Hack so that typing.Generic.__class_getitem__
# treats typing_extensions.Protocol
- # as equivalent to typing.Protocol on Python 3.8+
+ # as equivalent to typing.Protocol
if abc.ABCMeta.__eq__(cls, other) is True:
return True
- return (
- cls is Protocol and other is getattr(typing, "Protocol", object())
- )
+ return cls is Protocol and other is typing.Protocol
# This has to be defined, or the abc-module cache
# complains about classes with this metaclass being unhashable,
@@ -737,146 +648,33 @@
return NotImplemented
return True
- if sys.version_info >= (3, 8):
- class Protocol(typing.Generic, metaclass=_ProtocolMeta):
- __doc__ = typing.Protocol.__doc__
- __slots__ = ()
- _is_protocol = True
- _is_runtime_protocol = False
+ class Protocol(typing.Generic, metaclass=_ProtocolMeta):
+ __doc__ = typing.Protocol.__doc__
+ __slots__ = ()
+ _is_protocol = True
+ _is_runtime_protocol = False
- def __init_subclass__(cls, *args, **kwargs):
- super().__init_subclass__(*args, **kwargs)
+ def __init_subclass__(cls, *args, **kwargs):
+ super().__init_subclass__(*args, **kwargs)
- # Determine if this is a protocol or a concrete subclass.
- if not cls.__dict__.get('_is_protocol', False):
- cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+ # Determine if this is a protocol or a concrete subclass.
+ if not cls.__dict__.get('_is_protocol', False):
+ cls._is_protocol = any(b is Protocol for b in cls.__bases__)
- # Set (or override) the protocol subclass hook.
- if '__subclasshook__' not in cls.__dict__:
- cls.__subclasshook__ = _proto_hook
+ # Set (or override) the protocol subclass hook.
+ if '__subclasshook__' not in cls.__dict__:
+ cls.__subclasshook__ = _proto_hook
- # Prohibit instantiation for protocol classes
- if cls._is_protocol and cls.__init__ is Protocol.__init__:
- cls.__init__ = _no_init
-
- else:
- class Protocol(metaclass=_ProtocolMeta):
- # There is quite a lot of overlapping code with typing.Generic.
- # Unfortunately it is hard to avoid this on Python <3.8,
- # as the typing module on Python 3.7 doesn't let us subclass typing.Generic!
- """Base class for protocol classes. Protocol classes are defined as::
-
- class Proto(Protocol):
- def meth(self) -> int:
- ...
-
- Such classes are primarily used with static type checkers that recognize
- structural subtyping (static duck-typing), for example::
-
- class C:
- def meth(self) -> int:
- return 0
-
- def func(x: Proto) -> int:
- return x.meth()
-
- func(C()) # Passes static type check
-
- See PEP 544 for details. Protocol classes decorated with
- @typing_extensions.runtime_checkable act
- as simple-minded runtime-checkable protocols that check
- only the presence of given attributes, ignoring their type signatures.
-
- Protocol classes can be generic, they are defined as::
-
- class GenProto(Protocol[T]):
- def meth(self) -> T:
- ...
- """
- __slots__ = ()
- _is_protocol = True
- _is_runtime_protocol = False
-
- def __new__(cls, *args, **kwds):
- if cls is Protocol:
- raise TypeError("Type Protocol cannot be instantiated; "
- "it can only be used as a base class")
- return super().__new__(cls)
-
- @typing._tp_cache
- def __class_getitem__(cls, params):
- if not isinstance(params, tuple):
- params = (params,)
- if not params and cls is not typing.Tuple:
- raise TypeError(
- f"Parameter list to {cls.__qualname__}[...] cannot be empty")
- msg = "Parameters to generic types must be types."
- params = tuple(typing._type_check(p, msg) for p in params)
- if cls is Protocol:
- # Generic can only be subscripted with unique type variables.
- if not all(isinstance(p, typing.TypeVar) for p in params):
- i = 0
- while isinstance(params[i], typing.TypeVar):
- i += 1
- raise TypeError(
- "Parameters to Protocol[...] must all be type variables."
- f" Parameter {i + 1} is {params[i]}")
- if len(set(params)) != len(params):
- raise TypeError(
- "Parameters to Protocol[...] must all be unique")
- else:
- # Subscripting a regular Generic subclass.
- _check_generic(cls, params, len(cls.__parameters__))
- return typing._GenericAlias(cls, params)
-
- def __init_subclass__(cls, *args, **kwargs):
- if '__orig_bases__' in cls.__dict__:
- error = typing.Generic in cls.__orig_bases__
- else:
- error = typing.Generic in cls.__bases__
- if error:
- raise TypeError("Cannot inherit from plain Generic")
- _maybe_adjust_parameters(cls)
-
- # Determine if this is a protocol or a concrete subclass.
- if not cls.__dict__.get('_is_protocol', None):
- cls._is_protocol = any(b is Protocol for b in cls.__bases__)
-
- # Set (or override) the protocol subclass hook.
- if '__subclasshook__' not in cls.__dict__:
- cls.__subclasshook__ = _proto_hook
-
- # Prohibit instantiation for protocol classes
- if cls._is_protocol and cls.__init__ is Protocol.__init__:
- cls.__init__ = _no_init
+ # Prohibit instantiation for protocol classes
+ if cls._is_protocol and cls.__init__ is Protocol.__init__:
+ cls.__init__ = _no_init
-if sys.version_info >= (3, 8):
- runtime_checkable = typing.runtime_checkable
-else:
- def runtime_checkable(cls):
- """Mark a protocol class as a runtime protocol, so that it
- can be used with isinstance() and issubclass(). Raise TypeError
- if applied to a non-protocol class.
-
- This allows a simple-minded structural check very similar to the
- one-offs in collections.abc such as Hashable.
- """
- if not (
- (isinstance(cls, _ProtocolMeta) or issubclass(cls, typing.Generic))
- and getattr(cls, "_is_protocol", False)
- ):
- raise TypeError('@runtime_checkable can be only applied to protocol classes,'
- f' got {cls!r}')
- cls._is_runtime_protocol = True
- return cls
+# The "runtime" alias exists for backwards compatibility.
+runtime = runtime_checkable = typing.runtime_checkable
-# Exists for backwards compatibility.
-runtime = runtime_checkable
-
-
-# Our version of runtime-checkable protocols is faster on Python 3.7-3.11
+# Our version of runtime-checkable protocols is faster on Python 3.8-3.11
if sys.version_info >= (3, 12):
SupportsInt = typing.SupportsInt
SupportsFloat = typing.SupportsFloat
@@ -986,11 +784,6 @@
# 3.10.0 and later
_TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
- if sys.version_info >= (3, 8):
- _fake_name = "Protocol"
- else:
- _fake_name = "_Protocol"
-
class _TypedDictMeta(type):
def __new__(cls, name, bases, ns, total=True):
"""Create new typed dict class object.
@@ -1011,10 +804,10 @@
generic_base = ()
# typing.py generally doesn't let you inherit from plain Generic, unless
- # the name of the class happens to be "Protocol" (or "_Protocol" on 3.7).
- tp_dict = type.__new__(_TypedDictMeta, _fake_name, (*generic_base, dict), ns)
+ # the name of the class happens to be "Protocol"
+ tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns)
tp_dict.__name__ = name
- if tp_dict.__qualname__ == _fake_name:
+ if tp_dict.__qualname__ == "Protocol":
tp_dict.__qualname__ = name
if not hasattr(tp_dict, '__orig_bases__'):
@@ -1077,7 +870,7 @@
_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
@_ensure_subclassable(lambda bases: (_TypedDict,))
- def TypedDict(__typename, __fields=_marker, *, total=True, **kwargs):
+ def TypedDict(typename, fields=_marker, /, *, total=True, **kwargs):
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
TypedDict creates a dictionary type such that a type checker will expect all
@@ -1124,20 +917,20 @@
See PEP 655 for more details on Required and NotRequired.
"""
- if __fields is _marker or __fields is None:
- if __fields is _marker:
+ if fields is _marker or fields is None:
+ if fields is _marker:
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
else:
deprecated_thing = "Passing `None` as the 'fields' parameter"
- example = f"`{__typename} = TypedDict({__typename!r}, {{}})`"
+ example = f"`{typename} = TypedDict({typename!r}, {{}})`"
deprecation_msg = (
f"{deprecated_thing} is deprecated and will be disallowed in "
"Python 3.15. To create a TypedDict class with 0 fields "
"using the functional syntax, pass an empty dictionary, e.g. "
) + example + "."
warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
- __fields = kwargs
+ fields = kwargs
elif kwargs:
raise TypeError("TypedDict takes either a dict or keyword arguments,"
" but not both")
@@ -1150,13 +943,13 @@
stacklevel=2,
)
- ns = {'__annotations__': dict(__fields)}
+ ns = {'__annotations__': dict(fields)}
module = _caller()
if module is not None:
# Setting correct module is necessary to make typed dict classes pickleable.
ns['__module__'] = module
- td = _TypedDictMeta(__typename, (), ns, total=total)
+ td = _TypedDictMeta(typename, (), ns, total=total)
td.__orig_bases__ = (TypedDict,)
return td
@@ -1186,7 +979,7 @@
assert_type = typing.assert_type
else:
- def assert_type(__val, __typ):
+ def assert_type(val, typ, /):
"""Assert (to the type checker) that the value is of the given type.
When the type checker encounters a call to assert_type(), it
@@ -1199,12 +992,12 @@
At runtime this returns the first argument unchanged and otherwise
does nothing.
"""
- return __val
+ return val
-if hasattr(typing, "Required"):
+if hasattr(typing, "Required"): # 3.11+
get_type_hints = typing.get_type_hints
-else:
+else: # <=3.10
# replaces _strip_annotations()
def _strip_extras(t):
"""Strips Annotated, Required and NotRequired from a given type."""
@@ -1262,11 +1055,11 @@
- If two dict arguments are passed, they specify globals and
locals, respectively.
"""
- if hasattr(typing, "Annotated"):
+ if hasattr(typing, "Annotated"): # 3.9+
hint = typing.get_type_hints(
obj, globalns=globalns, localns=localns, include_extras=True
)
- else:
+ else: # 3.8
hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
if include_extras:
return hint
@@ -1279,7 +1072,7 @@
# Not exported and not a public API, but needed for get_origin() and get_args()
# to work.
_AnnotatedAlias = typing._AnnotatedAlias
-# 3.7-3.8
+# 3.8
else:
class _AnnotatedAlias(typing._GenericAlias, _root=True):
"""Runtime representation of an annotated type.
@@ -1384,7 +1177,7 @@
if sys.version_info[:2] >= (3, 10):
get_origin = typing.get_origin
get_args = typing.get_args
-# 3.7-3.9
+# 3.8-3.9
else:
try:
# 3.9+
@@ -1462,7 +1255,7 @@
It's invalid when used anywhere except as in the example above.
"""
raise TypeError(f"{self} is not subscriptable")
-# 3.7-3.8
+# 3.8
else:
TypeAlias = _ExtensionsSpecialForm(
'TypeAlias',
@@ -1519,7 +1312,7 @@
covariant=False, contravariant=False,
default=_marker, infer_variance=False):
if hasattr(typing, "TypeAliasType"):
- # PEP 695 implemented, can pass infer_variance to typing.TypeVar
+ # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar
typevar = typing.TypeVar(name, *constraints, bound=bound,
covariant=covariant, contravariant=contravariant,
infer_variance=infer_variance)
@@ -1541,7 +1334,7 @@
if hasattr(typing, 'ParamSpecArgs'):
ParamSpecArgs = typing.ParamSpecArgs
ParamSpecKwargs = typing.ParamSpecKwargs
-# 3.7-3.9
+# 3.8-3.9
else:
class _Immutable:
"""Mixin to indicate that object should not be copied."""
@@ -1630,7 +1423,7 @@
def __init_subclass__(cls) -> None:
raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type")
-# 3.7-3.9
+# 3.8-3.9
else:
# Inherits from list as a workaround for Callable checks in Python < 3.9.2.
@@ -1735,7 +1528,7 @@
pass
-# 3.7-3.9
+# 3.8-3.9
if not hasattr(typing, 'Concatenate'):
# Inherits from list as a workaround for Callable checks in Python < 3.9.2.
class _ConcatenateGenericAlias(list):
@@ -1770,7 +1563,7 @@
)
-# 3.7-3.9
+# 3.8-3.9
@typing._tp_cache
def _concatenate_getitem(self, parameters):
if parameters == ():
@@ -1804,7 +1597,7 @@
See PEP 612 for detailed information.
"""
return _concatenate_getitem(self, parameters)
-# 3.7-8
+# 3.8
else:
class _ConcatenateForm(_ExtensionsSpecialForm, _root=True):
def __getitem__(self, parameters):
@@ -1874,7 +1667,7 @@
"""
item = typing._type_check(parameters, f'{self} accepts only a single type.')
return typing._GenericAlias(self, (item,))
-# 3.7-3.8
+# 3.8
else:
class _TypeGuardForm(_ExtensionsSpecialForm, _root=True):
def __getitem__(self, parameters):
@@ -1972,7 +1765,7 @@
return self._getitem(self, parameters)
-if hasattr(typing, "LiteralString"):
+if hasattr(typing, "LiteralString"): # 3.11+
LiteralString = typing.LiteralString
else:
@_SpecialForm
@@ -1995,7 +1788,7 @@
raise TypeError(f"{self} is not subscriptable")
-if hasattr(typing, "Self"):
+if hasattr(typing, "Self"): # 3.11+
Self = typing.Self
else:
@_SpecialForm
@@ -2016,7 +1809,7 @@
raise TypeError(f"{self} is not subscriptable")
-if hasattr(typing, "Never"):
+if hasattr(typing, "Never"): # 3.11+
Never = typing.Never
else:
@_SpecialForm
@@ -2046,10 +1839,10 @@
raise TypeError(f"{self} is not subscriptable")
-if hasattr(typing, 'Required'):
+if hasattr(typing, 'Required'): # 3.11+
Required = typing.Required
NotRequired = typing.NotRequired
-elif sys.version_info[:2] >= (3, 9):
+elif sys.version_info[:2] >= (3, 9): # 3.9-3.10
@_ExtensionsSpecialForm
def Required(self, parameters):
"""A special typing construct to mark a key of a total=False TypedDict
@@ -2087,7 +1880,7 @@
item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
return typing._GenericAlias(self, (item,))
-else:
+else: # 3.8
class _RequiredForm(_ExtensionsSpecialForm, _root=True):
def __getitem__(self, parameters):
item = typing._type_check(parameters,
@@ -2175,7 +1968,7 @@
def _is_unpack(obj):
return get_origin(obj) is Unpack
-elif sys.version_info[:2] >= (3, 9):
+elif sys.version_info[:2] >= (3, 9): # 3.9+
class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True):
def __init__(self, getitem):
super().__init__(getitem)
@@ -2192,7 +1985,7 @@
def _is_unpack(obj):
return isinstance(obj, _UnpackAlias)
-else:
+else: # 3.8
class _UnpackAlias(typing._GenericAlias, _root=True):
__class__ = typing.TypeVar
@@ -2225,7 +2018,7 @@
def __init_subclass__(self, *args, **kwds):
raise TypeError("Cannot subclass special typing classes")
-else:
+else: # <=3.10
class TypeVarTuple(_DefaultMixin):
"""Type variable tuple.
@@ -2304,10 +2097,10 @@
raise TypeError("Cannot subclass special typing classes")
-if hasattr(typing, "reveal_type"):
+if hasattr(typing, "reveal_type"): # 3.11+
reveal_type = typing.reveal_type
-else:
- def reveal_type(__obj: T) -> T:
+else: # <=3.10
+ def reveal_type(obj: T, /) -> T:
"""Reveal the inferred type of a variable.
When a static type checker encounters a call to ``reveal_type()``,
@@ -2323,14 +2116,14 @@
argument and returns it unchanged.
"""
- print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
- return __obj
+ print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr)
+ return obj
-if hasattr(typing, "assert_never"):
+if hasattr(typing, "assert_never"): # 3.11+
assert_never = typing.assert_never
-else:
- def assert_never(__arg: Never) -> Never:
+else: # <=3.10
+ def assert_never(arg: Never, /) -> Never:
"""Assert to the type checker that a line of code is unreachable.
Example::
@@ -2353,10 +2146,10 @@
raise AssertionError("Expected code to be unreachable")
-if sys.version_info >= (3, 12):
+if sys.version_info >= (3, 12): # 3.12+
# dataclass_transform exists in 3.11 but lacks the frozen_default parameter
dataclass_transform = typing.dataclass_transform
-else:
+else: # <=3.11
def dataclass_transform(
*,
eq_default: bool = True,
@@ -2443,12 +2236,12 @@
return decorator
-if hasattr(typing, "override"):
+if hasattr(typing, "override"): # 3.12+
override = typing.override
-else:
+else: # <=3.11
_F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
- def override(__arg: _F) -> _F:
+ def override(arg: _F, /) -> _F:
"""Indicate that a method is intended to override a method in a base class.
Usage:
@@ -2475,13 +2268,13 @@
"""
try:
- __arg.__override__ = True
+ arg.__override__ = True
except (AttributeError, TypeError):
# Skip the attribute silently if it is not writable.
# AttributeError happens if the object has __slots__ or a
# read-only property, TypeError if it's a builtin class.
pass
- return __arg
+ return arg
if hasattr(typing, "deprecated"):
@@ -2490,7 +2283,8 @@
_T = typing.TypeVar("_T")
def deprecated(
- __msg: str,
+ msg: str,
+ /,
*,
category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
stacklevel: int = 1,
@@ -2533,17 +2327,17 @@
See PEP 702 for details.
"""
- def decorator(__arg: _T) -> _T:
+ def decorator(arg: _T, /) -> _T:
if category is None:
- __arg.__deprecated__ = __msg
- return __arg
- elif isinstance(__arg, type):
- original_new = __arg.__new__
- has_init = __arg.__init__ is not object.__init__
+ arg.__deprecated__ = msg
+ return arg
+ elif isinstance(arg, type):
+ original_new = arg.__new__
+ has_init = arg.__init__ is not object.__init__
@functools.wraps(original_new)
def __new__(cls, *args, **kwargs):
- warnings.warn(__msg, category=category, stacklevel=stacklevel + 1)
+ warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
if original_new is not object.__new__:
return original_new(cls, *args, **kwargs)
# Mirrors a similar check in object.__new__.
@@ -2552,21 +2346,21 @@
else:
return original_new(cls)
- __arg.__new__ = staticmethod(__new__)
- __arg.__deprecated__ = __new__.__deprecated__ = __msg
- return __arg
- elif callable(__arg):
- @functools.wraps(__arg)
+ arg.__new__ = staticmethod(__new__)
+ arg.__deprecated__ = __new__.__deprecated__ = msg
+ return arg
+ elif callable(arg):
+ @functools.wraps(arg)
def wrapper(*args, **kwargs):
- warnings.warn(__msg, category=category, stacklevel=stacklevel + 1)
- return __arg(*args, **kwargs)
+ warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
+ return arg(*args, **kwargs)
- __arg.__deprecated__ = wrapper.__deprecated__ = __msg
+ arg.__deprecated__ = wrapper.__deprecated__ = msg
return wrapper
else:
raise TypeError(
"@deprecated decorator with non-None category must be applied to "
- f"a class or callable, not {__arg!r}"
+ f"a class or callable, not {arg!r}"
)
return decorator
@@ -2584,7 +2378,7 @@
typing._check_generic = _check_generic
-# Backport typing.NamedTuple as it exists in Python 3.12.
+# Backport typing.NamedTuple as it exists in Python 3.13.
# In 3.11, the ability to define generic `NamedTuple`s was supported.
# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
# On 3.12, we added __orig_bases__ to call-based NamedTuples
@@ -2655,7 +2449,7 @@
return (_NamedTuple,)
@_ensure_subclassable(_namedtuple_mro_entries)
- def NamedTuple(__typename, __fields=_marker, **kwargs):
+ def NamedTuple(typename, fields=_marker, /, **kwargs):
"""Typed version of namedtuple.
Usage::
@@ -2675,7 +2469,7 @@
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
"""
- if __fields is _marker:
+ if fields is _marker:
if kwargs:
deprecated_thing = "Creating NamedTuple classes using keyword arguments"
deprecation_msg = (
@@ -2684,14 +2478,14 @@
)
else:
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
- example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
+ example = f"`{typename} = NamedTuple({typename!r}, [])`"
deprecation_msg = (
"{name} is deprecated and will be disallowed in Python {remove}. "
"To create a NamedTuple class with 0 fields "
"using the functional syntax, "
"pass an empty list, e.g. "
) + example + "."
- elif __fields is None:
+ elif fields is None:
if kwargs:
raise TypeError(
"Cannot pass `None` as the 'fields' parameter "
@@ -2699,7 +2493,7 @@
)
else:
deprecated_thing = "Passing `None` as the 'fields' parameter"
- example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
+ example = f"`{typename} = NamedTuple({typename!r}, [])`"
deprecation_msg = (
"{name} is deprecated and will be disallowed in Python {remove}. "
"To create a NamedTuple class with 0 fields "
@@ -2709,27 +2503,17 @@
elif kwargs:
raise TypeError("Either list of fields or keywords"
" can be provided to NamedTuple, not both")
- if __fields is _marker or __fields is None:
+ if fields is _marker or fields is None:
warnings.warn(
deprecation_msg.format(name=deprecated_thing, remove="3.15"),
DeprecationWarning,
stacklevel=2,
)
- __fields = kwargs.items()
- nt = _make_nmtuple(__typename, __fields, module=_caller())
+ fields = kwargs.items()
+ nt = _make_nmtuple(typename, fields, module=_caller())
nt.__orig_bases__ = (NamedTuple,)
return nt
- # On 3.8+, alter the signature so that it matches typing.NamedTuple.
- # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
- # so just leave the signature as it is on 3.7.
- if sys.version_info >= (3, 8):
- _new_signature = '(typename, fields=None, /, **kwargs)'
- if isinstance(NamedTuple, _types.FunctionType):
- NamedTuple.__text_signature__ = _new_signature
- else:
- NamedTuple.__call__.__text_signature__ = _new_signature
-
if hasattr(collections.abc, "Buffer"):
Buffer = collections.abc.Buffer
@@ -2764,7 +2548,7 @@
if hasattr(_types, "get_original_bases"):
get_original_bases = _types.get_original_bases
else:
- def get_original_bases(__cls):
+ def get_original_bases(cls, /):
"""Return the class's "original" bases prior to modification by `__mro_entries__`.
Examples::
@@ -2786,13 +2570,13 @@
assert get_original_bases(int) == (object,)
"""
try:
- return __cls.__orig_bases__
+ return cls.__orig_bases__
except AttributeError:
try:
- return __cls.__bases__
+ return cls.__bases__
except AttributeError:
raise TypeError(
- f'Expected an instance of type, not {type(__cls).__name__!r}'
+ f'Expected an instance of type, not {type(cls).__name__!r}'
) from None
@@ -2920,13 +2704,13 @@
# Setting this attribute closes the TypeAliasType from further modification
self.__name__ = name
- def __setattr__(self, __name: str, __value: object) -> None:
+ def __setattr__(self, name: str, value: object, /) -> None:
if hasattr(self, "__name__"):
- self._raise_attribute_error(__name)
- super().__setattr__(__name, __value)
+ self._raise_attribute_error(name)
+ super().__setattr__(name, value)
- def __delattr__(self, __name: str) -> Never:
- self._raise_attribute_error(__name)
+ def __delattr__(self, name: str, /) -> Never:
+ self._raise_attribute_error(name)
def _raise_attribute_error(self, name: str) -> Never:
# Match the Python 3.12 error messages exactly
@@ -2987,7 +2771,7 @@
is_protocol = typing.is_protocol
get_protocol_members = typing.get_protocol_members
else:
- def is_protocol(__tp: type) -> bool:
+ def is_protocol(tp: type, /) -> bool:
"""Return True if the given type is a Protocol.
Example::
@@ -3002,13 +2786,13 @@
False
"""
return (
- isinstance(__tp, type)
- and getattr(__tp, '_is_protocol', False)
- and __tp is not Protocol
- and __tp is not getattr(typing, "Protocol", object())
+ isinstance(tp, type)
+ and getattr(tp, '_is_protocol', False)
+ and tp is not Protocol
+ and tp is not typing.Protocol
)
- def get_protocol_members(__tp: type) -> typing.FrozenSet[str]:
+ def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
"""Return the set of members defined in a Protocol.
Example::
@@ -3022,11 +2806,11 @@
Raise a TypeError for arguments that are not Protocols.
"""
- if not is_protocol(__tp):
- raise TypeError(f'{__tp!r} is not a Protocol')
- if hasattr(__tp, '__protocol_attrs__'):
- return frozenset(__tp.__protocol_attrs__)
- return frozenset(_get_protocol_attrs(__tp))
+ if not is_protocol(tp):
+ raise TypeError(f'{tp!r} is not a Protocol')
+ if hasattr(tp, '__protocol_attrs__'):
+ return frozenset(tp.__protocol_attrs__)
+ return frozenset(_get_protocol_attrs(tp))
# Aliases for items that have always been in typing.
diff --git a/tox.ini b/tox.ini
index 3d583ef..5bed022 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
isolated_build = True
-envlist = py37, py38, py39, py310, py311, py312
+envlist = py38, py39, py310, py311, py312
[testenv]
changedir = src