Repository navigation
Conversation
|
Hi @ysheikh2! Thank you for your pull request and welcome to our community. Action RequiredIn 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. ProcessIn 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 If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
456bea1 to
a110941
Compare
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
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: Both .venvs therefore have the same polars version. pyrefly picks a file's environment from where the file is, not from what imports it. To answer your question, an open file still uses its own folder's environment with this PR, so an open 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. |
a110941 to
e72cda7
Compare
|
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.) |
|
Hey, thanks again for the effort here. This makes sense and I do see the need for a change like this, specifically for editable installs / PEP 610 support. Handle is the right place, but including this as part of sys_info clouds all other usage of sys_info like stdlib. Maybe we put it in handle directly. We also need to be careful this doesn't hurt performance much considering we are now checking many copies of dependencies. Thanks for your patience: I'll have more time next week to go over this change, discuss with the team, and derisk it further. |
In file mode and the language server, each file is checked with the config of its location. A library with its own config and virtual environment, installed editable into an app's environment, therefore resolved its imports in its own environment. The app then saw two copies of the same package, and pyrefly reported errors such as "`DataFrame` is not assignable to `DataFrame`". A module found through the importer's site-package path, whose own config uses a different site-package path, now gets a handle that records the importer's site-package path. Its config is a copy of its location config with that path, so its imports resolve to the same modules as the app's. `SysInfo` and the stdlib are unchanged, and in project mode every handle stays as it was, since every module has the same config. The same file can now be checked in more than one environment, so IDE features that look up a definition's module use the handle the current file imports, and reverse-dependency queries include every copy. The handle's hash leaves the new field out, which keeps hash-ordered collections, and so the modules a check loads, the same as before. Fixes meta#3556
e72cda7 to
1a15486
Compare
|
Thanks, that makes sense. I've moved it out of
Some further research and checks for how other libraries implement:This matches ty. ty identifies a module by the file and the environment checking it, and resolves a library's imports in the importer's environment, so a library imported from two environments is checked twice (astral-sh/ty#819 (comment)). Two equal environments share one copy, as they do here. Pyright also takes site-packages from the importer, but takes On performance, I measured
With one app the check takes 15% fewer instructions, because the library no longer loads a second copy of its dependencies from its own |
|
Thanks for moving it into
The workspace diagnostics filter in Not many bites on my discussion here and no issues bringing it up internally, so I think we're unblocked on landing this. Thanks again for your patience. |
The importer's site-package path on `Handle` is now an interned `SitePackagePath`, so `Handle` derives `Hash` again and comparing it stays a pointer compare. `copies()` looks up one handle per Python environment instead of scanning every loaded module. Only the IDE changes that a test fails without are kept. Lookups that build a handle without an import, including `class_tparams` from main, use the handle the importer's environment gives the module. So does the class docstring lookup in hover. The handle for an explicit path is read from the entry that records the dependency, which keeps project mode within 1% of main on PyTorch. `get_all_errors` reports each file once, so `check --check-all` no longer repeats a library's errors for every environment it was checked in. The IDE, configuration and import resolution docs describe the behaviour.
|
Thanks for going through it, and for taking it to the team. I've merged the latest main and worked through the points. Benchmarks below are 1. Why the IDE changes. An open library file is still checked as the library, in its own environment. The IDE changes are for the other direction, an app file looking up something in the library. The app's import gives the library's copy in the app's environment. A handle built with I reverted each IDE change in turn and kept only the ones a test fails without:
Keyword arguments, outgoing calls, merging incoming calls by file and the in-memory counterparts in find references gave the same results without their changes, so I've dropped those. An importer's copy of a library file is only a dependency, so it has no index for incoming calls to read. 2. Hash. The field is now an interned 3. 4. The
I've rewritten it so it adds no lookup. The explicit-path branch reads the handle from the 5. Cross-module lookup without an import. Added 6. A venv outside any config. Added
I've added this to the summary. One more fix from going over it again: |
Summary
In file mode and the language server, each file gets the config of its location. If a library with its own config and
.venvis 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 "DataFrameis not assignable toDataFrame". Project mode is not affected.This PR checks such a library in the importer's environment:
Handlehas an optional, internedSitePackagePathof the importer's environment. It is set when a module is found under the importer's site-package path and its own config uses a different one.SysInfoand the stdlib are unchanged.State::get_configgives such a handle a copy of its location config with the importer's site-package path, so the library's imports resolve to the same modules as the app's.check --check-allreport each file once, from its own environment.An open file keeps its own folder's environment. A library imported from two environments is checked once per environment, as in pyright and ty. A module in a
.venvoutside any config is checked in the importer's environment, and once more in the default config if it is opened directly. One case remains. A library found throughsearch-pathcan use a type from its own environment. Looking up that type from the app can then load a second copy of its module.Fixes #3556
Prepared with an AI assistant (Claude) and reviewed by the author.
Test Plan
pyrefly/lib/test/state.rscover two apps sharing a library and a change of the library's environment. They also cover class fields through a function-local import, a.venvoutside any config, IDE lookups and hover from an app, references, and--check-allerrors. Each fails without the part of the change it covers.cargo test --profile benchandcargo clippyon Linux, thestatetests on macOS, and thetest.pylint. Not run on Windows.pyrefly check --threads 1, release build on Linux, median of 7 runs. The apps install the library editable into their ownuvenvironments..venvoutside any config.venvoutside any configThe extra cost comes from checking a library once per environment that imports it. mypy_primer runs single-environment project checks, so it doesn't exercise this change.