-
-
Notifications
You must be signed in to change notification settings - Fork 2.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
Search procedure for inline annotations #1190
Comments
I agree that it would be nice if mypy could do the right thing automatically when a library module has inline annotations. However, this proposal has a few problems:
Here are some further ideas:
@bdarnell What do you think? |
I'd be happy to include some sort of package-level marker to indicate that the package contains PEP 484 type annotations and these should be used instead of typeshed (and that these decisions should be made at package scope instead of file-by-file). I'd rather not make this mypy-specific since mypy is not supposed to be the only consumer of PEP 484 annotations. If mypy can't handle the annotations in a package then it should either fail or fall back to typeshed for that package. It would be unfortunate if the only use of inline annotations was in a tool that extracted them to Requiring all stubs to be submitted to typeshed seems like the worst possible outcome - I was under the impression that typeshed was essentially a transitional thing, and the long-term goal was for type information to migrate from typeshed into the respective packages. I think centralizing everything in typeshed would discourage participation by package authors. |
I haven't forgotten this (I've just been distracted). We just encountered an issue here where there's a discrepancy between the way PyCharm and mypy search for stubs. In this case it was about an extension module, for which the user had created a stub file (but not added it to typeshed). Mypy found it fine on the default module search path. But PyCharm would only find it if there was a corresponding .py file in the same directory. In their case the fix was to add a .py file that simple re-exported the extension module. But it would be nice to have some kind of standardization for this. |
PEP 484 is not clear about several things about the search path for stubs. I've mentioned some of them in a comment to python/typing#184. @bdarnell Shouldn't *.pyi files from typeshed override the *.py files from installed packages? (according to the PEP). If there is no installed top-level package for the module in typeshed, then I believe the type checker could skip the typeshed stub for it. |
@vlasovskikh Why would typeshed annotations take precedence over annotations in the code itself? The files in typeshed may describe a different version of the package and the annotations closer to the code are more likely to be correct for that version. If typeshed takes precedence then it is impossible to transition from annotations in typeshed to annotations in the code itself, and packages are being added to typeshed without coordinating with the package authors. |
@bdarnell Given the following variants (please correct me if I'm missing something):
I would prefer the variant (1) here. Speaking of (3) it may take a while to determine if any files inside a package contain type hints, so it will slow the code analysis down making it hard to run it on-the-fly. Also (3) implies that any annotation added to a module (say, in the form of a |
You're assuming that typeshed annotations will be higher-quality than in-code annotations; I am assuming the opposite. More to the point, if I as the author of a package add type annotations to that package, that's how I want my users' code to be type-checked. I don't want people coming to my support forums with type-checking issues that are caused by version skew between typeshed and the actual code they have installed. Typeshed currently contains near-useless stubs for my project which were added without my knowledge. The presence of these stubs stands in the way of me adding my own annotations to the code. This is frustrating and discourages me from exploring type annotations further. Your option 3 is the only one that makes sense to me as a library author. I'm fine with adding some piece of metadata somewhere to say "this package uses PEP 484 annotations which should be used by type checkers" so that you don't have to actually load the code to find the annotations (and to address the possibility of incompletely-annotated projects that aren't ready to be checked without typeshed support). |
@bdarnell I see your point. Forcing library authors to mark their libraries with a piece of metadata that tells that the annotations should override typeshed seems a bit bureaucratic. We might be better off with (2) + a type checker-dependent notification that typeshed has a stub for a particular library so the user might want to use the stub if they want to. |
I like (2) as well. Can one of you formulate a PR to the PEP? |
In #1372 (comment) @bdarnell wrote
Actually mypy's search algorithm takes things one directory at a time. If the .py file occurs in a directory that's earlier in MYPYPATH than the .pyi file, the .py file always wins (with -s, it's marked as not found, but mypy doesn't fall back on the .pyi file if it's in a later directory). When a .py and .pyi are in the same directory, the .pyi always wins (regardless of -s). I personally find this a very reasonable algorithm. If you want to have independent control over whether your .py or your .pyi files win, put them in different directories, and flip the directory order in MYPYPATH. |
It's perfectly reasonable that when both Here's my concern: I want to publish a library that contains PEP 484 annotations in the source code, and I want users of this library to be able to type check their code using my annotations. If If there were a tool to automate the creation of |
That's not completely true. The presence of a The way to make it note them is to pass them explicitly on the command line -- or the directory containing the package. All
OK, this is the crux of the matter. I think "encouraged" is too strong a word, and we should really document it in a way that doesn't let people fall in that trap. It's also possible that we'll have to change something, but first I'd like to explore how close we can get without changes to mypy first. Today, without changes to mypy or typeshed, the way to arrange for what you want without passing the tornado package on the command line would be to download tornado and copy the files into a separate directory, and point MYPYPATH to that directory. The reason for the separate directory is that the version of tornado that's actually used is installed in But just passing the tornado package on the command line (together with the app itself) would be much simpler! In that case pointing to the copy installed in site-packages works just fine, and does the right thing regardless of whether Another problem is that we'd like to have a solution that lets the user set an environment variable once, rather than having to pass things (other than their own app and the flags they like) on the command line. At Dropbox so far we side-step these issues by having a script that runs mypy with the right arguments checked into our repos, but I realize this is not an ideal solution for most use cases. Perhaps a solution can be found using a central config file as suggested in #1249. Another possible solution would be to extend the meaning of |
A problem with just passing the tornado package on the command line is that this forces mypy to analyze the entire package. Using the |
OK, I believe that Passing A This new setting still seems like a lot of unnecessary manual maintenance, though. I'm envisioning something like a line in |
True but not the whole truth --
Sorry, that's just not what it's for. At this stage of mypy's development, we need
That's more or less the case (modulo some bugs), but it does this by suppressing type-checking errors whenever
The reality of mypy's implementation is that for it to find the errors in that file, it either needs stubs for every library package that it imports, or source code for those library packages. Analyzing the source code is slow. It would be nice to have a middle ground, but realistically that's not going to happen soon. However, add tweaking the way imports are processed is pretty easy.
Right, I wasn't proposing it to work that way.
No way. It has serious problems.
Or in typeshed. Note that the normal behavior of mypy is not to search PYTHONPATH or the default TBH so far you're unique in wanting to use inline annotations for Tornado. Most package authors have expressed little interest in this (they're either too busy or worried about compatibility) and are happy with imperfects stubs in typeshed maintained by others. Hopefully in the future your approach will prevail! |
OK, that's making more sense now. My introduction to |
I'm trying to synthesize a more concrete proposal from the above discussion. Here's my proposal: If you use inline annotations in a library that you maintain, you'd have a few different options for enabling mypy to use them:
The first option is not very user friendly, as there's no standard location for 3rd party The third option is also a little clunky. Looking up the right path can be done through something like |
Here's an idea to make (3) a little easier to use: add a repeatable mypy option that names a module or package name. Mypy will then look this module/package up using
This assumes that both Tornado and Flask have (Python 2 style) inline annotations in the installed package. NOTE: We will soon support a global setup file; the |
I like the interface, except for the fact that it's a little unclear which Python interpreter will be used by mypy. For example, if using 2.7, user might expect |
Sounds right. I guess until all that is implemented, we can recommend something like the following: M=tornado
P=$(python2.7 -c "import $M; print($M.__file__)" | sed 's|/__init__\.pyc*||')
mypy my_app_main.py "$P" |
I think you'll like #1895 once it's landed -- it'll let anybody (well, any setup.py :-) install their own additions to typeshed. |
#1895 gives me a place to put |
Oh, yeah, sorry, my previous comments suggest that I knew that (once :-). But I haven't heard your opinion on the suggestion I made there. I think it would be reasonable for there to be an option that can be used to name packages that are searched without also searching their sibling packages, so that you point mypy at a package installed in e.g. site-packages without causing mypy to search the site-packages directory. This option could be specified on the command line; once we have a global setup file it could also be specified there. @bdarnell Does that address your concern, or do you need more? PS. In my earlier example I think I meant:
|
|
Sorry about that. Unfortunately I know nothing about how setup.py works and
you'll have to spell it out in more detail.
|
My knowledge of setup.py is thin as well; I was hoping that someone else would be able to fill in the gap behind my hand-waving. A verbose way to do what I'm talking about would be the |
Usually, entrypoint should be executable. There is some more obscure arguments, such as |
Could we salvage option 1 by providing some minimal tooling in mypy (or some other common place) for packaging up Upsides I see to this approach:
|
I just re-read PEP-484 and found https://www.python.org/dev/peps/pep-0484/#storing-and-distributing-stub-files, which seems fairly close to what I'm already suggesting, but absent the additional tooling to auto-build |
We could use http://setuptools.readthedocs.io/en/latest/setuptools.html#adding-setup-arguments, and add such an entrypoint for mypy, so that you can just add something like Alternately, perhaps the |
Hi. Sorry to bother you like this here but is there any official example with stub-only package? |
This makes it possible to use inline types from installed packages and use installed stubs according to PEP 561. Adds `--python-executable` and `--no-site-packages` command-line options. PEP 561: https://www.python.org/dev/peps/pep-0561/ Fixes #2625, #1190, #965.
Fixed by #4693. |
@ethanhs So now there is a way to make mypy use inline annotations without duplicating code to .pyi? How? |
(moved from python/typeshed#52)
Currently mypy (AFAICT) looks for type information in the current directory/location of file being checked, directories on MYPYPATH, and typeshed, in that order, and takes the first file it finds. This will become problematic as more libraries adopt inline type annotations: the user will need to maintain a MYPYPATH that includes all such libraries. The easiest way to do that is to
pip install -r requirements.txt
and use that environment'ssite-packages
as mypy's path, but this will include packages that do not have type annotations and these source files may mask stubs for those libraries in typeshed.I propose that rather than stopping the search at the first file that exists, mypy examine source files to see whether they have usable type annotations, and if not, continue the search. If the search concludes without any usable type information but it did find some source files, then the first one can be used (to at least get the list of top-level function names, etc)
Completely:
-m
mode, the directory containing the given file if a file is given),$MYPYPATH
,$PYTHONPATH
, typeshed. I think it would be convenient if there were a shortcut to add thesite-packages
of a given virtualenv (perhaps defaulting to the environment in which mypy is running); this would come in between$PYTHONPATH
and typeshed.2a. Look for a
.pyi
stub file. If one is found, we're done.2b. If no
.pyi
file, look for a.py
source file. If it can be parsed and contains type information, we're done. If it can be parsed but does not contain types, and it's the first one we've seen, remember it.The text was updated successfully, but these errors were encountered: