Search code examples
pythonlinuxsubprocesspytestgithub-actions

subprocess.run does not execute pytest command as expected, on actions workflow it DOES NOT add/take into account cli params


Basic working example:

Actions workflow:

name: Manual_Run

on:
  workflow_dispatch:
    inputs:
      mark:
        description: 'Which tests are executed'
        default: ''
        required: false
        type: string

  workflow_call:
    inputs:
      env:
        description: 'Environment on which the tests are executed'
        default: 'Prod'
        required: true
        type: string

permissions:
  id-token: write
  contents: read

jobs:

  build:

    runs-on: ubuntu-latest

    env:
      INPUT_MARK: ${{ github.event.inputs.mark }}

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup python 3.10
      uses: actions/setup-python@v5
      with:
        python-version: '3.10'
        cache: 'pip'

    - name: Install python requirements
      run: pip install -r requirements.txt
    - name: Run Pytest
      run: python run_tests.py

run_tests.py is

import subprocess



if __name__ == "__main__":
    cmd = ['pytest', '-m', 'sum']
    subprocess.Popen(cmd, text=True, shell=True)

Targeted tests (the only tests in the repo, test_feats.py)

import pytest


@pytest.mark.sum
def test_sum():
    assert 1 + 1 == 2

@pytest.mark.minus
def test_minus():
    assert 1 - 1 == 0

If i run run_tests.py locally on windows OS, everything works as expected:

collected 2 items / 1 deselected / 1 selected
.
.
.
================= 1 passed, 1 deselected, 2 warnings in 0.15s =================

But on remote/actions workflow/ ubuntu latest OS:

collected 2 items
.
.
.
======================== 2 passed, 2 warnings in 0.03s =========================

If somebody has any idea on why it is happanening please lend me a hand, I legit worked through everything i could find, but there's no logical reason for it to behave as such


Solution

  • The problem has to do with different handling of the arguments for subprocess.Popen on Windows and Posix, if you are using shell=True.

    From the documentation:

    On POSIX with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of:

    Popen(['/bin/sh', '-c', args[0], args[1], ...])

    This means that you either should use shell=False (which is the default), or, if you need it to be shell=True for some reason, you need to handle it differently in Windows and Posix:

    import sys
    
    if __name__ == "__main__":
        cmd = ['pytest', '-m', 'sum']
        if sys.platform != "win32":
            cmd = [" ".join(cmd)]
        subprocess.Popen(cmd, text=True, shell=True)
    

    otherwise your arguments will be treated as shell arguments instead of pytest arguments.

    Under Windows, the arguments are handled differently - the first item is handled as the command, and the rest as arguments for this command, therefore you should separate them in this case.