Cannot activate Conda environment from subprocess

旧城冷巷雨未停 提交于 2020-12-31 01:42:38

问题


I am building an Electron app (running an Angular application) which acts as the User Interface for a python program underneath.

The python program uses anaconda for package management (I am using miniconda for development).

When the app boots up, it checks whether the required conda environment exists, and if not, creates it.

The following code is part of a Service which is responsible for managing the python program.

public doEnvironmentSetup() {
    let stdOutSub = new Subject<string>();
    let stdErrSub = new Subject<string>();
    let completeSubject = new Subject<string>();
    this.runningSetup = true;

    const TEMP_ENV_FILE = join(tmpdir(), "env.yml");

    return Promise.resolve()
      .then(() => {

        // Copy packaged environment.yml to TEMP_ENV_FILE

      })
      .then(() => this.electron.getApplicationStoragePath())
      .then((stor) => {

        setTimeout(() => {

          let runProcess = this.electron.childProcess.spawn("conda", ["env", "create", "--file", TEMP_ENV_FILE, "--name", CONDA_ENV_NAME], {
            cwd: stor
          });

          const stdOutReaderInterface = createInterface(runProcess.stdout);
          const stdErrReaderInterface = createInterface(runProcess.stderr);

          stdOutReaderInterface.on('line', (line) => {
            stdOutSub.next(line);
          });

          stdErrReaderInterface.on('line', (line) => {
            stdErrSub.next(line);
          });

          runProcess.on('close', (code: number) => {
            this.electron.fs.unlinkSync(TEMP_ENV_FILE);
            this.runningSetup = false;
            completeSubject.next("");
          });

        }, 2000);

        return {
          stdOut: stdOutSub,
          stdErr: stdErrSub,
          onComplete: completeSubject
        };

      });

  }

Now, when I need to run the actual python code, the piece of code which runs is (not giving the whole thing, since it is too long for our purpose here) :

        execCmd.push(
          `conda init ${this.electron.os.platform() === "win32" ? "powershell" : "bash"}`,
          `conda activate ${CONDA_ENV_NAME}`,
          // long python spawn command
          `conda deactivate`,
        )

        setTimeout(() => {

          logLineSubject.next({ out: "--- Setting up Execution Environment ---", err: "" });

          logLineSubject.next({ out: `Running in ${dir}`, err: "" });

          const cmd = execCmd.join(" && ");

          let runProcess = this.electron.childProcess.spawn(cmd, {
            detached: false,
            windowsHide: true,
            cwd: cwd,
            shell: this.getShell()
          });

          const stdOutInterface = createInterface(runProcess.stdout);
          const stdErrInterface = createInterface(runProcess.stderr);

          stdOutInterface.on('line', (line) => {
            // get this line back to the component
          });

          stdErrInterface.on('line', (line) => {
            // get this line back to the component
          });

          runProcess.on("error", (err) => {
            // get this back to the component
          });

          runProcess.on('close', (code: number) => {
            // get this line back to the component
          });

        }, 1000);

where getShell is defined as:

private getShell() {
  return process.env[this.electron.os.platform() === "win32" ? "COMSPEC" : "SHELL"];
}

However, whenever I try to run this, it comes back with:

CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.
To initialize your shell, run
    $ conda init <SHELL_NAME>
blah blah blah ...

When I try with source activate ${CONDA_ENV_NAME}, it comes back with:

/bin/bash: activate: No such file or directory

Not really sure what I am doing wrong here. Can somebody please point me in the right direction?

PS: It works with source $(conda info --root)/etc/profile.d/conda.sh, but I can't really use it since I need to support Windows as well!

Disclaimer: I am new to python/anaconda.


回答1:


I'm not sure what needs to get run in Windows Powershell, but for bash, you need to run the script that conda init configures bash to run at startup, rather than conda init. That is,

miniconda3/etc/profile.d/conda.sh

so it should be something like

execCmd.push(
    `. ${CONDA_ROOT}/etc/profile.d/conda.sh`,
    `conda activate ${CONDA_ENV_NAME}`,
    // long python spawn command
     `conda deactivate`,
)

I suspect the Windows case is running one of the .bat or .ps1 files in the condabin directory.

Alternatively, if conda is defined and you have a Python script (e.g., script.py), then you might be able to get away with using conda run, e.g.,

execCmd.push(
    `conda run -n ${CONDA_ENV_NAME} python script.py`
)

and that could potentially work cross-platform. Please note that conda run only recently added support for interactive I/O and it must be enabled with the --live-stream flag (see v4.9.0 Release Notes). Otherwise, it simply buffers everything hitting stdout/stderr and doesn't return it until the process exits.




回答2:


The main problem is that your shell (CMD in this case) is not configured to handle conda. You have to add it to your system path by providing the Miniconda/Anaconda to the PATH enviroment variable.

Check this StackOverflow Question to know how to do it.




回答3:


Update:

I was referring the & for Windows platform and I also run conda by absolute path to conda.bat instead of call global installed conda. So it is easy for user to install it:

const execPath = path.dirname(process.execPath)
// silent install Miniconda3 to this path (electron-forge resources)
const condaPath = [execPath, "resources", "Miniconda3"].join(path.sep)
const conda = [condaPath, "condabin", "conda.bat"].join(path.sep)
cmd = `${condaPath} activate ${venvPath} & python ${filename} ${arg1} ${arg2}`

I was looking for this too and I got this working just by & as mentioned here

I have the same setup using electron and conda for UI talked to python back. This is in my node:

spawn(
  `conda activate ${name} & python ${filename} ${arg1} ${arg2}`,
  { shell: true }
);

I got it working to stream stdin/out too. Make sure the shell is true.



来源:https://stackoverflow.com/questions/59328111/cannot-activate-conda-environment-from-subprocess

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!