Search code examples
pythonsubprocesspopenwinsock2

Python Popen hanging during second run of script


# @file Test.py
# Requirement 1
def requirement1():
    '''
    Requirement 1:
    The server will accept command line arguments of:
    -a / --address <ip address> -IP address to listen on(default: all or default interface)
    -p / --port <port> -Port to listen on(default: 12345)
    '''

    log = "Requirement 1:\n"

    args1 = ["", "-a 127.0.0.1", "--address 127.0.0.1"]
    args2 = ["", "-p 7777", "--port 7777"]

    processName = f"..\exe\server.exe"

    stdout.write(f"    [•] {processName} {args1[2]} {args2[2]}\n")
    someProcess = None
    someProcess = subprocess.Popen([processName, args1[2], args2[2]], stdout = subprocess.PIPE)
    sleep(1) # Wait for slow ass WiNdOws to get resources for the process

    # If process failed to start
    if someProcess is None:
        log += f"    [F] {processName} {args1[2]} {args2[2]}\n"
        return log

    lines = someProcess.stdout.readlines()
    for line in lines:
        line = cleanLine(line)
        stdout.write(f"{line}\n")
        if "ERROR" in line:
            log += f"    [F] {processName} {args1[2]} {args2[2]}\n"
        elif "ready" in line:
            log += f"    [P] {processName} {args1[2]} {args2[2]}\n"
        else:
            continue

    someProcess.kill()

    return log

I am doing a unit test on a server I wrote myself: Client; start, ask for input, send string to server, wait up to 1 second for server to reply with same string, or time out after 1 second. Close client. Server; start, wait for connection, return string given, wait for connection until user sends Ctrl+C in Command Prompt.

While testing server.exe:

If I someProcess = subprocess.Popen([processName, args1[2], args2[2]], stdout = subprocess.PIPE) after restarting my Windows10 machine, lines = someProcess.stdout.readlines() will work as expected and the rest of the script runs just fine grabbing the lines from the stdout of the spawned process from Popen().

However, when I force quit via a Ctrl+C keyboard interrupt in the Command Prompt running my Test.py script, the NEXT time (and every time after) I run my script lines = someProcess.stdout.readlines() will hang.

Measures I've taken to investigate and try and clear this:

Restarted machine and placed everything for the someProcess variable in a context manager like with subprocess.Popen([processName, args1[2], args2[2]], stdout = subprocess.PIPE) as someProcess: thinking that with would control all resources and clean up for me should the Test.py script quit in ANY manner. Run the script, force quit, can't rerun the scritp without the same problem occurring. Turns out with doesn't clean up well. (So is this Python failing to clean up after itself?)

I've gone to Task Manager and used ProcessExplorer to find the process started with Popen() and end that task/kill process. This however does not allow me to rerun the script with intended results. The readlines() call still hangs. I have to restart my entire machine. (So is this Windows failing to clean up ALL resources after a process exits ungracefully?)

Ok, maybe the port is stuck? Lets do netstat -ano real quick. Nope, no port being used by zombies or PIDs that my script started. Also, the server would throw the appropriate WSA error: 10048 and I would know that the port I told it to bind() to is taken. Regardless, someProcess.stdout.readlines() should get that error from the spawned process and then print to the Test.py stdout the server's 10048 error. However, readlines() hangs and never returns.

What I think is happening:

  1. Run Test.py, everything is fine.
  2. Rerun Test.py, force quit with Ctrl-C.
  3. (This is when my problem starts) Rerun Test.py, script runs, gets to the someProcess.stdout.readlines() and is blocked endlessly waiting for something to read. However, there is nothing to read because the stream,pipe for the someProcess THIS run and EVERY run after force quitting Test.py is broken?

QUESTION: How do I make sure that whenever I call ```subprocess.Popen()```` it will return a process with which I can safely work with? Regardless of previous runs of Test.py and how it was terminated.


Solution

  • A thorough read of this https://bugs.python.org/issue43346 and a lengthy conversation with my colleague has satisfied my question.

    Long story short: subprocess.run() and subprocess.Popen() have known blocking issues when threads close on Windows systems and is being tracked by Python.org.