Skip to content
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

How to typecheck "2nd" party packages? #3350

Closed
eddie-dunn opened this issue May 10, 2017 · 11 comments
Closed

How to typecheck "2nd" party packages? #3350

eddie-dunn opened this issue May 10, 2017 · 11 comments

Comments

@eddie-dunn
Copy link

Let's say we have two separate packages, a and b. b depends on stuff in a, so a is installed when b is installed. E.g., there's a module in b that does the following:

# b/module.py
import a.util

def foo(var: str) -> str: ...

def bar() -> None:
    mystr = a.util.func_that_returns_str()
    print(foo(mystr))
# a/util.py
def func_that_returns_str() -> str: ...

If I run mypy on b/module.py I will get the following error: error: Cannot find module named 'a', even though a is installed in my virtualenv.

As I understand it, this is the intended behavior, and the solution is to either append --ignore-missing-imports or add stubs to typeshed.

The problem in our case is that

  • a is a private package we have developed for our own internal use, so typeshed is not an option
  • mypy --ignore-missing-imports b/module.py will not be able to verify that module.bar correctly calls module.foo
  • attempting to add my virtualenv's packages to MYPYPATH seems to be highly discouraged, and is also not a great option because b might also depend on and install 3rd party stuff that isn't properly type hinted

So basically, my question is this: How can we tell mypy to include the imported package a when typechecking b?

I suppose we could add .pyi stubs for a inside the b repository and point MYPYPATH there, but that is suboptimal as it would require us to maintain the API for a in two separate repositories, and entirely unnecessary since everything in a is already properly type hinted.

@gvanrossum
Copy link
Member

You can write or generate your own stubs for a and update MYPYPATH to include the directory containing those stubs (the parent of the directory a containing a/module.pyi etc., presumably).

Or, assuming the source for a type-checks without errors, you can point mypy at package a directly, again by setting MYPYPATH -- the trick is that you shouldn't use the installed version of a, because that presumably contains lots of other 3rd party packages/modules for which you would prefer the typeshed stubs.

@eddie-dunn
Copy link
Author

Sorry for taking so long to respond, but I haven't had the opportunity to try this out until now.

Now I've tried both approaches.

As I mentioned in my original post, writing or generating stubs incurs an unnecessary maintenance burden I would prefer to avoid, since a type-checks without issues.

Pointing MYPYPATH to a cloned repo of a is a better approach, but requiring that a is cloned somewhere locally is also a bit annoying, especially since it is already installed and available in the virtualenv.

Ideally, I would have liked to be able to explicitly whitelist specific packages inside the virtualenv's site-packages folder. E.g., MYPYPATH_WHITELIST=some_package:some_other_package, and make mypy aware that usage of some_package and some_other_package should be type-checked if found to be installed in the venv.

Or is that something you don't want to support with mypy?

@JelleZijlstra
Copy link
Member

I'm working on a proposal to standardize a way to have a site-packages-like directory for stubs; see python/typing#84.

@eddie-dunn
Copy link
Author

@JelleZijlstra Does that solve the issue I have, though? I don't see the point of using .pyi files (i.e., stubs), if the .py files themselves are already type hinted. It's like we're regressing back to requiring C header files.

Or can .py files be used as stubs as well?

To exemplify, I've solved the issue I describe in the previous posts by having my tox.ini copy some_package and some_other_package from site-packages into a temp-folder, then pointing MYPYPATH there. I.e.,

cp -R <venvpath>/lib/python3.6/site-packages/some_package /tmp/mypypath
cp -R <venvpath>/lib/python3.6/site-packages/some_other_package /tmp/mypypath
MYPYPATH=/tmp/mypypath mypy src/my_package

It works, but it's an ugly hack.

@JukkaL
Copy link
Collaborator

JukkaL commented Jun 29, 2017

The problem with using .py files is that they might only type check cleanly with specific mypy options, or only with specific mypy versions. Stub files are more likely to work consistently in multiple contexts, though even there it's possible to have issues if Optional[...] is not used consistently. One option that we've discussed is making a tool that can automatically generate annotated .pyi files from .py files. Then a 3rd party package would ship annotated .py files and automatically generated .pyi files, and mypy would somehow automatically find and use the .pyi files. Another option would be to implicitly ignore all type errors in 3rd party modules, but that would be less robust as mypy version incompatibilities can result in invalid inferred types for public methods, for example.

@ilevkivskyi
Copy link
Member

One option that we've discussed is making a tool that can automatically generate annotated .pyi files from .py files. Then a 3rd party package would ship annotated .py files and automatically generated .pyi files, and mypy would somehow automatically find and use the .pyi files.

Note that there is #3169 for preserving existing annotations by stubgen. I hope @dmoisset will continue working on it.

@JukkaL
Copy link
Collaborator

JukkaL commented Jun 29, 2017

Just preserving annotations is not quite enough, since some types are inferred. Stubgen would have to type check the code and then print out the inferred types.

@eddie-dunn
Copy link
Author

Well, to be honest, I am personally not that enamored with stubs, since I think they are redundant in the same way I think header files are redundant. I see why they exist, and why they might be required in some cases, but I don't understand why they should be required. And as my hacky workaround above shows, they aren't actually necessary at all.

A mypy user who wants to utilize 2nd/3rd party .py files from some arbitrary installed package when type checking their code, should be able to do so, don't you think? Obviously, it's then also up to the user to worry about whether the correct mypy options/versions are in effect.

Anyway, I'm asking for clarification here because I want to know if you are at all open to the idea of adding the option to whitelist stuff in site-packages. If yes, I could start working on a pull request, but I realize that what I'm asking for might not be general enough for inclusion in mypy, and I don't want to waste time implementing something you don't want at all.

@JukkaL
Copy link
Collaborator

JukkaL commented Jun 29, 2017

We are open to the idea, but it's not clear what the option should look like. Preferable a user shouldn't need to know which packages have inline annotations in them -- mypy should be able to figure this out automatically somehow. Perhaps each package should declare the presence of inline annotations. There is more discussion about this topic in #1190. Maybe we should close this issue and continue discussion in #1190?

Here's a summary of how this could move forward:

  • Propose a concrete approach for how to do this (no implementation yet) in Search procedure for inline annotations #1190. Read the existing discussion first -- there are a bunch of things to consider.
  • Once we reach agreement, you (or somebody else) can implement it in mypy.
  • Once there is an implementation, somebody can write it up as a PEP in case the approach can be generalized to other tools beyond mypy.

@ambv
Copy link
Contributor

ambv commented Jul 13, 2017

Since this is not mypy-specific, I'd suggest continuing in python/typing#84.

@emmatyping
Copy link
Collaborator

Closing in favor of python/typing#84.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants