-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Add stubs for WTForms #10557
Add stubs for WTForms #10557
Conversation
To fix the (The allowlist has to be in a separate repo for security reasons) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I tried out the stubs in a larger project and it was definitely a little bit more painful to use there, but this can mostly be attributed to the additional abstraction layer that project uses to dynamically create forms and the heavy use of Other than that I found a small typing issue in There is still a type safety issue with |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks like it was a heroic effort! I read through all the stubs and noted some minor issues.
Co-authored-by: Jelle Zijlstra <[email protected]>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The mypy-primer hits suggest the |
It can be a little bit annoying and there isn't a real unsafety in that particular piece of code, but I ultimately decided that type safety is still worth the little bit of extra hassle, especially considering it doesn't come up if you use Most validators should guard against |
Diff from mypy_primer, showing the effect of this PR on open source code: pegen (https://github.com/we-like-parsers/pegen)
+ src/pegen/web.py:6: error: Unused "type: ignore" comment [unused-ignore]
+ src/pegen/web.py:7: error: Unused "type: ignore" comment [unused-ignore]
+ src/pegen/web.py:55: error: Argument 1 to "make_parser" has incompatible type "str | None"; expected "str" [arg-type]
+ src/pegen/web.py:56: error: Argument 1 to "parse_string" has incompatible type "str | None"; expected "str" [arg-type]
|
This one's a bit of a doozy. I had already attempted to create stubs for this a few years back and gave up because they ended up being too annoying to use since the API isn't very friendly to static analysis.
But I've recently seen some examples in
types-PyYAML
that gave me renewed hope to attempt this and ended up with something I'm pretty happy with. It's not perfect, there are some things about the API that flat out don't work in type checkers:Field.__new__
will return by default anUnboundField
which is not a subclass ofField
and even if I were to make it a faux-subclass and added overloads in__new__
to make it work for the base class, I would then also need to add an overloaded__new__
to every subclass and it would also break user-defined subclasses ofField
. So that's not really sustainable. What I did instead was turnField
into a faux descriptor, that will return anUnboundField
when accessed from the Form class and itself when it's accessed on the instance, that should cover most use-cases and it required only one additional hacky workaround forFieldList
which has to acceptField
in addition toUnboundField
, if we don't want to force people to manually constructUnboundField
instances for type checking.Other than that I've added a few test cases for the more complex type protocols that can be passed into
Field
/Form
such asvalidators
,widget
andfilters
since it was a little bit tricky to ensure there would be no false negatives while still retaining some amount of type safety.I've integrated the stubs in two medium sized projects which both do some advanced things with the internals of WTForms in different ways and managed to catch a lot of the rough edges already.
A slight pain point right now is that form validation cannot result in any kind of type narrowing, so you will need to manually add some
assert form.field.data is not None
for fields withInputRequired
/DataRequired
validators. It shouldn't be too big of an issue though, since in a lot of cases you should just be usingform.populate_obj()
rather than accessing individual field data.There also seems to be an issue with overriding
Field.widget
in subclasses, since it is both aClassVar
and instance variable. For some reason mypy will complain if subclasses annotate the field as_Widget[Self]
, even though that matches the definition on the base class, so that seems like a false positive on mypy's end to me. This should only really be relevant though for user defined fields with a custom default widget that have subclasses themselves.The docs for WTForms are pretty good and can be found here: https://wtforms.readthedocs.io/en/3.0.x/
Although it might still be quicker to just look at the source code: https://github.com/wtforms/wtforms/blob/3.0.1/src/wtforms/__init__.py