Search code examples
pythonunixpipsubprocess

Subprocess - pip: pass package names dynamically


I am trying to download without installation some Python packages using pip with the shell command

$ python -m pip download --destination-directory path -r requirements.txt

What I'd like to do is to bypass the step of having a file, requirements.txt, which contains the list of packages to download and just pass the package names as parameters. Here my attempt

import sys
import subprocess
from tempfile import NamedTemporaryFile


PIP = (sys.executable, '-m', 'pip')


def download(to_dir, *pkgs):
    with NamedTemporaryFile(mode="w+", suffix=".txt") as tmp:
        tmp.write('\n'.join(pkgs))
        
        # debugging info
        tmp.seek(0)
        print(tmp.read(), tmp.name)
        tmp.seek(0)

        return PIP + ('download', '--destination-directory', to_dir, '-r', tmp.name) 


pkg = "sphinx"        # example of package
to_dir = "valid path" # download directory

cmd = download(to_dir, pkg)
print(cmd)
p = subprocess.call(cmd)
print(p)

which raises the error

sphinx
/tmp/tmpmg3inbtu.txt
('path to python3.11', '-m', 'pip', 'download', '--destination-directory', 'valid path', '-r', '/tmp/tmpmg3inbtu.txt')
ERROR: Could not open requirements file: [Errno 2] No such file or directory: '/tmp/tmpmg3inbtu.txt'

Solution

  • The temporary file is deleted immediately when the function download() returns (actually when with is ended). To prevent:

        with NamedTemporaryFile(mode="w+", suffix=".txt", delete=False) as tmp:
    

    But don't forget to delete the file yourself using os.unlink(). See https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile

    Another approach is to subprocess.call(cmd) inside with.