-
-
Notifications
You must be signed in to change notification settings - Fork 113
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 and GenConverter do not seem to work together #201
Comments
Just FYI you don't need |
I was bitten by this lately too. Wrap the entire type in quotes, instead of just
It has to do with how |
Okay, I see. I confirm, this works. However now I try to get this one-line recursive type
to work. Unfortunately, this generates a "NameError" even if PEP563 is enabled (I know this is not a cattrs issue. perhaps this is not supported by PEP563). But with PEP563 disabled, putting quotes around it completely
gives a
Also putting the quote somewhere in between, or around "YY" does not avoid the exception.
Some background: I am writing a code generator which generates type-hinted code automatically. So I need a strategy to generate the type-hints which always works. I am not really sure what the correct way to specify type "YY" is, so I cannot really tell if there is a cattr issue here or not. However, https://stackoverflow.com/a/53845083/1739884 seems to indicate that
should work. |
That's an interesting problem. I don't think PEP 563 is in play here directly, that PEP deals with annotations (think a type of a class field or a function argument), you're dealing with raw types. For example, if you call The I think in order to support this use case cattrs would need to be able to handle ForwardRefs natively. Then you'd write:
I'm not exactly sure if it's possible though. In order to resolve the forward reference, we'd need to get a reference to the Note that you can hack it yourself with:
but you'll essentially create a recursive loop, and you won't be able to even print out the type. Also cattrs won't be able to handle it since it can't hash it, due to the circular reference. |
My preliminary conclusions after I thought about this issue:
After thinking about this, probably the best solution is to take the cattr suggestion in the exception literally and to register a custom hook for each ForwardRef that is used. The Example looks like
Observations:
Do you agree to this? |
I'm looking at the source code of I see ForwardRefs can be evaluated manually before use by calling |
I am not sure if we could really evaluate the ForwardRefs, as it would probably trigger the hashing issue with recursive types. So I guess we need to keep the ForwardRefs unevaluated. Actually we have two issues, one which I did not think about when I wrote my last comment. There may be two types of same name (say YY) in two modules. Now depending in which module the ForwardRef is created, it refers to different types. This is clear, but when we cannot evaluate the Refs we need to keep the text and find another way to resolve the ambiguity. And registering hooks for the ForwardRef may generate clashes, as same object may semantically refer to different types. I also have an idea how to automatically add the module Information to forwardrefs but that I need to try out first. I will report in the next days. |
@aha79 I think we can evaluate ForwardRefs without causing the recursive issue. An example:
|
@Tinche I think this is a workable solution. Sidenote: You do not need to explicitly construct ForwardRef, this is done automatically :-). Just write However if we accept explicitly calling ForwardRef, we could also pass the module parameter (e.g. Then we can pass
Evaluation could be done inside of However, there two minor drawbacks.
Nevertheless I think it would be a great thing to support ForwardRefs in catts. |
This seems to do the job
The following tests assume the previous code is in different module
The same results are obtained if |
I just found a better way: In Python 3.10 +, cattrs could evaluate ForwardRefs without user intervention. We would just need to require that the user must use The Hooks now look like this
With Python 3.10+ everything works out-of-the-box! (this also works when YY is imported from different module!)
The nice thing of NewType is that in 3.10+ the Nevertheless: Python 3.9+: one can use the
Python <3.9: explicit evaluation
|
Hm, interesting. We don't support NewTypes currently (there's an open issue for that), so I don't know if I'm OK with having a NewType hook as part of this thing. I think NewTypes are potentially more important than ForwardRefs, so I cannot comment on this approach until I've looked at NewTypes in depth, which I don't have time to do now. The first approach seemed simpler. |
@Tinche Allright. Then lets split the issue, and put the NewType handling aside. It can be added later, and, as a side-effect, improve the usability for ForwardRefs. Should I prepare a PR for the ForwardRef handling? |
Yep, that sounds like a good start. |
Description
When using recursive types (see example below),
structure
withGenConverter
raises aStructureHandlerNotFoundError
with the message "Unsupported type: ForwardRef('Y'). Register a structure hook for it".
The error does not happen with PEP 563 enabled. That means when we add 'from future import annotations' to the program and
replace
y: typing.Optional['Y']
withy: typing.Optional[Y]
everything works as expected.What I Did
The text was updated successfully, but these errors were encountered: