-3

I've got the following project tree:

SomeDir
|- script.py
|- TestDir
|  |- test.py

script.py is designed to be called in CLI and contains several classes: notably one that does the actual job and one that is a class exit_codes(Enum) that defines exit codes.

test.py is to be used by pytest and typical tests consist of calling script.py with arguments that are used to initialize a working class object. This object then does some processing and calls sys.exit(<some enumerator>.value), using the exit codes in the Enum class.

In the test I'm giving the command line to subprocess.run and assert its returncode against the desired exit code.

Thus test.py and script.py should use the same code and I'd like to do something like

from script import exit_codes

but without additional actions script cannot be found.

There are a few possibilities that works.

The typical workaround is sys.path.append using .. from __file__ (Importing files from different folder).

As suggested in comments I can also save sys.path before editing it and importing my symbols and then restore it's original value.

Eventually, as suggested in a first answer I can make script.py a module by adding __init__.py in its directory, but then I can only run pytest from this directory.

With respect to pytest, it seems that a pytest.ini file can also be used but I didn't experiment with it so far.

It could also be a combination of these technics.

What is considered as the best practice?

4
  • why don't you just import it again in your test.py Commented Jul 31 at 16:02
  • from script import exit_codes gives ModuleNotFoundError: No module named 'script' Commented Jul 31 at 16:06
  • 1
    if you are really afraid (but that means you dir trees and python projects are a mess), set sys.path then from script import exit_codes then restore sys.path to previous value Commented Jul 31 at 16:21
  • if you are scared to try, then create a duplicate of your files and then try it on your duplicate Commented Jul 31 at 19:15

1 Answer 1

1

Convert script.py into a package

Convert SomeDir into a proper Python package by introducing an init.py file and moving reusable logic (like exit_codes) into a separate module. Example:

SomeDir/
│
├── script.py               # CLI entry point
├── common.py               # contains exit_codes and other reusable logic
├── __init__.py             # makes it a package
│
└── TestDir/
    └── test.py             # tests using pytest

In common.py:

from enum import Enum

class exit_codes(Enum):
    SUCCESS = 0
    FAILURE = 1

In script.py:

import sys
from common import exit_codes

def main():
    # some processing
    sys.exit(exit_codes.SUCCESS.value)

if __name__ == '__main__':
    main()

In TestDir/test.py:

import subprocess
from common import exit_codes

def test_script_success():
    result = subprocess.run(
        ['python', '../script.py'], capture_output=True
    )
    assert result.returncode == exit_codes.SUCCESS.value

Then run pytest from the top-level directory, not inside TestDir:

cd SomeDir
pytest

This way:

  • You don’t modify sys.path
  • You avoid name collisions
  • You can easily reuse logic between script and tests
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.