-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Sort environments by assumed usefulness
This is a design document for a new environment sorting logic for the quickpick that appears when selecting an interpreter (#16520).
Currently, environments are sorted by Python version > architecture (if any) > company name (if any). This hampers discoverability, and we believe that sorting them with the most useful ones at the top will improve the environment selection experience.
"Usefulness" would be determined by environment type:
- Local environments (e.g.
venv
) will be closest to the top of the quickpick; - Globally-installed environments (e.g.
$WORK_ON
orconda
) will follow; - Global installs (e.g.
/usr/bin/python3.9
) will be last.
Within each category, sort by Python versions in descending order (most recent first), with some Conda-related special cases:
- If there are several types of globally-installed environment available, Conda ones should have lowest priority within their Python version subgroup;
- When having multiple Conda environments with the same Python version, the
base
environment should be last within its Python version subgroup.
The work done here covers the quickpick that appears when the Python: Select Interpreter
command is triggered. This work will also impact the auto-selection process, which will be discussed once the sorting logic gets merged.
An option could be to still sort by Python version only, but by descending order instead of the current ascending logic. Even though it would simpler than what we decided on, it doesn't differentiate between environment types, and doesn't suggest that local environments would be more preferable to the user.
The quickpick gets populated by IInterpreterSelector.getSuggestions
:
public async getSuggestions(resource: Resource, ignoreCache?: boolean): Promise<IInterpreterQuickPickItem[]> {
const interpreters = await this.interpreterManager.getInterpreters(resource, {
onSuggestion: true,
ignoreCache,
});
interpreters.sort(this.interpreterComparer.compare.bind(this.interpreterComparer));
return Promise.all(interpreters.map((item) => this.suggestionToQuickPickItem(item, resource)));
}
Environments are retrieved from interpreterManager.getInterpreters
, but the real sorting magic happens in interpreters.sort(this.interpreterComparer.compare.bind(this.interpreterComparer))
, which is an instance of the InterpreterComparer
class implementing the IInterpreterComparer
interface:
export interface IInterpreterComparer {
compare(a: PythonEnvironment, b: PythonEnvironment): number;
}
The comparison logic would follow this algorithm:
- Check the environment type: local > global environment > global install;
- Then, compare by descending Python version;
- Apply Conda-specific rules if necessary;
- Finally, compare with the rest of the environment info: architecture, company name, and environment name.
Environment types will be labeled as follow:
Environment Type | Sorted Type |
---|---|
venv | Local/Globally-installed |
Conda | Globally-installed |
virtualenv | Globally-installed |
virtualenvwrapper | Globally-installed |
Pipenv | Globally-installed |
Poetry | Globally-installed |
pyenv | Global |
Windows Store | Global |
System | Global |
Global | Global |
Unknown | Global |
We currently use a singleton of the IInterpreterHelper
helper class to retrieve the display name of an interpreter, which we still need necessary for the last sorting rule (compare env info). As such, we will need to expose the new comparison logic in a class that implements the IInterpreterComparer
interface.
This helper will also be used to retrieve the path of the current workspace to determine whether a venv
environment is local or globally installed.
No externally-facing APIs will be exposed.
This sorting algorithm will be gated behind an experiment, so we can see how it impacts our current metrics. Since we know that we want to roll out this change to all users (instead of just testing hypotheses), the experiment will have a quick progression.
No new telemetry will be added as part of this work.
Unit tests will be written for the comparison function. Tests should also be added to interpreterSelector.getSuggestions
to make sure that the sorting logic works for both experiment groups.