-
-
Notifications
You must be signed in to change notification settings - Fork 373
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 PEP484 stubs #238
Add PEP484 stubs #238
Conversation
So, we need a mypy-Plugin in any case (please coordinate with @Tinche if you want to help, so we don't end up with duplicate work here!), [c|sh]ouldn't that plugin also handle the |
I'm not really sure how to handle the stubs; does mypy scan the PYTHONPATH for them? I seem to remember it doesn't. Putting them into typeshed is not a good solution imho, what about versioning? Maybe someone should go ask them what to do exactly. But stubs aren't really the big issue here. We basically need a mypy plugin that would run between the 2nd pass semantic analysis and the typecheck pass that would inject attrs generated stuff into attrs classes, and I think mypy doesn't have this type of plugin yet. |
I think that the plugin should focus on exposing the dunder methods to mypy, and not concern itself with the return value of The core problem from a static type-checking POV is that in our example we want x = 1
x = 'foo' # error The class C:
x : ClassVar[int] = 1
C.x = 2 # ok
C().x = 2 # error So that's not what we want either. Could we create a mypy plugin that allows this dual-type behavior between classes and instances? Maybe, I don't know the API well enough to say for sure, but my instincts tell me that it will be far more difficult than adding the As @Tinche pointed out, the current mypy plugin system does not expose everything needed to solve our issues for attrs: attempting to solve the I believe that the desire to make Here's an example showing those two use cases : import attr
@attr.s
class C(object):
x : int = attr.ib(convert=int)
y: str = attr.ib()
D = attr.make_class('D', {'x': attr.CountingAttr()}) A user who is experienced with static type checking will actually find it unintuitive if the attribute types are allowed to change. Yes, we're lying to the static type checker (e.g. by saying All these things considered, I think we should focus on writing a mypy plugin that exposes the dunder methods added by
Mypy has its own search path defined by
I agree about the disadvantages of typeshed. There is an epic typing issue discussing installation of third-party stubs, and it's still not resolved. Maybe the simplest solution is just to move the annotations into the code itself? This also has a major advantage in PyCharm: when working with modules that provide stubs, the "go to definition" behavior will take you to the definition within the .pyi file, which doesn't contain docs or any code. edit: The only disadvantage I can think of to moving the annotations into the code is that it will add
I agree that for complete support we'll need such a plugin, but adding typing to attr's public API will provide immediate benefits in tools like PyCharm, where perfection is not required. This is a step in the right direction and sets us up to solve the harder issue of dunder methods. |
Codecov Report
@@ Coverage Diff @@
## master #238 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 9 9
Lines 856 830 -26
Branches 183 174 -9
=====================================
- Hits 856 830 -26
Continue to review full report at Codecov.
|
I read all of the epic typing issue on third-party pyi stubs, as well as the newly drafted PEP on the subject, and here's the synopsis:
That means that installing the pyi files alongside the py files is correct. @Tinche @hynek, do you all have any concerns about merging this PR prior to making a mypy plugin? Any other concerns that you'd like addressed? I still have to fix a few remaining typing issues and deal with documentation and testing, I just want to make sure I have the green light first. thanks. |
Correct me if I'm wrong, but it seems to me that merging this is kind of pointless without a mypy plugin because it fixes only a part of the failures? Unless I miss something, I’d put this PR on hold until we can actually use mypy (better sooner than later as far as I'm concerned…). |
This is probably true for mypy, since it would typically be used as part of a CI process where erroneous failures are problematic, but this PR would immediately benefit users of IDEs with PEP 484 support, like PyCharm. Below is an animation showing how the stubs improve PyCharm's completions. What's great is that this works for users who aren't yet on python 3.6 and can't take advantage of variable annotations. It also gives completions for utility functions like |
I see, so the question becomes: [how] can we meaningfully test them? Ideally both for consistency and completeness? |
The mypy project contains some pytest utilities that shouldn't be to hard to port/utilize, but mpy will have to be added to the list of test requirements. Our test runner will look something like this The test runner code can only be run in python 3.5+ but the code within the .test files can be written using 2 or 3. I see two approaches to running them:
|
Update: I've got the test running added, but the stubs don't check properly in mypy due to a bug: python/mypy#4027 Let me know if you have any comments on how the tests are run. |
This is the recommended approach for 3rd party stubs. See: python/typing#84 (comment)
it does not make sense to test the stubs in multiple python *runtime* environments (e.g. python 3.5, 3.6, pypy3) because the results of static analysis wrt attrs is not dependent on the runtime. Moreover, mypy is not installing correctly in pypy3 which has nothing to do with attrs.
It is crucial to ensure that make_class() works with attr.ib(), as a result we no longer have any functions that care about _CountingAttr.
this allows for an abbreviated idiom: `x: List[int] = Factory(list)`
In preparation for removing them from the pyi_stubs branch.
This is a final test of the current stubs before moving the stub tests to a new branch.
Replace with a simple mypy pass/fail test
@hynek ready for your review! |
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.
yay!
tests/typing_example.py
Outdated
import attr | ||
|
||
|
||
# Type argument |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
@@ -0,0 +1,240 @@ | |||
from typing import ( |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
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.
I will leave the review of the typing part to @chadrik and only comment on the packaging part.
Which is currently broken. :) And it shows one problem we’ll always have: we have to compete with typeshed's stubs. :|
I suspected it might not work, so to prove it, I installed mypy and deleted attrs’ stubs from it…I wish there were a better way?
Anyways, you need to add include_package_data=True,
to the setup.py call so the typed.py gets actually installed.
Also please remove the .idea
from .gitignore and add it to your ~/.gitignore_global
we try to steer clear of editor-specific ignores.
Finally, let’s move the typing env higher up in tox’s envlist
(I don’t care about Travis). It runs fast, so it’s quick feedback before the slow part begins.
Thanks!!!
(As for the typeshed competition problem: does anyone have an idea how to ensure we’re using our own stubs? Maybe define some private sentinel object that typeshed doesn’t have and check against it in |
Hm, PEP 561 (and the mypy implementation) dictates that installed packages should take precedence over typeshed. Is that not happening? |
It is. However this PR’s packaging has a bug and we can’t tell because it falls back to typeshed. |
Hi all, I addressed your notes.
Why don't we remove them from typeshed? I don't see any good reason to keep them in both, and there are a lot of reasons not to. |
The only reason not to AFAIK is that if the pyre/pytype folks (who haven't yet implemented/are not implementing PEP 561 respectively) may want them in typeshed. That could make things more complicated, but I'd suggest opening an issue and asking. E: forgot critical "may" want them |
Wouldn’t that be a regression for users of older versions of attrs? 🤔 |
We're talking about the bloodiest of bleeding edge here. I just don't think it's worth taking on the work -- and additional issues -- of maintaining both for the tiny niche of users who are using the typeshed stubs but not able to update to the latest version of attrs, or who are using pyre or pytype and are not able to wait for PEP 561 support. Keep in mind that the typeshed stubs are limited to the bleeding-edge anyway, as they can only support a single version of attrs at a time. |
Almost eleven months, 164 comments, a lot hence and forth, but we’ve made it! Thanks @chadrik and everyone else! Now we can work on details. :) |
Congrats everyone! @euresti actually did a lot of the hardest work, I just pushed along the process. @ethanhs Let me know when you get started on your pytest plugin. I have some thoughts on what an ideal test framework would look like, and would be happy to pitch in. My next project is going to be lobbying the mypy devs to accept a PR to find plugins on the PYTHONPATH, so we can move ours into this repo. |
Also, I've parked my old changes containing pytest and doctest plugins here: https://github.com/chadrik/attrs/tree/stubs_tests |
Congrats! @chadrik I likely will work on the plugin this weekend. I've opened https://github.com/ethanhs/pytest-stub-check/issues/1 and would welcome your input. |
* Add narrative docs for type annotations * Better wording * Use better code-block types * Add newsfragment for #238 that refers to these docs
Here's a first take at PEP484-compatible pyi stubs. The discussion in #215 has been mostly focused on runtime type checking, so I wanted to make sure that the plan discussed there will work with static analysis as well.
Here's a quick test of the stubs that I put together to run against mypy:
mypy knows nothing about the dunder methods added by attrs, so cmp methods and
__init__
fail. This will have to be solved with a mypy plugin, which I'll look into next. Same goes forattr.make_class
though that's likely to be more difficult.One important thing to note: in order to ensure that
y: str = attr.ib()
does not trigger a mypy error, the return type ofattr.ib
isAny
instead of_CountingAttr
. A side effect of this is that it complicates passing the result ofattr.ib
to functions that expect_CountingAttr
instances. There are a handful of strategies to solve this:typing.cast
: ugly and verboseattr.ib
that will return_CountingAttr
, to use with functions likemake_class
.make=True
?) whose presence invokes an@overload
ofattr.ib
that returns_CountingAttr
_CountingAttr
and copy the keyword defaults fromattr.ib
to_CountingAttr.__init__
. In other words, if you use static type checking and are working with a function that expects_CountingAttr
instances, use_CountingAttr()
directly instead ofattr.ib()
I favor the 3rd solution.After some consideration I favor 4.If you're interested in accepting this, the next things to figure out are:
Let me know what you think.
edit added option 4.