API Reference
Reuse generic class type arguments at runtime.
This library provides a decorator that allows you to mark a class as
a 'runtime generic': after instantiation, the class will have a __args__
attribute
that contains the type arguments of the instance.
Examples:
from __future__ import annotations
import io
from typing import TYPE_CHECKING, Generic, TypeVar
from runtime_generics import get_alias, get_type_arguments, runtime_generic, type_check
if TYPE_CHECKING:
from typing import IO, Literal, overload
T = TypeVar("T", str, bytes)
@runtime_generic
class IOWrapper(Generic[T]):
data_type: type[T]
def __init__(self, stream: IO[T]) -> None:
(self.data_type,) = get_type_arguments(self)
self.stream = stream
if TYPE_CHECKING:
@overload
def is_binary(self: IOWrapper[bytes]) -> Literal[True]: ...
@overload
def is_binary(self: IOWrapper[str]) -> Literal[False]: ...
def is_binary(self) -> bool:
# alternatively here: `self.data_type == bytes`
return type_check(self, IOWrapper[bytes])
def __repr__(self) -> str:
return f"<{get_alias(self)} object at ...>"
my_binary_data = IOWrapper[bytes](io.BytesIO(b"foo"))
assert my_binary_data.data_type is bytes
assert my_binary_data.is_binary()
assert repr(IOWrapper[str](io.StringIO())) == "<__main__.IOWrapper[str] object at ...>"
Classes:
-
GenericArgs
–Marker class for type arguments of runtime generics.
Functions:
-
get_parametrization
–Map type parameters to type arguments in a generic alias.
-
get_parents
–Get all parametrized parents of a runtime generic class or instance.
-
get_mro
–Get all parametrized parents of a runtime generic using the C3 algorithm.
-
get_alias
–For any runtime generic (class, instance), find the most relevant generic alias.
-
runtime_generic_patch
–Patch
objects
to support runtime generics. -
no_alias
–Mark a classmethod as not being passed a generic alias in place of cls.
-
get_type_arguments
–Get all type arguments of a runtime generic.
-
runtime_generic_proxy
–Create a runtime generic descriptor with a result type.
-
runtime_generic_init
–Initialize a runtime generic instance.
-
runtime_generic
–Mark a class as a runtime generic.
-
type_check
–Examine whether a runtime generic is a valid subtype of another runtime generic.
get_parametrization(runtime_generic)
¶
Map type parameters to type arguments in a generic alias.
Source code in runtime_generics/__init__.py
345 346 347 348 349 350 |
|
get_parents(cls)
¶
Get all parametrized parents of a runtime generic class or instance.
Source code in runtime_generics/__init__.py
379 380 381 |
|
get_mro(cls)
¶
Get all parametrized parents of a runtime generic using the C3 algorithm.
Source code in runtime_generics/__init__.py
420 421 422 |
|
get_alias(rg)
¶
For any runtime generic (class, instance), find the most relevant generic alias.
Parameters:
Examples:
>>> from typing import Generic, TypeVar
>>> T = TypeVar("T")
...
>>> @runtime_generic
... class Foo(Generic[T]):
... pass
...
>>> get_alias(Foo)
runtime_generics.Foo[typing.Any]
>>> get_alias(Foo())
runtime_generics.Foo[typing.Any]
>>> get_alias(Foo[int])
runtime_generics.Foo[int]
>>> get_alias(Foo[int]())
runtime_generics.Foo[int]
Source code in runtime_generics/__init__.py
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
|
runtime_generic_patch(*objects, stack_offset=2)
¶
Patch objects
to support runtime generics.
Source code in runtime_generics/__init__.py
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
|
no_alias(cls_method)
¶
Mark a classmethod as not being passed a generic alias in place of cls.
Source code in runtime_generics/__init__.py
548 549 550 551 |
|
get_type_arguments(rg)
¶
Get all type arguments of a runtime generic.
Parameters:
Returns:
-
args
–The type arguments of the examined runtime generic.
Examples:
>>> from typing import Generic, TypeVar
>>> T = TypeVar("T")
...
>>> @runtime_generic
... class Foo(Generic[T]):
... pass
>>> args: tuple[type[int]] = get_type_arguments(Foo[int]())
>>> args
(<class 'int'>,)
Source code in runtime_generics/__init__.py
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
|
runtime_generic_proxy(result_type)
¶
Create a runtime generic descriptor with a result type.
Source code in runtime_generics/__init__.py
588 589 590 591 592 593 594 595 596 |
|
runtime_generic_init(self, args, origin)
¶
Initialize a runtime generic instance.
Source code in runtime_generics/__init__.py
599 600 601 602 603 604 605 606 |
|
runtime_generic(cls, result_type=None)
¶
Mark a class as a runtime generic.
This is a class decorator that dynamically adds a __class_getitem__
descriptor
to the class. This method returns a callable that takes type arguments and returns
a new instance of the class with the __args__
attribute set to the type arguments.
Examples:
>>> from typing import Generic, TypeVar
>>> T = TypeVar("T")
...
>>> @runtime_generic
... class Foo(Generic[T]):
... pass
...
>>> Foo[int]().__args__
(<class 'int'>,)
Source code in runtime_generics/__init__.py
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
|
type_check(subtype, cls)
¶
Examine whether a runtime generic is a valid subtype of another runtime generic.
Variance is supported. TypeVar bounds are not yet supported.
Parameters:
Examples:
>>> from typing import Any, Dict, Generic, TypeVar
...
>>> T = TypeVar("T")
>>> T_co = TypeVar("T_co", covariant=True)
>>> T_contra = TypeVar("T_contra", contravariant=True)
...
>>> type_check(Dict[str, int], Dict[str, bool]) # KT, VT - invariant
False
>>> @runtime_generic
... class Foo(Generic[T, T_co, T_contra]):
... pass
...
>>> @runtime_generic
... class Bar(Generic[T_contra, T_co, T], Foo[T, T_co, T_contra]):
... pass
...
>>> type_check(Foo[int, int, int], Foo[int, int, int])
True
>>> type_check(Foo[int, bool, int], Foo[int, int, int])
True
>>> type_check(Foo[int, int, int], Foo[int, int, bool])
True
>>> type_check(Foo[int, int, int], Foo[int, bool, int])
False
>>> type_check(Foo[int, int, bool], Foo[int, int, int])
False
>>> type_check(Bar[int, int, int], Foo[int, int, bool])
True
>>> type_check(Bar[bool, int, int], Foo[int, int, int])
False
>>> type_check(Bar[int, bool, int], Foo[int, int, int])
True
Returns:
-
bool
–Whether
subtype
is a valid subtype ofcls
.
Source code in runtime_generics/__init__.py
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 |
|