You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While the extension supports discovery of virtual environments in a number of cases, there are still many ways in which they can remain undiscovered. I personally ran into this the other day using the Python-distributed venv, but it applies equally to virtualenv, pyenv, pipenv, anaconda, etc.
Automatic discovery is doable if we can keep in sync with the facilities for discovering environments of the different tools. However, there are limits to that, including the following contributing factors (see #2142):
environment variables
kind of shell (login, interactive, etc.)
Note that this relates closely to the issues of getting info from the terminal and of running subprocesses with the right environment variables.
Basically we need to know what kind of shell we use when we run subprocesses to know if we will pick up e.g. environment variables as set by folks in their .bashrc for pyenv (or LC_ALL for Black as another motivating example).
Note that these overlap substantially. So it isn't just a matter of choosing one. :)
Idea 1 (Single class for each type)
Have a single interface that does everything for a type of Python environment.
I.e. IEnvironment (concrete implementation for Conda, name CondaEnvironment class):
Listing conda environments
Activation of conda environments
Creation of conda environments
Identifying conda environments
etc.
IEnvironment:
GetInterpreters() : Promise<PythonInterpreter[]>
Gets a list of interpreters for the type of this implementation (e.g. Conda, Virtua, etc).
GetActivationScripts(shell: TerminalShellType): Promise<string[]>
Gets the activation scripts.
GetInterpreterType(pythonPath:string): Promise<InterpretrerType|undefined>
Gets the type of the interpreter.
GetInterpreterType(interpreter: PythonInterpreter): Promise<InterpreterType|undefined>
Gets the type of the interpreter.
CreateEnvironment(name:string, path?:string): Promise
Creates and environment with a given name and optionally in a specific path (not sure we need the second arg, to be determined).
Note:
Listing environments will return a simple object that defines an environment (with readonly attributes).
Much like a repository pattern.
Idea 2 (Composition)
Instead I'm for composition. I.e. IEnvironmentFinder or IEnvironmentProvider, IEnvironmentCreator, IEnvironmentActivator, etc.
Similar to what we have today, but refactor it to make it more maintainable.
For instance GlobalWorkspaceLocator might have to be broken up into GlobalPyEnvEnvLocator, GlobalVirtualEnvLocator, CondaLocator, etc...
Note:
Listing environments will return a simple object that defines an environment (with readonly attributes).
Much like a repository pattern, but instead of having a single interface to do everything we break it up.
I.e. we compose using lightweight interfaces..
Idea 3
Proposal by Derek Keeler (@d3r3kk) that we go with interfaces such as IInterpreter that'll have members such as:
Type
Path
GetVersion()...
GetBitness()...
GetDisplayName()
GetInterpreters()
CreateInterpreter()
This is similar to the first approach but each instance of returning simple data objects we return objects that have can in turn do stuff.
Like Option 2, but have one class per environment type that implements the granular interfaces.
Idea 5
3 tiers of info:
distro -- e.g. who provided the Python executable (e.g. python.org, conda, ubuntu)
environment
interpreter
Here's a rough (and incomplete) sketch the should give you a sense of where I'd put info and functionality.
exportinterfaceIPythonDistribution{readonlyname: NonEmptyString;readonlyversion: semver.SemVer;readonlypublisher: NonEmptyString;readonlysupportedVersions: ISupportedPythonVersions;readonlyenvs: IPythonEnvironments;/** * Add a new virtual environment using the distro. */createVenv(spec: IVenvSpec,version?: IPythonVersion): IPythonEnvironment;}exportinterfaceISupportedPythonVersions{readonly[index: number]: ISupportedPythonVersion;}exportinterfaceISupportedPythonVersionextendsIPythonVersion{readonlyinstalled: boolean;/** * Ensure that the Python version is installed. * * If that version of Python is directly usable then a new * environment is returned. Otherwise null is returned. */ensureInstalled(): IPythonEnvironment|null;}//================================// Python environmentsexportenumPythonEnvKind{Unknown,Global,Venv,VirtualEnv,PyEnv,PipEnv}exportinterfaceIPythonEnvironment{readonlyname: NonEmptyString;readonlykind: PythonEnvKind;readonlydistro: IPythonDistribution;readonlyinterpreter: IPythonInterpreter;readonlyspec?: IVenvSpec;readonlypackages: IPythonPackages;/** * Add a new virtual environment based on this env. */createVenv(IVenvSpec): IPythonEnvironment;// TODO: activation/** * Remove this environment. */destroy(): void;}exportinterfaceIPythonEnvironments{readonly[index: number]: IPythonEnvironment;}exportinterfaceIVenvSpec{readonlyname: NonEmptyString;readonlydirname?: NonEmptyString;}//================================// Python packagesexportinterfaceIPythonPackages{lookUp(string|IPythonPackageSpec): ISupportedPythonPackage;listAll(): IPythonPackage[];}exportinterfaceIPythonPackageSpec{readonlyname: NonEmptyString;readonlyversion: semver.SemVer;}exportinterfaceISupportedPythonPackageextendsIPythonPackageSpec{install(): IPythonPackage[];}exportinterfaceIPythonPackageextendsIPythonPackageSpec{uninstall(): void;}//================================// Python interpreters/** * A wrapper around a single Python interpreter. */exportinterfaceIPythonInterpreter{readonlyfilename: NonEmptyString;readonlyversion: IPythonVersion;readonlyenv: IPythonEnvironment;}exportenumPythonReleaseLevel{Alpha,Beta,Candidate,Final}exportinterfaceIPythonRelease{readonlyreleaseLevel: PythonReleaseLevel;readonlyserial: int;toString(): string;}exportinterfaceIPythonVersion{readonlymajor: int;readonlyminor: int;readonlymicro?: int;readonlyrelease?: IPythonRelease;toString(): string;}
Idea 6
add implementations of the interfaces (e.g. from idea 5) to a global registry
*add helper functions that pick the right implementation from the registry based on some criteria
replace existing environment-specific providers (spread throughout the code base) with generic providers that pull from the global registry for their specific needs (i.e. the providers/services have no knowledge of any specific environment; they are written strictly against the generic interfaces)
Activity
ericsnowcurrently commentedon Jul 31, 2018
Also see #1479.
brettcannon commentedon Jul 31, 2018
Basically we need to know what kind of shell we use when we run subprocesses to know if we will pick up e.g. environment variables as set by folks in their
.bashrcfor pyenv (orLC_ALLfor Black as another motivating example).[-]Improve discovery of virtual environments.[/-][+]Analyze design of environment discovery/management[/+][-]Analyze design of environment discovery/management[/-][+]Analyze design of environment management[/+]DonJayamanne commentedon Sep 20, 2018
Requirements:
DonJayamanne commentedon Sep 20, 2018
Ideas
Note that these overlap substantially. So it isn't just a matter of choosing one. :)
Idea 1 (Single class for each type)
Have a single interface that does everything for a type of Python environment.
I.e.
IEnvironment(concrete implementation forConda, nameCondaEnvironmentclass):etc.
IEnvironment:
Gets a list of interpreters for the type of this implementation (e.g. Conda, Virtua, etc).
Gets the activation scripts.
Gets the type of the interpreter.
Gets the type of the interpreter.
Creates and environment with a given name and optionally in a specific path (not sure we need the second arg, to be determined).
Note:
Idea 2 (Composition)
Instead I'm for composition. I.e.
IEnvironmentFinder or IEnvironmentProvider,IEnvironmentCreator,IEnvironmentActivator, etc.Similar to what we have today, but refactor it to make it more maintainable.
For instance GlobalWorkspaceLocator might have to be broken up into GlobalPyEnvEnvLocator, GlobalVirtualEnvLocator, CondaLocator, etc...
Note:
Idea 3
Proposal by Derek Keeler (@d3r3kk) that we go with interfaces such as IInterpreter that'll have members such as:
This is similar to the first approach but each instance of returning simple data objects we return objects that have can in turn do stuff.
Derek Keeler (@d3r3kk) please feel free to update accordingly.
Idea 4
Like Option 2, but have one class per environment type that implements the granular interfaces.
Idea 5
3 tiers of info:
Here's a rough (and incomplete) sketch the should give you a sense of where I'd put info and functionality.
Idea 6
*add helper functions that pick the right implementation from the registry based on some criteria
Idea 7
<TBD>
brettcannon commentedon Sep 20, 2018
I was actually thinking option 1 because as you outlined in the requirements you really only need 4 methods, but I'm not attached to the idea.
DonJayamanne commentedon Sep 25, 2018
Derek Keeler (@d3r3kk) Eric Snow (@ericsnowcurrently)
Please could you review the above options & update if necessary.
DonJayamanne commentedon Sep 26, 2018
Here's an incremental approach to solving this issue: