|
24 | 24 |
|
25 | 25 | if not TYPE_CHECKING: |
26 | 26 | # TODO: remove the TYPE_CHECKING block once these are typed in basedtypeshed |
27 | | - from typing import _tp_cache |
| 27 | + from typing import _GenericAlias, _remove_dups_flatten, _tp_cache, _type_check |
28 | 28 |
|
29 | 29 | if sys.version_info >= (3, 11): |
30 | 30 | from typing import _collect_parameters |
31 | 31 | else: |
32 | 32 | from typing import _collect_type_vars as _collect_parameters |
33 | 33 |
|
| 34 | +if not TYPE_CHECKING: |
| 35 | + |
| 36 | + class _BasedSpecialForm(_SpecialForm, _root=True): |
| 37 | + def __repr__(self): |
| 38 | + return "basedtyping." + self._name |
| 39 | + |
| 40 | + if sys.version_info < (3, 9): |
| 41 | + |
| 42 | + def __getitem__(self, item): |
| 43 | + if self._name == "Intersection": |
| 44 | + return _IntersectionGenericAlias(self, item) |
| 45 | + |
34 | 46 |
|
35 | 47 | if TYPE_CHECKING: |
36 | 48 | Function = Callable[..., object] # type: ignore[no-any-explicit] |
@@ -404,10 +416,84 @@ def Untyped(self: _SpecialForm, parameters: object) -> NoReturn: |
404 | 416 | raise TypeError(f"{self} is not subscriptable") |
405 | 417 |
|
406 | 418 | else: |
407 | | - Untyped: Final = _SpecialForm( |
| 419 | + Untyped: Final = _BasedSpecialForm( |
408 | 420 | "Untyped", |
409 | 421 | doc=( |
410 | 422 | "Special type indicating that something isn't typed.\nThis is more" |
411 | 423 | " specialized than ``Any`` and can help with gradually typing modules." |
412 | 424 | ), |
413 | 425 | ) |
| 426 | + |
| 427 | +if not TYPE_CHECKING: |
| 428 | + |
| 429 | + class _IntersectionGenericAlias(_GenericAlias, _root=True): |
| 430 | + def copy_with(self, params): |
| 431 | + return Intersection[params] |
| 432 | + |
| 433 | + def __eq__(self, other): |
| 434 | + if not isinstance(other, _IntersectionGenericAlias): |
| 435 | + return NotImplemented |
| 436 | + return set(self.__args__) == set(other.__args__) |
| 437 | + |
| 438 | + def __hash__(self): |
| 439 | + return hash(frozenset(self.__args__)) |
| 440 | + |
| 441 | + def __instancecheck__(self, obj): |
| 442 | + return self.__subclasscheck__(type(obj)) |
| 443 | + |
| 444 | + def __subclasscheck__(self, cls): |
| 445 | + for arg in self.__args__: |
| 446 | + if issubclass(cls, arg): |
| 447 | + return True |
| 448 | + |
| 449 | + def __reduce__(self): |
| 450 | + func, (origin, args) = super().__reduce__() |
| 451 | + return func, (Intersection, args) |
| 452 | + |
| 453 | + if sys.version_info > (3, 9): |
| 454 | + |
| 455 | + @_BasedSpecialForm |
| 456 | + def Intersection(self, parameters): |
| 457 | + """Intersection type; Intersection[X, Y] means both X and Y. |
| 458 | +
|
| 459 | + To define an intersection: |
| 460 | + - If using __future__.annotations, shortform can be used e.g. A & B |
| 461 | + - otherwise the fullform must be used e.g. Intersection[A, B]. |
| 462 | +
|
| 463 | + Details: |
| 464 | + - The arguments must be types and there must be at least one. |
| 465 | + - None as an argument is a special case and is replaced by |
| 466 | + type(None). |
| 467 | + - Intersections of intersections are flattened, e.g.:: |
| 468 | +
|
| 469 | + Intersection[Intersection[int, str], float] == Intersection[int, str, float] |
| 470 | +
|
| 471 | + - Intersections of a single argument vanish, e.g.:: |
| 472 | +
|
| 473 | + Intersection[int] == int # The constructor actually returns int |
| 474 | +
|
| 475 | + - Redundant arguments are skipped, e.g.:: |
| 476 | +
|
| 477 | + Intersection[int, str, int] == Intersection[int, str] |
| 478 | +
|
| 479 | + - When comparing intersections, the argument order is ignored, e.g.:: |
| 480 | +
|
| 481 | + Intersection[int, str] == Intersection[str, int] |
| 482 | +
|
| 483 | + - You cannot subclass or instantiate an intersection. |
| 484 | + """ |
| 485 | + if parameters == (): |
| 486 | + raise TypeError("Cannot take an Intersection of no types.") |
| 487 | + if not isinstance(parameters, tuple): |
| 488 | + parameters = (parameters,) |
| 489 | + msg = "Intersection[arg, ...]: each arg must be a type." |
| 490 | + parameters = tuple(_type_check(p, msg) for p in parameters) |
| 491 | + parameters = _remove_dups_flatten(parameters) |
| 492 | + if len(parameters) == 1: |
| 493 | + return parameters[0] |
| 494 | + return _IntersectionGenericAlias(self, parameters) |
| 495 | + |
| 496 | + else: |
| 497 | + Intersection = _BasedSpecialForm("Intersection", doc="") |
| 498 | +else: |
| 499 | + Intersection: _SpecialForm |
0 commit comments