|
5 | 5 | from __future__ import annotations
|
6 | 6 |
|
7 | 7 | import sys
|
| 8 | +from dataclasses import dataclass |
8 | 9 | from typing import ( # type: ignore[attr-defined]
|
9 | 10 | TYPE_CHECKING,
|
10 | 11 | Any,
|
|
53 | 54 | "Untyped",
|
54 | 55 | "Intersection",
|
55 | 56 | "TypeForm",
|
| 57 | + "generic", |
56 | 58 | )
|
57 | 59 |
|
58 | 60 | if TYPE_CHECKING:
|
@@ -508,7 +510,9 @@ def __reduce__(self) -> (object, object):
|
508 | 510 | if sys.version_info > (3, 9):
|
509 | 511 |
|
510 | 512 | @_BasedSpecialForm
|
511 |
| - def Intersection(self: _BasedSpecialForm, parameters: object) -> object: # noqa: N802 |
| 513 | + def Intersection( # noqa: N802 |
| 514 | + self: _BasedSpecialForm, parameters: object |
| 515 | + ) -> object: |
512 | 516 | """Intersection type; Intersection[X, Y] means both X and Y.
|
513 | 517 |
|
514 | 518 | To define an intersection:
|
@@ -574,3 +578,64 @@ def f[T](t: TypeForm[T]) -> T: ...
|
574 | 578 | reveal_type(f(int | str)) # int | str
|
575 | 579 | """
|
576 | 580 | )
|
| 581 | + |
| 582 | + |
| 583 | +@dataclass |
| 584 | +class _BaseGenericFunction(Generic[P, T]): |
| 585 | + fn: Callable[P, T] |
| 586 | + |
| 587 | + |
| 588 | +@dataclass |
| 589 | +class _GenericFunction(_BaseGenericFunction[P, T]): |
| 590 | + # TODO: make this an TypeVarTuple when mypy supports it |
| 591 | + # https://github.com/python/mypy/issues/16696 |
| 592 | + __type_params__: tuple[object, ...] | None = None |
| 593 | + """Generic type parameters. Currently unused""" |
| 594 | + |
| 595 | + def __getitem__(self, items: object) -> _ConcreteFunction[P, T]: |
| 596 | + items = items if isinstance(items, tuple) else (items,) |
| 597 | + return _ConcreteFunction(self.fn, items) |
| 598 | + |
| 599 | + |
| 600 | +@dataclass |
| 601 | +class _ConcreteFunction(_BaseGenericFunction[P, T]): |
| 602 | + __type_args__: tuple[object, ...] | None = None |
| 603 | + """Concrete type parameters. Currently unused""" |
| 604 | + |
| 605 | + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: |
| 606 | + return self.fn(*args, **kwargs) |
| 607 | + |
| 608 | + |
| 609 | +class _GenericFunctionFacilitator: |
| 610 | + __type_params__: tuple[object, ...] | None = None |
| 611 | + args: tuple[object, ...] |
| 612 | + |
| 613 | + def __call__(self, fn: Callable[P, T]) -> _GenericFunction[P, T]: |
| 614 | + return _GenericFunction(fn, self.args) |
| 615 | + |
| 616 | + |
| 617 | +class _GenericFunctionDecorator: |
| 618 | + """Decorate a function to allow supplying type parameters on calls: |
| 619 | +
|
| 620 | + @generic[T] |
| 621 | + def f1(t: T): ... |
| 622 | +
|
| 623 | + f1[int](1) |
| 624 | +
|
| 625 | + @generic |
| 626 | + def f2[T](t: T): ... |
| 627 | +
|
| 628 | + f2[int](1) |
| 629 | + """ |
| 630 | + |
| 631 | + def __call__(self, fn: Callable[P, T]) -> _GenericFunction[P, T]: |
| 632 | + params = cast(Union[Tuple[object, ...], None], getattr(fn, "__type_params__", None)) |
| 633 | + return _GenericFunction(fn, params) |
| 634 | + |
| 635 | + def __getitem__(self, items: object) -> _GenericFunctionFacilitator: |
| 636 | + result = _GenericFunctionFacilitator() |
| 637 | + result.args = items if isinstance(items, tuple) else (items,) |
| 638 | + return result |
| 639 | + |
| 640 | + |
| 641 | +generic = _GenericFunctionDecorator() |
0 commit comments