Why isn't subprocess.Popen.kill() doing anything?

Viewed 37

I am following the Google Cloud Functions python testing example here: https://cloud.google.com/functions/docs/testing/test-http

import os
import subprocess
import uuid

import requests
from requests.packages.urllib3.util.retry import Retry


def test_args():
    name = str(uuid.uuid4())
    port = os.getenv('PORT', 8080)  # Each functions framework instance needs a unique port

    process = subprocess.Popen(
      [
        'functions-framework',
        '--target', 'hello_http',
        '--port', str(port)
      ],
      cwd=os.path.dirname(__file__),
      stdout=subprocess.PIPE
    )

    # Send HTTP request simulating Pub/Sub message
    # (GCF translates Pub/Sub messages to HTTP requests internally)
    BASE_URL = f'http://localhost:{port}'

    retry_policy = Retry(total=6, backoff_factor=1)
    retry_adapter = requests.adapters.HTTPAdapter(
      max_retries=retry_policy)

    session = requests.Session()
    session.mount(BASE_URL, retry_adapter)

    name = str(uuid.uuid4())
    res = session.post(
      BASE_URL,
      json={'name': name}
    )
    assert res.text == 'Hello {}!'.format(name)

    # Stop the functions framework process
    process.kill()
    process.wait()

After running pytest against this test file, the test passes and the code exits nearly immediately, but it doesn't appear to have killed the process:

± ps -ef | grep functions
thoraxe    11661    1985 49 20:30 pts/0    00:00:02 /home/thoraxe/.pyenv/versions/3.9.13/envs/avogadro-trainer-3-9-13/bin/python3.9 /home/thoraxe/.pyenv/versions/3.9.13/envs/avogadro-trainer-3-9-13/bin/functions-framework --target train --port 8080
thoraxe    11684   11661  0 20:30 pts/0    00:00:00 /home/thoraxe/.pyenv/versions/3.9.13/envs/avogadro-trainer-3-9-13/bin/python3.9 /home/thoraxe/.pyenv/versions/3.9.13/envs/avogadro-trainer-3-9-13/bin/functions-framework --target train --port 8080

I'm using Python 3.9.13 in a virtual environment on Fedora.

As this is sample code from Google, I'd expect it to work, but something is definitely not working here. Can someone suggest what I might be doing wrong?

1 Answers

When a Python assertion fails, the program exits immediately and does not continue. The kill/wait are never actually executed unless the test is successful. This is a major bummer because the function framework will continue to run in the background and new code changes aren't apparently picked up on subsequent pytest runs.

Using a different wrapper framework like https://github.com/okken/pytest-check solves the problem because all steps will be performed, even if there are failures.

However, note that legitimate Python failures/errors/explosions will still result in the functions framework not properly exiting.

Related