New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ForwardRef.__eq__ does not respect module parameter #90491
Comments
The __eq__ method of ForwardRef does not take into account the module parameter. However, ForwardRefs with dissimilar module parameters are referring to different types even if they have different name. Thus also the ForwardRef's with same name but different module, should not be considered equal. Consider the following code from typing import *
ZZ = Optional['YY']
YY = int
YY = Tuple[Optional[ForwardRef("YY", module=__name__)], int]
print( YY.__args__[0].__args__[0].__forward_module__ )
# this prints None, but should print __main__ (or whatever __name__ contains) When the first ForwardRef is not created, the program behaves correctly #ZZ = Optional['YY']
YY = int
YY = Tuple[Optional[ForwardRef("YY", module=__name__)], int]
print( YY.__args__[0].__args__[0].__forward_module__ )
# this prints __main__ (or whatever __name__ contains) The issue is that the line This problem could be fixed if the __eq__ method of FowardRef also checks for module equality. i.e. in ForwardRef.__eq__ in typing.py replace return self.__forward_arg__ == other.__forward_arg__ with return self.__forward_arg__ == other.__forward_arg__ and self.__forward__module__ == other.__forward__module__ Ideally, (and for consistency reasons) the Change: def __repr__(self):
return f'ForwardRef({self.__forward_arg__!r})' to def __repr__(self):
if self.__forward_module__ is None:
return f'ForwardRef({self.__forward_arg__!r})'
else:
return f'ForwardRef({self.__forward_arg__!r}, module={self.__forward__module!r})' |
Good catch. Do you want to submit a PR? I agree that the repr() could also be better (but please only show other values if they differ from the default). |
I will give it a try. |
@andreas ForwardRef isn't meant to be explicitly instantiated by a user 1 (it's a typing internal class), so do you mind sharing what your current use case is please? My concern here is that exposing things in repr would force us to keep |
Yeah, sure. The use-case is (de)serialization. Right now I use the library cattr, but there are many others. If you are interested there is related discussion in the cattr board 1. The original problem is how to define the types for serialization.
Json = Union[ List['Json'], Dict[str, 'Json'], int, float, bool, None ] (Note: even though mypy does not support this construct, pylance meanwhile does 2)
The problem is that the implicit When one wants to separate interface from implementation this issue is even more complicated. The module where the serialization function is called is typically different from the module with the type definition (This is probably more the norm than the exception) The option I expored is to explicitly create the ForwardRef and specify the module parameter (even though I have to admit that I also did read that the ForwardRef is only for internal use).
Ugly, but this is better than nothing. A (worse) alternative is to do Json = Union[ List['Json'], Dict[str, 'Json'], int, float, bool, None ]
typing._eval_type(Json, globals(), locals()) That works since it puts the ForwardRefs into "evaluated" state (self.__forward_value__ is then set) A third, but future, alternative could be to automatically decompose the argument of ForwardRef into module and type. Then one could write Json = Union[ List[__name__+'.Json'], Dict[str, __name__+'.Json'], int, float, bool, None ] and all above problems would be solved. Then ForwardRef could stay internal and this is more readable. Even though, the static type checkers would need to understand Anyhow, it would be nice to have a solution here. |
Ah, let me add one point: PEP-563 (-> Even with PEP-563 enabled, the JSON example Json = Union[ List['Json'], Dict[str, 'Json'], int, float, bool, None ] needs to be written in exact the same way as without PEP-563. In other words there are cases where |
module
parameter in ForwardRef #30536module
inForwardRef.__repr__
#31283module
parameter in ForwardRef (GH-30536) #31379module
parameter in ForwardRef (GH-30536) #31380Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: