Skip to content

Check a module in the Python environment it is imported from - #5414

Open
ysheikh2 wants to merge 2 commits into
meta:mainfrom
ysheikh2:fix-3556-env-per-importer
Open

ysheikh2 wants to merge 2 commits into
meta:mainfrom
ysheikh2:fix-3556-env-per-importer

Conversation

@ysheikh2

@ysheikh2 ysheikh2 commented Oct 27, 2026 •

Copy link
Copy Markdown

Summary

In file mode and the language server, each file gets the config of its location. If a library with its own config and .venv is installed editable into an app's .venv, the library's imports resolve in its own .venv. The app then sees two copies of the same package and reports "DataFrame is not assignable to DataFrame". Project mode is not affected.

This PR checks such a library in the importer's environment:

  • SysInfo now carries the site-package path. It is already part of the Handle key and is passed on by every import.
  • If a file lies in its handle's site-package path and that path differs from its own config's, State::get_config uses a copy of the config with the importer's site-package path.

An open file keeps its own folder's environment, so a library file opened on its own is checked as today. A library imported from two environments is checked once per environment. pyright, basedpyright and ty use the same model, with the workspace folder as the unit.

One open question for maintainers is whether the environment should live in SysInfo or in a separate Handle field.

Fixes #3556

  • I am a human, and I fully understand the code I am submitting.

Prepared with an AI assistant (Claude) and reviewed by the author.

Test Plan

  • New test test_library_imported_from_another_environment: fails before the change, passes after.
  • cargo fmt -- --check, cargo clippy --release, cargo test --profile bench.
  • The repro from the issue, with the language server (folders app + lib, and root + app) and pyrefly check main.py: 1 error before, 0 after. Project mode: 0 errors before and after.
before-after fix

@meta-cla

meta-cla Bot commented Oct 27, 2026

Copy link
Copy Markdown

Hi @ysheikh2!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@ysheikh2
ysheikh2 force-pushed the fix-3556-env-per-importer branch from 456bea1 to a110941 Compare October 27, 2026 16:53
@github-actions github-actions Bot added size/l and removed size/l labels Oct 27, 2026
@ysheikh2
ysheikh2 marked this pull request as ready for review October 27, 2026 17:19
@meta-cla

meta-cla Bot commented Oct 27, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@kinto0

kinto0 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Sorry about the delayed response. I think this breaks a lot of assumptions we have: in the IDE, which environment do we pick when a file is open? I won't have enough time to review this today or reply to the original issue unfortuately.

@ysheikh2

ysheikh2 commented Oct 30, 2026 •

Copy link
Copy Markdown
Author

Sorry about the delayed response. I think this breaks a lot of assumptions we have: in the IDE, which environment do we pick when a file is open? I won't have enough time to review this today or reply to the original issue unfortuately.

Hi, thanks for your feedback

I've never come across this being an issue in any other type checker for python, with basedpyright, pyright, mypy it's always just worked for these monorepo setups and/or multi-root VSCode workspaces. A lot of my team hits this with our monorepos, as does the reporter of #3556, and it also comes up in #4987.

The setup from #3556 is a VS Code workspace with two folders, each with its own interpreter:

app/   its own pyproject.toml and .venv, with lib as an editable dependency, which brings polars into that .venv
lib/   its own pyproject.toml and .venv, with polars as a dependency

Both .venvs therefore have the same polars version. app/main.py calls mylib.frames.width(pl.DataFrame()).

pyrefly picks a file's environment from where the file is, not from what imports it. polars in app/main.py comes from app/.venv, but polars in lib/mylib/frames.py comes from lib/.venv. The two DataFrame classes don't match, so pyrefly reports an error. pyrefly check app/main.py gives the same error without an editor.

To answer your question, an open file still uses its own folder's environment with this PR, so an open frames.py is checked with lib/.venv, as today. The PR adds a second check of frames.py with app/.venv, used only for app's results and never shown in the editor.

This is the same model the other type checkers use. pyright, basedpyright and ty use one environment per workspace folder, and every file a folder reaches, including a library installed as editable, is checked with that folder's environment. A file reached from two folders is analysed twice, once per folder. Eric Traut describes this in microsoft/pyright#4329 (comment). mypy uses one environment per run. This PR uses the importing environment instead of the workspace folder as the unit.

A library with its own config and Python environment, installed editable
into an app's environment, is checked with its own config in file mode
and the language server. Its third-party imports then resolve in its own
environment, so the app sees two copies of the same class (meta#3556).

The test sets up an app and a library, each with its own site-package
path containing a `dep` module, and expects no errors. It fails today.
In file mode and the language server, each file gets the config of its
location. A library installed editable into an app's environment, but
with its own config and environment, then resolves its imports in its
own environment, and the app sees two copies of the same package
(meta#3556). Project mode does not have this problem, because it uses one
config for every file.

`SysInfo` now also carries the site-package path of the environment.
It is part of the `Handle` key and every import passes it on, so a file
reached from two environments becomes two modules. When a file lies in
one of its handle's site-package directories and that environment
differs from its location config's, `State::get_config` uses a cached
copy of the config with the importer's site-package path, so its imports
resolve to the same modules as the importer's. A file opened on its own
keeps its own config. Bundled stubs never carry an environment, and the
stdlib is still computed once per version and platform.
@meta-codesync

meta-codesync Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D128474304. (Because this pull request was imported automatically, there will not be any future comments.)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bad-argument-type false positive for multiple venvs in one check

2 participants