Open DanielRosenwasser opened 6 months ago
This would be rather hard for us to implement given our current design.
Each workspace folder shares settings. We have no facility for having different settings per python file or subdirectory.
Given you can only have one pyrightconfig per dir, can you pretend like the user has done this via multi-root workspaces internally? LSP calls back use the URI of the file, so will still get the same settings. This isn't unlike tsserver's "ProjectService" which just maps requests down to specific language service instances.
You mean each pyrightconfig.js is like a different workspace? I think that might work? I don't think we actually have to map our internal workspace
to a VS code one. I mean we have the <default>
workspace which doesn't exist anywhere.
Yeah; when I was talking to Daniel about the feasibility to it, the way I was thinking it would be possible would be to use multi-root as more of an "advisory" set of roots to consider, but to still be able to infer other roots when the user doesn't specify them.
I'm in the process of getting tsserver to do LSP, and reconsiling multi-root with our existing ProjectService is something I've been thinking about, and based on the fact that gopls does this sort of inference for go modules / workspaces, it feels possible for both sides of the pipe to have their own idea of what a "workspace" or "project" is and still interact.
Expanding, I think my perspective on multi-root workspaces has sorta shifted over the years; now I see it mainly as a way to provide multiple VS Code settings in a single folder. The underlying LS implementation can completely ignore this information when serving requests or performing analysis. But when it needs to talk back to ask questions, the answers it gets may vary based on what workspace each root was. (But if I'm missing something I'd love to know!)
Linking to https://github.com/microsoft/vscode-python/issues/21204 since this issue feels like it's trying to solve many of the same issues.
I'm thinking another problem we probably have currently is that we depend on the Python extension's concept of a "selected environment", so in the example where you'd like to use Python 3.12 for one folder and Python 3.11 for another, I think the Python extension would have to support automatic "flipping" environments when users change active files, which currently isn't supported unless users are in a multi-root workspace. There are conversations happening on the Python extension side around no longer stricting environment selection to a workspace (and instead have more of a "per file" environment association), so when that happens I think it might be easier to get Pylance to work with the proposal outlined here
(cc @karthiknadig)
For Scenario 1, could it be possible to specify in VS Code settings what pyproject.toml has to be used ?
I guess one thing I didn't really mention in this write-up is anything about when a file isn't included by the nearest pyrightconfig.json
. Is it implicitly covered by the nearest pyproject.toml
? Or does it need to keep searching upwards?
Background
Today, the way that Pylance and Pyright work is that the editor experience only respects the options in
pyrightconfig.json
orpyproject.toml
if they reside in a workspace root. Unfortunately this can be very confusing, and creates a lot of friction in specific workflows.Scenario: Multi-Language Setups
Here, every different language setup is sufficiently self-contained; however, a developer navigating such a codebase will not have their settings reflected in their editing experience out-of-the-box.
One could move their
pyproject.toml
orpyrightconfig.json
to the top-level, but then the Python-specificpackage.json
would not be co-located with the other Python-specific configuration files.Scenario: Scripts and Examples
When developing a library or shared codebase, it's common for examples and scripts to have different type-checking settings. For example,
src
files against Python 3.11 and check them as strictly as possibleexamples
andscripts
against Python 3.12, but with differing strictness settings compared to each otherHowever, when opening this directory, Pylance will only consult the top-level
pyproject.toml
, making such a setup impossible. While thestrict
option for specifying globs of directories works okay, it doesn't offer anything more granular.Overall Pain Points
In these workflows, a developer who wants their editor experience to align with their type-checking needs to open a specific subdirectory up rather than the package root. This runs counter to lots of out-of-the-box expectations when cloning a repo and opening a directory, or creating a Codespace/container session, and things just working after some configuration step.
Additionally, when things don't work correctly (e.g. strict type checking not kicking in), there's no obvious way to know when the configuration file is (or isn't) being consulted.
Why Not Multi-Root Workspaces?
The most obvious alternative is multi-root workspaces which kind of solves the issue for the first motivating scenario, but not so much for the second.
Multi-root workspaces also require developers to reopen a directory in a specific way and fundamentally alter the editor UI which makes things a bit weird when the codebase root has to contain both itself and its subdirectories as roots.
Additionally, it is a sort of non-obvious fix for the problem at hand because the current behavior of configuration being tied to workspace roots is surprising in itself. Many users may dismiss the "we found a workspace file" message that VS Code issues, which is a non-obvious footgun when things don't behave correctly.
As a TypeScript developer, multi-root workspaces are not a typical requirement for structuring a codebase. In fact, the same is now becoming true for Go developers as well. Needing to add a workspace file just for Python is an awkward asymmetry.
Proposal
Search for Nearest Configuration File
I would propose that the Pyright/Pylance editing experience adopt an approach where
pyrightconfig.json
andpyproject.toml
files are searched by walking upward in the directory chain. Upon opening a Python file, the language service would need to decide on which project "owns" a file (though note that in some cases, a file might be owned by multiple projects).In TypeScript, this problem has been tackled in a similar fashion, walking up the spine of directories looking for
tsconfig.json
andjsconfig.json
files, rather than just consulting the workspace root. For the most part, this flexibility has worked out well for us and matches most users' intuitions. The proposal here is very similar in spirit.UI for "Find Configuration"
In addition to this, the TypeScript/JavaScript editor integration in Visual Studio Code also provides a command called
TypeScript: Go to Project Configuration (tsconfig)
.There is also an indicator for the configuration file path in the status bar, with an option to open that file.
I would suggest adopting similar UI/conventions here to make configuration more discoverable and diagnosable.