{"id":7612,"library":"pytest-console-scripts","title":"Pytest plugin for testing console scripts","description":"pytest-console-scripts is a pytest plugin designed for comprehensive testing of Python console scripts. It offers two execution modes: an 'in-process' mode for fast development iteration by running scripts within the same interpreter as pytest, and a 'subprocess' mode to simulate real-world execution environments. The library is actively maintained, with its latest version being 1.4.1, and typically follows a minor release cadence as needed for bug fixes and Python version support.","status":"active","version":"1.4.1","language":"en","source_language":"en","source_url":"https://github.com/kvas-it/pytest-console-scripts","tags":["pytest","testing","console scripts","cli"],"install":[{"cmd":"pip install pytest-console-scripts","lang":"bash","label":"Install latest version"},{"cmd":"pip install pytest-console-scripts==1.4.1","lang":"bash","label":"Install specific version"}],"dependencies":[{"reason":"Core testing framework that this plugin extends.","package":"pytest","optional":false},{"reason":"Minimum required Python version for 1.4.1 is 3.8.","package":"Python","optional":false}],"imports":[{"note":"The `script_runner` is provided as a pytest fixture and does not require an explicit import in test files, though it can be type-hinted if desired: `from pytest_console_scripts import ScriptRunner`","symbol":"script_runner","correct":"def test_my_script(script_runner): ..."}],"quickstart":{"code":"import pytest\nimport subprocess\nimport sys\n\n# Create a dummy console script and setup.py for demonstration\n# In a real project, these would exist in your project structure\n\n# --- my_package/setup.py ---\n# from setuptools import setup, find_packages\n# setup(\n#     name='my_package',\n#     version='0.1.0',\n#     packages=find_packages(),\n#     entry_points={\n#         'console_scripts': [\n#             'my-script=my_package.main:main'\n#         ]\n#     },\n# )\n\n# --- my_package/main.py ---\n# import sys\n# def main():\n#     if len(sys.argv) > 1 and sys.argv[1] == '--hello':\n#         print('Hello from my-script')\n#     else:\n#         print('Running my-script')\n\n# Simplified setup for a runnable quickstart within one file:\n# Create dummy files for demonstration purposes\n# A real setup would involve 'pip install -e .' in a virtual environment.\n\n# Create a temporary directory and files\nimport tempfile\nimport os\n\ndef create_dummy_project(tmp_path):\n    pkg_dir = tmp_path / \"my_package\"\n    pkg_dir.mkdir()\n    (pkg_dir / \"__init__.py\").touch()\n    (pkg_dir / \"main.py\").write_text(\n        \"\"\"\nimport sys\ndef main():\n    if len(sys.argv) > 1 and sys.argv[1] == '--hello':\n        print('Hello from my-script')\n    else:\n        print('Running my-script')\n    sys.exit(0)\n\"\"\"\n    )\n    (tmp_path / \"setup.py\").write_text(\n        \"\"\"\nfrom setuptools import setup, find_packages\nsetup(\n    name='my_package',\n    version='0.1.0',\n    packages=find_packages(),\n    entry_points={\n        'console_scripts': [\n            'my-script=my_package.main:main'\n        ]\n    },\n)\n\"\"\"\n    )\n    return tmp_path\n\n# Example Test File (e.g., test_scripts.py)\ndef test_my_console_script(script_runner, tmp_path):\n    # Set up the dummy project and install it in editable mode\n    project_root = create_dummy_project(tmp_path)\n    subprocess.run([sys.executable, '-m', 'pip', 'install', '-e', str(project_root)], check=True)\n\n    # Run the script without arguments\n    result = script_runner.run(['my-script'])\n    assert result.returncode == 0\n    assert 'Running my-script' in result.stdout\n    assert result.stderr == ''\n\n    # Run the script with an argument\n    result_hello = script_runner.run(['my-script', '--hello'])\n    assert result_hello.returncode == 0\n    assert 'Hello from my-script' in result_hello.stdout\n    assert result_hello.stderr == ''\n\n    # Test behavior in subprocess mode (optional)\n    result_subprocess = script_runner.run(['my-script'], launch_mode='subprocess')\n    assert result_subprocess.returncode == 0\n    assert 'Running my-script' in result_subprocess.stdout","lang":"python","description":"To use `pytest-console-scripts`, define your console scripts in your project's `setup.py` (or `pyproject.toml`) and install your package in editable mode (`pip install -e .`). Then, in your pytest test files, use the `script_runner` fixture to execute your scripts and assert their output and exit codes. This example demonstrates creating a minimal project structure, installing it, and testing a console script using both default (in-process) and subprocess launch modes."},"warnings":[{"fix":"Upgrade your Python environment to 3.8 or newer. If you must use Python 3.7, pin your `pytest-console-scripts` version to `<1.4.1`.","message":"Support for Python 3.7 has been dropped in version 1.4.1.","severity":"breaking","affected_versions":">=1.4.1"},{"fix":"Upgrade your Python environment to 3.7 or newer. If you must use Python 3.6, pin your `pytest-console-scripts` version to `<1.4.0`.","message":"Support for Python 3.6 was dropped in version 1.4.0.","severity":"breaking","affected_versions":">=1.4.0"},{"fix":"For scripts requiring input, consider using `inprocess` mode and `pytest-mock` to patch `builtins.input`. Example: `mocker.patch('builtins.input', return_value='<your_expected_input>')`.","message":"When testing scripts that require user input (e.g., via `input()`), mocking might not work as expected in `subprocess` mode, as the script runs in a separate Python interpreter. Mocking is generally more reliable in `inprocess` mode.","severity":"gotcha","affected_versions":"All"},{"fix":"Ensure your package is installed in editable mode (`pip install -e .`) and re-run this command after adding new entry points to ensure they are registered in the environment.","message":"During development, if you add new console scripts to your `setup.py` or `pyproject.toml`, they might not be immediately discoverable by `pytest-console-scripts` without a reinstallation.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Ensure `pytest-console-scripts` is installed in your test environment (`pip install pytest-console-scripts`) and that pytest is being run from a directory where it can discover the plugin.","cause":"The `pytest-console-scripts` plugin is not installed or not correctly discovered by pytest.","error":"pytest.FixtureLookupError: Fixture 'script_runner' not found"},{"fix":"Use `pytest-mock` to patch `builtins.input` within your test. Example: `mocker.patch('builtins.input', return_value='my_input_string')` before running the script with `script_runner`.","cause":"The test runner provides no interactive input to the script when `input()` is called, leading to a hang or an `EOFError` when trying to read from a closed stdin.","error":"Your script requires user input (e.g., using `input()`) and hangs during tests, or test fails with `EOFError`."},{"fix":"Run pytest with the `-s` flag (`pytest -s`) to disable output capturing for print statements and logs, allowing script output to appear directly in the console. The `script_runner.run` method also returns the `stdout` and `stderr` for programmatic inspection.","cause":"By default, pytest captures stdout/stderr. Output is only shown for failing tests, or when explicitly requested.","error":"Tests are failing, but the stdout/stderr from my console script isn't showing in the pytest output."}]}