Search code examples
pythonpython-2.7subprocesspytestpopen

Python 2.7 subprocess Popen returns None


I am currently working on a set of integration tests with pytest in python 2.7 that do the following:

1) run a server binary in the background on my local machine

2) send requests to the server and validate the results

3) terminate the background server process

Everything seems to be working fine, except that I am unable to terminate the server process running on my computer. Although it continues to run on my computer, Python seems to have forgotten about it; my Popen object is None.

AttributeError: 'NoneType' object has no attribute 'terminate'

Are there any thoughts on what is causing this? Am I missing something obvious?

import time
import subprocess

server_background_process_pipe = None

def setup_module():
    # Start the test server in the background
    cmd = 'bin/my_server --key1='+value1+' --key2='+value2+' &' # The '&' tells my bin to run in the background
    server_background_process_pipe = subprocess.Popen(cmd, shell=True,stderr=subprocess.STDOUT)
    print(server_background_process_pipe) # prints '<subprocess.Popen object at 0x10aabd250>'
    time.sleep(1) # Wait for the server to be ready

def test_basic_get_request():
    print(server_background_process_pipe) # prints 'None'
    response = send_request_to_server() 
    fail_if_not_as_expected(response) # Response is exactly as expected

def teardown_module():
    # kill the server that was launched in setup_module to serve requests in the tests
    # AttributeError: 'NoneType' object has no attribute 'terminate'
    server_background_process_pipe.terminate()

Extra info:

It is None even while the server process is still running. It is None while the tests are running. It runs long after the test suite finishes. If I re-run the tests, I get a message in my console that my server failed to deploy because it is already running. The tests still pass because they send requests to the server from the previous execution.

Since the server needs to run in the background, I am using the subprocess.Popen constructor directly instead of one of the convenience methods like check_output.


Solution

  • In

    def setup_module():
        …
        server_background_process_pipe = subprocess.Popen(…)
    

    server_background_process_pipe is a local variable. It's never assigned to global server_background_process_pipe so global server_background_process_pipe is always None and the code

    def teardown_module():
        server_background_process_pipe.terminate()
    

    tries to get attribute terminate from None.

    What you want is to make the initial assignment to the global variable:

    def setup_module():
        …
        global server_background_process_pipe
        server_background_process_pipe = subprocess.Popen(…)