bpo-43783: Add ParamSpecArgs/Kwargs (GH-25298)

This commit is contained in:
Jelle Zijlstra 2021-04-10 19:57:05 -07:00 committed by GitHub
parent 750f484752
commit 522433601a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 11 deletions

View File

@ -1058,8 +1058,10 @@ These are not used in annotations. They are building blocks for creating generic
components. ``P.args`` represents the tuple of positional parameters in a
given call and should only be used to annotate ``*args``. ``P.kwargs``
represents the mapping of keyword parameters to their values in a given call,
and should be only be used to annotate ``**kwargs`` or ``**kwds``. Both
attributes require the annotated parameter to be in scope.
and should be only be used to annotate ``**kwargs``. Both
attributes require the annotated parameter to be in scope. At runtime,
``P.args`` and ``P.kwargs`` are instances respectively of
:class:`ParamSpecArgs` and :class:`ParamSpecKwargs`.
Parameter specification variables created with ``covariant=True`` or
``contravariant=True`` can be used to declare covariant or contravariant
@ -1078,6 +1080,24 @@ These are not used in annotations. They are building blocks for creating generic
``ParamSpec`` and ``Concatenate``).
* :class:`Callable` and :class:`Concatenate`.
.. data:: ParamSpecArgs
.. data:: ParamSpecKwargs
Arguments and keyword arguments attributes of a :class:`ParamSpec`. The
``P.args`` attribute of a ``ParamSpec`` is an instance of ``ParamSpecArgs``,
and ``P.kwargs`` is an instance of ``ParamSpecKwargs``. They are intended
for runtime introspection and have no special meaning to static type checkers.
Calling :func:`get_origin` on either of these objects will return the
original ``ParamSpec``::
P = ParamSpec("P")
get_origin(P.args) # returns P
get_origin(P.kwargs) # returns P
.. versionadded:: 3.10
.. data:: AnyStr
``AnyStr`` is a type variable defined as

View File

@ -550,9 +550,11 @@ which adds or removes parameters of another callable. Examples of usage can
be found in :class:`typing.Concatenate`.
See :class:`typing.Callable`, :class:`typing.ParamSpec`,
:class:`typing.Concatenate` and :pep:`612` for more details.
:class:`typing.Concatenate`, :class:`typing.ParamSpecArgs`,
:class:`typing.ParamSpecKwargs`, and :pep:`612` for more details.
(Contributed by Ken Jin in :issue:`41559`.)
(Contributed by Ken Jin in :issue:`41559`, with minor enhancements by Jelle
Zijlstra in :issue:`43783`. PEP written by Mark Mendoza.)
PEP 613: TypeAlias Annotation

View File

@ -25,7 +25,7 @@ from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
from typing import Annotated, ForwardRef
from typing import TypeAlias
from typing import ParamSpec, Concatenate
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
import abc
import typing
import weakref
@ -3004,6 +3004,7 @@ class GetTypeHintTests(BaseTestCase):
class GetUtilitiesTestCase(TestCase):
def test_get_origin(self):
T = TypeVar('T')
P = ParamSpec('P')
class C(Generic[T]): pass
self.assertIs(get_origin(C[int]), C)
self.assertIs(get_origin(C[T]), C)
@ -3022,6 +3023,8 @@ class GetUtilitiesTestCase(TestCase):
self.assertIs(get_origin(list[int]), list)
self.assertIs(get_origin(list), None)
self.assertIs(get_origin(list | str), types.Union)
self.assertIs(get_origin(P.args), P)
self.assertIs(get_origin(P.kwargs), P)
def test_get_args(self):
T = TypeVar('T')
@ -4265,11 +4268,16 @@ class ParamSpecTests(BaseTestCase):
self.assertEqual(C4.__args__, (P, T))
self.assertEqual(C4.__parameters__, (P, T))
# ParamSpec instances should also have args and kwargs attributes.
def test_args_kwargs(self):
P = ParamSpec('P')
self.assertIn('args', dir(P))
self.assertIn('kwargs', dir(P))
P.args
P.kwargs
self.assertIsInstance(P.args, ParamSpecArgs)
self.assertIsInstance(P.kwargs, ParamSpecKwargs)
self.assertIs(P.args.__origin__, P)
self.assertIs(P.kwargs.__origin__, P)
self.assertEqual(repr(P.args), "P.args")
self.assertEqual(repr(P.kwargs), "P.kwargs")
def test_user_generics(self):
T = TypeVar("T")

View File

@ -114,6 +114,8 @@ __all__ = [
'no_type_check_decorator',
'NoReturn',
'overload',
'ParamSpecArgs',
'ParamSpecKwargs',
'runtime_checkable',
'Text',
'TYPE_CHECKING',
@ -727,6 +729,44 @@ class TypeVar( _Final, _Immutable, _TypeVarLike, _root=True):
self.__module__ = def_mod
class ParamSpecArgs(_Final, _Immutable, _root=True):
"""The args for a ParamSpec object.
Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
ParamSpecArgs objects have a reference back to their ParamSpec:
P.args.__origin__ is P
This type is meant for runtime introspection and has no special meaning to
static type checkers.
"""
def __init__(self, origin):
self.__origin__ = origin
def __repr__(self):
return f"{self.__origin__.__name__}.args"
class ParamSpecKwargs(_Final, _Immutable, _root=True):
"""The kwargs for a ParamSpec object.
Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
ParamSpecKwargs objects have a reference back to their ParamSpec:
P.kwargs.__origin__ is P
This type is meant for runtime introspection and has no special meaning to
static type checkers.
"""
def __init__(self, origin):
self.__origin__ = origin
def __repr__(self):
return f"{self.__origin__.__name__}.kwargs"
class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
"""Parameter specification variable.
@ -776,8 +816,13 @@ class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
__slots__ = ('__name__', '__bound__', '__covariant__', '__contravariant__',
'__dict__')
args = object()
kwargs = object()
@property
def args(self):
return ParamSpecArgs(self)
@property
def kwargs(self):
return ParamSpecKwargs(self)
def __init__(self, name, *, bound=None, covariant=False, contravariant=False):
self.__name__ = name
@ -1662,10 +1707,12 @@ def get_origin(tp):
get_origin(Generic[T]) is Generic
get_origin(Union[T, int]) is Union
get_origin(List[Tuple[T, T]][int]) == list
get_origin(P.args) is P
"""
if isinstance(tp, _AnnotatedAlias):
return Annotated
if isinstance(tp, (_BaseGenericAlias, GenericAlias)):
if isinstance(tp, (_BaseGenericAlias, GenericAlias,
ParamSpecArgs, ParamSpecKwargs)):
return tp.__origin__
if tp is Generic:
return Generic

View File

@ -0,0 +1,3 @@
The ``P.args`` and ``P.kwargs`` attributes of :class:`typing.ParamSpec` are
now instances of the new classes :class:`typing.ParamSpecArgs` and
:class:`typing.ParamSpecKwargs`, which enables a more useful ``repr()``. Patch by Jelle Zijlstra.