-
Notifications
You must be signed in to change notification settings - Fork 234
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
Syntax, semantics and use cases of forward references #34
Comments
Another idea: if we're okay with
I think I've seen similar things in other language.s Maybe the body of the forward declaration could be some special symbol, e.g. an ellipsis ( |
With either form of explicit forward ref (as opposed to the string literal variant) the key question for the runtime then becomes, whether, and how, to unify the forward ref. I think we can probably implement this by calling (This is really just an issue for the runtime typing.py that I am working on. For the static checker and the PEP, the issue is simply which syntax we should endorse.) |
The runtime implementation using inspection of |
What about forward references that target non-generic classes? We don't have a metaclass there. At least in the mypy implementation, string escapes are used fairly often. A subtle point is that type annotations sometimes introduce cyclic module dependencies that wouldn't be there otherwise, and in these cases we may need string literal types with a module prefix (of form
However, the name I don't know how the class statement syntax would support inter-module type references. Here is a toy example of a cyclic dependency caused by an annotation:
|
Mypy allows using arbitrary types in string literals. Also, normal and string literal types can be combined. For example, |
OK, scrap my ideas. Let's think harder about the syntax string literals must support. How about this:
This is a recursive grammar but not a super complicated one. (The list notation and ellipsis are for Callable.) At runtime we can take a shortcut by just using eval() in the right scope (let's say the globals of the function whose annotation we're evaluating). Perhaps after some regexp-based sanity check (just letters, digits, underscores, dots, brackets, spaces) to prevent fear of security vulnerabilities. I think Eugene Toder suggested that typing.py should define a helper function to normalize an annotation given some context (perhaps a function or method object). |
How about allowing ForwardRef for the common cases, so programmers don't have to put quotes on most things, but still allow strings for cases that can't handle, e.g. cyclical dependencies? |
I meditated on this and in the end decided that the quotes are almost always better, and TOOWTDI. |
As suggested here: python/typing#34 and python/typing#105
The best proposal we have for forward references is:
This typically occurs when defining a container class where the class itself appears in the signature of some of its methods; the (global) name for the class is not defined until after the body of the class definition has been executed. (In some cases there may be multiple mutually recursive classes, so a shorthand for "this class" isn't really enough.)
A typical example is:
Note that the entire annotation expression 'Node[T]' is quoted, not just the class name Node (because
'Node'[T]
makes no sense).The question I'm trying to ask here is whether there is a reasonable limit to the complexity of the syntax that we must support inside string quotes. And if not, whether we may need to invent some other way to specify forward references. For example, something like this has been proposed:
A related question is whether the
__annotations__
dict should be "patched" to reference the intended class, or whether it is up to the code introspecting__annotations__
to interpret forward references (and if the latter, what kind of support typing.py should export to help).I've got a feeling that the ForwardRef() approach makes things a little easier for the runtime support, at the cost of slight verbosity. The question is how common forward references really are (apart from stubs for builtin/stdlib container).
The text was updated successfully, but these errors were encountered: