You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add typing.get_orig_bases and typing.get_orig_class #101688
(side note, it might be possible to fully type get_orig_class it types.GenericAlias was generic over the __origin__ and __args__ i.e. Foo[int] == GenericAlias[Foo, int])
I'd be happy to accept this enhancement to typing.py. Users have previously requested this functionality in microsoft/pyright#3442 and python/typeshed#7811, but it's not really fixable by pyright or typeshed. The solution is to add the APIs suggested here to typing.py in CPython.
No — type checkers emit errors if you try to access them, as they're not included in the stub. As discussed in python/typeshed#7811 and python/typeshed#7827, adding them to the stub would in fact be impossible.
The main issue with defining those dunders in a stub file is they are defined for generic types but not other types. And Generic class is very special cased by type checkers/runtime that you can’t add it to the stub definition for Generic without risking type checker either ignoring it or causing buggy behavior.
Would be nice to have a public recommended way to access these for runtime type reflection. Simple implementation seems fine to me.
However, it only exists on instances of classes that don't have slots.
>>> d1 = dict[int, str]()
>>> d1.__orig_class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute '__orig_class__'
The inconsistency in whether this attribute is available or not might be quite confusing for users, so I'm not sure I like the idea of documenting the attribute or adding a getter function. It also doesn't appear to have been as much of a pain point for users as __orig_bases__ has been. @Gobot1234, could you elaborate more on your use case for accessing the __orig_class__ attribute?
(2) Should we document __orig_bases__ in the data model? Or is having types.get_original_bases() enough?
If an object that is not a class object appears in the tuple of bases of a class definition, then method __mro_entries__ is searched on it [...] The original bases are stored as orig_bases in the class namespace [...] NOTE: These two method names are reserved for use by the typing module and the generic types machinery, and any other use is discouraged.
So for now I think documenting __orig_class__ isn't probably worth it as as you pointed out, it's only conditionally available (I'm currently drafting up a PEP for better runtime type checking features and it will probably become public API following that if it's accepted or submitted ;)) but I currently use it for getting the specialised value of a type parameter at runtime.
Currently in a module I maintain there is an Inventory class which is generic over the types of items it stores and I want to get that at runtime so that I can instantiate the correct item class.
This either goes through subscription e.g. Inventory[SomeItemClass]() or class InventorySubclass(Inventory[AlwaysTheSameItem]): ... the first of which requires checking __orig_class__ the other runs through __orig_bases__ hence why I made this issue (full code here https://github.com/Gobot1234/steam.py/blob/82e74825d55db905dcb7414fa3f9313b67e31aba/steam/trade.py#L337-L340 if you're interested)
Okay, great. I'll close this as completed for now, in that case, as I think adding a new slot for all Python objects will require a lot more discussion, so deserves to be treated separately :)
Activity
AlexWaygood commentedon Feb 8, 2023
I'd be happy to accept this enhancement to
typing.py. Users have previously requested this functionality in microsoft/pyright#3442 and python/typeshed#7811, but it's not really fixable by pyright or typeshed. The solution is to add the APIs suggested here totyping.pyin CPython.sobolevn commentedon Feb 8, 2023
Aren't
__orig_bases__and__orig_class__enough?Gobot1234 commentedon Feb 8, 2023
They aren't documented anywhere AFAIK, that would also be a decent solution to the problem
AlexWaygood commentedon Feb 8, 2023
No — type checkers emit errors if you try to access them, as they're not included in the stub. As discussed in python/typeshed#7811 and python/typeshed#7827, adding them to the stub would in fact be impossible.
hmc-cs-mdrissi commentedon Feb 8, 2023
The main issue with defining those dunders in a stub file is they are defined for generic types but not other types. And Generic class is very special cased by type checkers/runtime that you can’t add it to the stub definition for Generic without risking type checker either ignoring it or causing buggy behavior.
Would be nice to have a public recommended way to access these for runtime type reflection. Simple implementation seems fine to me.
gh-101688: Implement types.get_original_bases (#101827)
AlexWaygood commentedon Apr 23, 2023
#101827 has now been merged:
types.get_original_baseswill be available for retrieving a class's__orig_bases__attribute in 3.12+.Remaining issues to discuss
(1) Should we add a "getter function" for the
__orig_class__attribute? If not, should we document its existence?As a refresher, these are the circumstances in which an object gets an
__orig_class__attribute:However, it only exists on instances of classes that don't have slots.
The inconsistency in whether this attribute is available or not might be quite confusing for users, so I'm not sure I like the idea of documenting the attribute or adding a getter function. It also doesn't appear to have been as much of a pain point for users as
__orig_bases__has been. @Gobot1234, could you elaborate more on your use case for accessing the__orig_class__attribute?(2) Should we document
__orig_bases__in the data model? Or is havingtypes.get_original_bases()enough?We could possibly document the attribute formally in the data model here: https://docs.python.org/3.12/reference/datamodel.html#resolving-mro-entries. However, doing so would be equivalent to a formal promise that the attribute will always exist as part of the language. I'm not sure I'm convinced that it's necessary for us to do that. PEP 560 describes the attribute as an implementation detail of the typing module, and it is not currently formally documented anywhere:
Gobot1234 commentedon Apr 23, 2023
So for now I think documenting
__orig_class__isn't probably worth it as as you pointed out, it's only conditionally available (I'm currently drafting up a PEP for better runtime type checking features and it will probably become public API following that if it's accepted or submitted ;)) but I currently use it for getting the specialised value of a type parameter at runtime.Currently in a module I maintain there is an
Inventoryclass which is generic over the types of items it stores and I want to get that at runtime so that I can instantiate the correct item class.This either goes through subscription e.g.
Inventory[SomeItemClass]()orclass InventorySubclass(Inventory[AlwaysTheSameItem]): ...the first of which requires checking__orig_class__the other runs through__orig_bases__hence why I made this issue (full code here https://github.com/Gobot1234/steam.py/blob/82e74825d55db905dcb7414fa3f9313b67e31aba/steam/trade.py#L337-L340 if you're interested)AlexWaygood commentedon Apr 24, 2023
Okay, great. I'll close this as completed for now, in that case, as I think adding a new slot for all Python objects will require a lot more discussion, so deserves to be treated separately :)