4

I have a virtualenv named 'venv' and it is activate:

(venv)>

and I wrote codes that I'll run it in the virtualenv (main.py):

import subprocess
result = subprocess.run('python other.py', stdout=subprocess.PIPE)

but when I run main.py file:

(venv)> python main.py

subprocess does not execute the command (python other.py) in the virtualenv i.e venv

How to run subprocess command in the current virtualenv session?

9
  • 1
    tried: subprocess.run(['python','other.py'], stdout=subprocess.PIPE) ? Commented May 31, 2018 at 19:38
  • 1
    You can't invoke something in the "current" shell, if by that you mean the one that started your program. It's the parent process. Any creation of a new program is always generating a subprocess; you can't modify the parent process without its active involvement in allowing you to do so. Commented May 31, 2018 at 19:41
  • 1
    That said, your subprocess.run code is broken as-given unless you make it subprocess.run(['python', 'other.py']) or use shell=True (which you shouldn't): As given in the question, it's looking for a program called python other.py, with a space in its filename, not looking for a program called python and passing it an other.py argument. Commented May 31, 2018 at 19:43
  • 1
    @hadi, ...you really, really shouldn't use shell=True. Easy to get security bugs if you're passing arguments to a program started that way, unless being very careful. Commented May 31, 2018 at 19:56
  • 1
    See the warning in the Python docs at docs.python.org/2/library/… Commented May 31, 2018 at 19:57

2 Answers 2

5

A child process can't run commands in its parent process without that process's involvement.

This is why ssh-agent requires usage as eval "$(ssh-agent -s)" to invoke the shell commands it emits on output, for example. Thus, the literal thing you're asking for here is impossible.

Fortunately, it's also unnecessary.


virtualenvs use environment variables inherited by child processes.

This means that you don't actually need to use the same shell that has a virtualenv activated to start a new Python interpreter intended to use the interpreter/libraries/etc. from that virtualenv.


subprocess.run must be passed a list, or shell=True must be used.

Either do this (which is better!)

import subprocess
result = subprocess.run(['python', 'other.py'], stdout=subprocess.PIPE)

Or this (which is worse!)

import subprocess
result = subprocess.run('python other.py', stdout=subprocess.PIPE, shell=True)
Sign up to request clarification or add additional context in comments.

4 Comments

why shell=True is worse?
While this does work, sys.executable is usually a better answer.
@hadi It can open up security vulnerabilities very easily. For example, if you use this code: import subprocess;name = input("Name? ");subprocess.run(f"echo Hi, {name}", shell=True) then someone can tell the user to type person;sudo rm -Rf /* as their name which will be a valid command since the entire string put together is echo person;sudo rm -Rf /* (sudo rm -Rf /* in UNIX will delete every file possible on the computer, recursively). They can't do that with shell=False since it's not actually run in a shell, so that can't happen.
@abarnert, I agree. Would you object if I were to edit that into my answer, or would that make it overly duplicative of yours? (Though, being community wiki, it's not really "my" answer, and should be considered amenable to editing without any kind of permission needed)
1

If you want to run a script with the same Python executable being used to run the current script, don't use python and rely on the path being set up properly, just use sys.executable:

A string giving the absolute path of the executable binary for the Python interpreter, on systems where this makes sense.

This works if you executed the script with python myscript.py relying on the active virtualenv's PATH. It also works if you executed the script with /usr/local/bin/python3.6 to ignore the PATH and test your script with a specific interpreter. Or if you executed the script with myscript.py, relying on a shbang line created at installation time by setuptools. Or if the script was run as a CGI depending on your Apache configuration. Or if you sudod the executable, or did something else that scraped down your environment. Or almost anything else imaginable.1

As explained in Charles Duffy's answer, you still need to use a list of arguments instead of a string (or use shell=True, but you rarely want to do that). So:

result = subprocess.run([sys.executable, 'other.py'], stdout=subprocess.PIPE)

1. Well, not quite… Examples of where it doesn't work include custom C programs that embed a CPython interpreter, some smartphone mini-Python environments, old-school Amiga Python, … The one most likely to affect you—and it's a pretty big stretch—is that on some *nix platforms, if you write a program that execs Python by passing incompatible names for the process and arg0, sys.executable can end up wrong.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.