gh-118647: Add defaults to typing.Generator and typing.AsyncGenerator (#118648)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
This commit is contained in:
parent
9fd33af5ac
commit
8419f01673
@ -3648,8 +3648,14 @@ Aliases to asynchronous ABCs in :mod:`collections.abc`
|
|||||||
is no ``ReturnType`` type parameter. As with :class:`Generator`, the
|
is no ``ReturnType`` type parameter. As with :class:`Generator`, the
|
||||||
``SendType`` behaves contravariantly.
|
``SendType`` behaves contravariantly.
|
||||||
|
|
||||||
If your generator will only yield values, set the ``SendType`` to
|
The ``SendType`` defaults to :const:`!None`::
|
||||||
``None``::
|
|
||||||
|
async def infinite_stream(start: int) -> AsyncGenerator[int]:
|
||||||
|
while True:
|
||||||
|
yield start
|
||||||
|
start = await increment(start)
|
||||||
|
|
||||||
|
It is also possible to set this type explicitly::
|
||||||
|
|
||||||
async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
|
async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
|
||||||
while True:
|
while True:
|
||||||
@ -3671,6 +3677,9 @@ Aliases to asynchronous ABCs in :mod:`collections.abc`
|
|||||||
now supports subscripting (``[]``).
|
now supports subscripting (``[]``).
|
||||||
See :pep:`585` and :ref:`types-genericalias`.
|
See :pep:`585` and :ref:`types-genericalias`.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.13
|
||||||
|
The ``SendType`` parameter now has a default.
|
||||||
|
|
||||||
.. class:: AsyncIterable(Generic[T_co])
|
.. class:: AsyncIterable(Generic[T_co])
|
||||||
|
|
||||||
Deprecated alias to :class:`collections.abc.AsyncIterable`.
|
Deprecated alias to :class:`collections.abc.AsyncIterable`.
|
||||||
@ -3754,8 +3763,14 @@ Aliases to other ABCs in :mod:`collections.abc`
|
|||||||
of :class:`Generator` behaves contravariantly, not covariantly or
|
of :class:`Generator` behaves contravariantly, not covariantly or
|
||||||
invariantly.
|
invariantly.
|
||||||
|
|
||||||
If your generator will only yield values, set the ``SendType`` and
|
The ``SendType`` and ``ReturnType`` parameters default to :const:`!None`::
|
||||||
``ReturnType`` to ``None``::
|
|
||||||
|
def infinite_stream(start: int) -> Generator[int]:
|
||||||
|
while True:
|
||||||
|
yield start
|
||||||
|
start += 1
|
||||||
|
|
||||||
|
It is also possible to set these types explicitly::
|
||||||
|
|
||||||
def infinite_stream(start: int) -> Generator[int, None, None]:
|
def infinite_stream(start: int) -> Generator[int, None, None]:
|
||||||
while True:
|
while True:
|
||||||
@ -3774,6 +3789,9 @@ Aliases to other ABCs in :mod:`collections.abc`
|
|||||||
:class:`collections.abc.Generator` now supports subscripting (``[]``).
|
:class:`collections.abc.Generator` now supports subscripting (``[]``).
|
||||||
See :pep:`585` and :ref:`types-genericalias`.
|
See :pep:`585` and :ref:`types-genericalias`.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.13
|
||||||
|
Default values for the send and return types were added.
|
||||||
|
|
||||||
.. class:: Hashable
|
.. class:: Hashable
|
||||||
|
|
||||||
Deprecated alias to :class:`collections.abc.Hashable`.
|
Deprecated alias to :class:`collections.abc.Hashable`.
|
||||||
|
@ -7289,6 +7289,17 @@ class CollectionsAbcTests(BaseTestCase):
|
|||||||
g = foo()
|
g = foo()
|
||||||
self.assertIsSubclass(type(g), typing.Generator)
|
self.assertIsSubclass(type(g), typing.Generator)
|
||||||
|
|
||||||
|
def test_generator_default(self):
|
||||||
|
g1 = typing.Generator[int]
|
||||||
|
g2 = typing.Generator[int, None, None]
|
||||||
|
self.assertEqual(get_args(g1), (int, type(None), type(None)))
|
||||||
|
self.assertEqual(get_args(g1), get_args(g2))
|
||||||
|
|
||||||
|
g3 = typing.Generator[int, float]
|
||||||
|
g4 = typing.Generator[int, float, None]
|
||||||
|
self.assertEqual(get_args(g3), (int, float, type(None)))
|
||||||
|
self.assertEqual(get_args(g3), get_args(g4))
|
||||||
|
|
||||||
def test_no_generator_instantiation(self):
|
def test_no_generator_instantiation(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
typing.Generator()
|
typing.Generator()
|
||||||
|
@ -1328,7 +1328,7 @@ class _BaseGenericAlias(_Final, _root=True):
|
|||||||
raise AttributeError(attr)
|
raise AttributeError(attr)
|
||||||
|
|
||||||
def __setattr__(self, attr, val):
|
def __setattr__(self, attr, val):
|
||||||
if _is_dunder(attr) or attr in {'_name', '_inst', '_nparams'}:
|
if _is_dunder(attr) or attr in {'_name', '_inst', '_nparams', '_defaults'}:
|
||||||
super().__setattr__(attr, val)
|
super().__setattr__(attr, val)
|
||||||
else:
|
else:
|
||||||
setattr(self.__origin__, attr, val)
|
setattr(self.__origin__, attr, val)
|
||||||
@ -1578,11 +1578,12 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
|||||||
# parameters are accepted (needs custom __getitem__).
|
# parameters are accepted (needs custom __getitem__).
|
||||||
|
|
||||||
class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True):
|
class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True):
|
||||||
def __init__(self, origin, nparams, *, inst=True, name=None):
|
def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()):
|
||||||
if name is None:
|
if name is None:
|
||||||
name = origin.__name__
|
name = origin.__name__
|
||||||
super().__init__(origin, inst=inst, name=name)
|
super().__init__(origin, inst=inst, name=name)
|
||||||
self._nparams = nparams
|
self._nparams = nparams
|
||||||
|
self._defaults = defaults
|
||||||
if origin.__module__ == 'builtins':
|
if origin.__module__ == 'builtins':
|
||||||
self.__doc__ = f'A generic version of {origin.__qualname__}.'
|
self.__doc__ = f'A generic version of {origin.__qualname__}.'
|
||||||
else:
|
else:
|
||||||
@ -1594,12 +1595,22 @@ class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True):
|
|||||||
params = (params,)
|
params = (params,)
|
||||||
msg = "Parameters to generic types must be types."
|
msg = "Parameters to generic types must be types."
|
||||||
params = tuple(_type_check(p, msg) for p in params)
|
params = tuple(_type_check(p, msg) for p in params)
|
||||||
|
if (self._defaults
|
||||||
|
and len(params) < self._nparams
|
||||||
|
and len(params) + len(self._defaults) >= self._nparams
|
||||||
|
):
|
||||||
|
params = (*params, *self._defaults[len(params) - self._nparams:])
|
||||||
actual_len = len(params)
|
actual_len = len(params)
|
||||||
|
|
||||||
if actual_len != self._nparams:
|
if actual_len != self._nparams:
|
||||||
|
if self._defaults:
|
||||||
|
expected = f"at least {self._nparams - len(self._defaults)}"
|
||||||
|
else:
|
||||||
|
expected = str(self._nparams)
|
||||||
if not self._nparams:
|
if not self._nparams:
|
||||||
raise TypeError(f"{self} is not a generic class")
|
raise TypeError(f"{self} is not a generic class")
|
||||||
raise TypeError(f"Too {'many' if actual_len > self._nparams else 'few'} arguments for {self};"
|
raise TypeError(f"Too {'many' if actual_len > self._nparams else 'few'} arguments for {self};"
|
||||||
f" actual {actual_len}, expected {self._nparams}")
|
f" actual {actual_len}, expected {expected}")
|
||||||
return self.copy_with(params)
|
return self.copy_with(params)
|
||||||
|
|
||||||
def copy_with(self, params):
|
def copy_with(self, params):
|
||||||
@ -2813,8 +2824,8 @@ DefaultDict = _alias(collections.defaultdict, 2, name='DefaultDict')
|
|||||||
OrderedDict = _alias(collections.OrderedDict, 2)
|
OrderedDict = _alias(collections.OrderedDict, 2)
|
||||||
Counter = _alias(collections.Counter, 1)
|
Counter = _alias(collections.Counter, 1)
|
||||||
ChainMap = _alias(collections.ChainMap, 2)
|
ChainMap = _alias(collections.ChainMap, 2)
|
||||||
Generator = _alias(collections.abc.Generator, 3)
|
Generator = _alias(collections.abc.Generator, 3, defaults=(types.NoneType, types.NoneType))
|
||||||
AsyncGenerator = _alias(collections.abc.AsyncGenerator, 2)
|
AsyncGenerator = _alias(collections.abc.AsyncGenerator, 2, defaults=(types.NoneType,))
|
||||||
Type = _alias(type, 1, inst=False, name='Type')
|
Type = _alias(type, 1, inst=False, name='Type')
|
||||||
Type.__doc__ = \
|
Type.__doc__ = \
|
||||||
"""Deprecated alias to builtins.type.
|
"""Deprecated alias to builtins.type.
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
Add type parameter defaults to :class:`typing.Generator` and
|
||||||
|
:class:`typing.AsyncGenerator`.
|
Loading…
x
Reference in New Issue
Block a user