prevent unexpected stdin reads and lock in subprocess

2024/10/18 12:55:29

A simple case I'm trying to solve for all situations. I am running a subprocess for performing a certain task, and I don't expect it to ask for stdin, but in rare cases that I might not even expect, it might try to read. I would like to prevent it from hanging in that case.

here is a classic example:

import subprocess
p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"])
p.wait()

This will hang forever. I have already tried adding

stdin=open(os.devnull)

and such..

will post if I find a valuable solution. would be enough for me to receive an exception in the parent process - instead of hanging on communicate/wait endlessly.

update: it seems the problem might be even more complicated than I initially expected, the subprocess (in password and other cases) reads from other file descriptors - like the /dev/tty to interact with the shell. might not be as easy to solve as I thought..

Answer

If your child process may ask for a password then it may do it outside of standard input/output/error streams if a tty is available, see the first reason in Q: Why not just use a pipe (popen())?

As you've noticed, creating a new session prevents the subprocess from using the parent's tty e.g., if you have ask-password.py script:

#!/usr/bin/env python
"""Ask for password. It defaults to working with a terminal directly."""
from getpass import getpasstry:_ = getpass()
except EOFError:pass # ignore
else:assert 0

then to call it as a subprocess so that it would not hang awaiting for the password, you could use start_new_session=True parameter:

#!/usr/bin/env python3
import subprocess
import syssubprocess.check_call([sys.executable, 'ask-password.py'],stdin=subprocess.DEVNULL, start_new_session=True,stderr=subprocess.DEVNULL)

stderr is redirected here too because getpass() uses it as a fallback, to print warnings and the prompt.

To emulate start_new_session=True on Unix on Python 2, you could use preexec_fn=os.setsid.

To emulate subprocess.DEVNULL on Python 2, you could use DEVNULL=open(os.devnull, 'r+b', 0) or pass stdin=PIPE and close it immediately using .communicate():

#!/usr/bin/env python2
import os
import sys
from subprocess import Popen, PIPEPopen([sys.executable, 'ask-password.py'],stdin=PIPE, preexec_fn=os.setsid,stderr=PIPE).communicate() #NOTE: assume small output on stderr

Note: you don't need .communicate() unless you use subprocess.PIPE. check_call() is perfectly safe if you use an object with a real file descriptor (.fileno()) such as returned by open(os.devnull, ..). The redirection occurs before the child process is executed (after fork(), before exec()) -- there is no reason to use .communicate() instead of check_call() here.

https://en.xdnf.cn/q/72616.html

Related Q&A

Regex split string by last occurrence of pattern

I am using regex to split a string <book name> by <author name> into book and author names.re.split(r\bby\b, text, 0, re.I)But problem arises when the book name contains the word "by&q…

How to write a unit-test where each test case has different input but does the same?

I need to create a unit-test for some python class. I have a database of inputs and expected results which should be generated by the UUT for those inputs.Here is the pseudo-code of what I want to do:f…

Unable to pass authentication information to NetSuites REST interface using Python

Ive been trying to get a NetSuite Restlet to work with Python 3.3 using urllib, but I cant seem to get the authorization to take and continually return a urllib.error.HTTPError: HTTP Error 401: Authori…

letsencrypt failed with ImportError: No module named interface

Im using Amazon linux, and I followed some steps for using letsencrypt that easily found in google search, but all it fails with:Error: couldnt get currently installed version for /root/.local/share/le…

ModuleNotFoundError: No module named skimage.util.montage

Im trying to import montage2d module from scikit-image:from skimage.util.montage import montage2dBut this error popped up:ModuleNotFoundError: No module named skimage.util.montageIm pretty sure I insta…

upgrading python module within code

My question relates to this question: Installing python module within code, but involves upgrading the module.Ive triedpackages=[apscheduler,beautifulsoup4,gdata]def upgrade(packages):for package in pa…

Django - 403 Forbidden CSRF verification failed

I have a contact form in Django for my website and when I was testing it locally it was working fine but now when I try to submit my contact form "live" it always comes up with 403 Forbidden …

Python model object validation

Im writing an interface to be used by two applications. This interface should use some DoSomethingRequest and DoSomethingResponse classes to do the communication.Is there any library that does some mod…

Cassandra 1.2 inserting/updating a blob column type using Python and the cql library

IntroI have a blob column on a Cassandra 1.2 column family, the table is defined as follows:CREATE TABLE objects (id text,obj blob,PRIMARY KEY (id) );The problem:The problem is that when I…

Using python with subprocess Popen

I am struggling to use subprocesses with python. Here is my task:Start an api via the command line (this should be no different than running any argument on the command line) Verify my API has come …