setuptools: Due to limitation, static analysis don't support import hook used in editable install v64+

setuptools version

setuptools==64.0.2

Python version

3.10

OS

macOS, Linux

Additional environment information

No response

Description

After installing editable packages (with a pyproject.toml) with setuptools >=64, mypy is no longer able to find the py.typed files when checking a codebase importing/referencing the editable installed package.

This was opened as a mypy issue in https://github.com/python/mypy/issues/13392, but they recommended we open an issue here. I’m guessing there will be some further discussion on both sides, but just getting the ball rolling.

Expected behavior

Editable installs are still discoverable by static type checkers.

How to Reproduce

See the README in https://github.com/JacobHayes/editable-install-hooks-repro

Output

$ mypy --namespace-packages --explicit-package-bases src
src/org/pkg2/__init__.py:1: error: Cannot find implementation or library stub for module named "org.pkg1"
src/org/pkg2/__init__.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
$ pylint src/
************* Module org.pkg2
src/org/pkg2/__init__.py:2:0: E0611: No name 'pkg1' in module 'org' (no-name-in-module)

-----------------------------------
Your code has been rated at 0.00/10

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 27
  • Comments: 24 (13 by maintainers)

Commits related to this issue

Most upvoted comments

For example other backend using the editables might also rely on import hooks. I would also not classify it as a bug.

Hatch does not by default for this reason https://hatch.pypa.io/latest/config/build/#dev-mode

It’s worth noting that ‘static analysis tools’ also include the tools used to provide rich editing experiences in modern editors; in particular, type inference and thus auto-completion suggestions and hover information rely heavily on static analysis. This goes way beyond type checkers.

Thank you @abravalheri that is helpful - I will watch this space for updates.

Perhaps you can help me with a conceptual question (that also might be related to @s-banach’s comment about the docs). What is the default editable install mode? I can see in the code that the default is called “lenient” but how does that differ from ‘compat’? Forgive me if this is a naïve question, even though I have been using python for a long time this is still all very - confusing.

I just came here to leave a comment on the language in https://setuptools.pypa.io/en/latest/userguide/development_mode.html

The way it’s currently worded feels like “we either don’t know or don’t care about the issue with type checking”. If the documentation is changed to say “compat is a temporary solution until we reach some agreement with the static type checkers on how to handle editable installs”, people like me might not show up here to rant and rave.

Hi @cswartzvi it’s been pushed back. I believe that compat is not the final solution for this problem, but I haven’t had the time to investigate and propose something more permanent yet.

As a general concept I believe that it would be possible to come up with a static file (e.g. JSON) describing editable package locations that could at the same time feed static analysis tools and a MetaPathFinder/PathEntryFinder (I briefly mentioned it in https://github.com/pfmoore/editables/issues/21#issuecomment-1227059911). Of course, this needs a PoC implementation + a round of discussion in the community.

Maybe setuptools should do something similar?

When we analyse the problem from the optics of type-checking this seems reasonable.

However there are other things in play: there is a series of setuptools issues that cannot be fixed with the previous implementation. Moreover, import-hooks help us to that match more closely a regular installation, and this is also very helpful to identify bugs before packages are published.

The TLDR is that an explicit opt-in mitigates some problems but re-open other ones.

Please also note that there is already a series of compromises and trade-offs in place: whenever “safe” setuptools will not use import-hooks.

There is also quite a few scape hatches for the case users are more concerned with type-checking than the other packaging problems, so they can “opt-out”.

Hatch does not by default for this reason https://hatch.pypa.io/latest/config/build/#dev-mode

pdm also requires users to explicitly opt-in to the import hook approach – https://pdm.fming.dev/latest/pyproject/build/#editable-build-backend

Maybe setuptools should do something similar? Solving this issue “the right way” (ex. new file consumed by tools) is going to take a while (many months). Requiring users to opt-in could mitigate the problem in the meantime.

Edit: Sorry for the confusion. Seems like we did not support custom import hooks, I was messing up some terms.

No problems, this is one of the most confusing aspect of Python 🤣

I suppose this means that this issue already captures the challenges with pylint interoperability and we don’t need to create a new one.

I have created a PR that does support them and correctly finds the new editable packages. However, since other linters are seemingly hesitant to add support for custom import hooks I’m wary of supporting them in pylint and astroid right away.

In the mailing list pointed out by @JacobHayes, there is a suggestion that could work as a more long-term/stable approach (probably as a packaging standard/PEP). In summary the suggestion is to have a JSON file placed somewhere in the site-packages directory, containing static paths and metadata for modules in the editable installation.

Do you think that would be a better (or more acceptable) approach for pylint?

I have been a bit short of time lately, but I think I could work with that… I also think that other people in PyPA would be supportive of this approach.


Meanwhile, this information may be relevant: setuptools will try to use static .pth files as much as possible. Projects that use the so called src-layout should be available without the need of import hooks.

If users want to use flat-layout, they can circumvent the limitations by using the strict mode.

We’re running into similar issues with pylint although I’m not sure it is exactly the same issue. We do support import hooks and try to resolve those so it might be better to open a standalone issue for our problem. If preferred, please let me know.

A small summary: pylint uses astroid to generate the ast tree of python files with some additional nice-to-haves to help use perform the analysis. astroid tries to mimc Python import behaviour with its interpreter._import module: https://github.com/PyCQA/astroid/tree/d033fe2f4c575d45a1706c52988dbcdf50645c5c/astroid/interpreter/_import On setuptools<63 we were able to discover editable installs through sys.path and looking for the __init__.py file. The code that is responsible for this is: https://github.com/PyCQA/astroid/blob/d033fe2f4c575d45a1706c52988dbcdf50645c5c/astroid/interpreter/_import/spec.py#L135-L145

This find the package and returns a ModuleSpec which resembles a importlib.ModuleSpec.

On setuptools>=64 this no longer works. I assume it is because there is a difference in the effect pip install -e has on sys.path but I’m not sure. Does anybody who is knowledgable about the new editable system see how we should resolve imports to editable packages?

The pylint issue that tracks this is https://github.com/PyCQA/pylint/issues/7306. Feel free to continue the discussion there if you want to keep this focused on the original issue.

Sure 🙂