mirror of https://github.com/python/cpython
bpo-43783: Add ParamSpecArgs/Kwargs (GH-25298)
This commit is contained in:
parent
750f484752
commit
522433601a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue