{"id":688,"library":"pytest-xdist","title":"pytest-xdist","description":"The pytest-xdist plugin extends pytest with new test execution modes, primarily designed for distributing tests across multiple CPUs or hosts to significantly speed up test execution. It also offers features like running tests in a Python subprocess and formerly supported remote SSH execution and `--looponfail` mode. Currently at version 3.8.0, the library is actively maintained with regular feature updates and bug fixes.","status":"active","version":"3.8.0","language":"python","source_language":"en","source_url":"https://github.com/pytest-dev/pytest-xdist","tags":["pytest","testing","parallel-testing","distributed-testing","test-runner"],"install":[{"cmd":"pip install pytest-xdist","lang":"bash","label":"Standard installation"},{"cmd":"pip install pytest-xdist[psutil]","lang":"bash","label":"With psutil for logical CPU detection"}],"dependencies":[{"reason":"pytest-xdist is a plugin for pytest and requires it to function.","package":"pytest"},{"reason":"Optional dependency to accurately detect the number of logical CPU cores for the '-n logical' option.","package":"psutil","optional":true}],"imports":[{"note":"pytest-xdist is a pytest plugin and primarily exposes its functionality via command-line arguments to the 'pytest' executable. Direct Python imports of its core functionality by users are generally not required for standard usage.","symbol":"pytest-xdist","correct":"pytest -n auto"}],"quickstart":{"code":"# Assuming you have pytest and pytest-xdist installed and tests in a 'tests/' directory\n# Create a simple test file, e.g., tests/test_example.py\n# import time\n# def test_fast():\n#     assert True\n# def test_medium():\n#     time.sleep(0.5)\n#     assert True\n# def test_slow():\n#     time.sleep(1)\n#     assert True\n\nimport os\nimport subprocess\n\ndef run_pytest_xdist(num_processes):\n    cmd = ['pytest', '-n', str(num_processes)]\n    print(f\"Running: {' '.join(cmd)}\")\n    result = subprocess.run(cmd, capture_output=True, text=True)\n    print(result.stdout)\n    if result.stderr:\n        print(\"STDERR:\", result.stderr)\n    return result.returncode\n\n# Example usage: run tests on all available CPU cores\n# A real-world scenario would just be `pytest -n auto` from the command line\nif __name__ == '__main__':\n    # Ensure pytest is installed for this example to run locally in a script\n    try:\n        import pytest\n    except ImportError:\n        print(\"pytest is not installed. Please install with 'pip install pytest'.\")\n        exit(1)\n\n    print(\"\\n--- Running tests with pytest -n auto ---\")\n    # In a real shell, you would simply type: pytest -n auto\n    # For programmatic execution, we demonstrate the subprocess call\n    exit_code = run_pytest_xdist('auto')\n    if exit_code == 0:\n        print(\"Tests passed successfully with -n auto.\")\n    else:\n        print(f\"Tests failed with -n auto. Exit code: {exit_code}\")\n\n    print(\"\\n--- Running tests with pytest -n 2 ---\")\n    # In a real shell, you would simply type: pytest -n 2\n    exit_code = run_pytest_xdist(2)\n    if exit_code == 0:\n        print(\"Tests passed successfully with -n 2.\")\n    else:\n        print(f\"Tests failed with -n 2. Exit code: {exit_code}\")\n","lang":"python","description":"After installation, `pytest-xdist` is typically used directly from the command line by adding the `-n` or `--numprocesses` option to your `pytest` command. The most common use is `pytest -n auto` to automatically detect and utilize all available physical CPU cores, or `pytest -n <NUM>` to specify a fixed number of worker processes. This example demonstrates how you would invoke it programmatically via `subprocess`, though direct command-line execution is the primary quickstart."},"warnings":[{"fix":"For debugging, run tests without `-n` (i.e., sequentially). For inspecting output, rely on logging or `--capture=fd` (file descriptor capture) where available and aggregated at the end of the run.","message":"The `-s` or `--capture=no` option for disabling pytest's output capture does not work with `pytest-xdist`. This means you will not see real-time stdout/stderr output from workers, and debugging with tools like `PDB` is generally not supported for tests running in parallel workers.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure parameterized values have a consistent order by converting them to lists or sorting them (e.g., `list({'a', 'b'})` or `sorted({'a', 'b'})`).","message":"Tests using `pytest.mark.parametrize` with unordered iterables (e.g., `set`) can lead to inconsistent test collection order across workers, causing `pytest-xdist` to fail.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Migrate away from using `--looponfail` (which is already deprecated). For `--rsyncdir`, consider alternative methods for syncing code to remote environments, as modern CI/CD practices often handle this differently. The `--boxed` argument was also deprecated and removed in 3.0.0, use `pytest-forked` and `--forked` instead.","message":"The `--looponfail` and `--rsyncdir` command-line arguments and their corresponding configuration variables (`looponfailroots`, `rsyncdirs`) are deprecated. They are scheduled for removal in `pytest-xdist 4.0`.","severity":"deprecated","affected_versions":"3.0.0 onwards"},{"fix":"For fixtures that truly need to run once per entire test session across all workers, explicit inter-process communication or shared storage (e.g., temporary files) is required, often with a 'first worker initializes, others wait' pattern. Consider using plugins like `pytest-shared-session-scope`.","message":"pytest's `scope='session'` fixtures are executed once per *worker process*, not once for the entire `pytest-xdist` run across all workers. This can lead to unexpected resource setup/teardown behavior and state issues if not managed carefully.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If strict `--exitfirst` behavior is critical for your workflow, you might need to pin `pytest-xdist` to version `3.3.1` or earlier. Alternatively, check the latest `pytest-xdist` documentation or issue tracker for updates or workarounds regarding this behavior.","message":"Users have reported that `--exitfirst` (`-x`) (which stops the test session on the first failure) does not function as expected with `pytest-xdist` versions 3.4.0 and higher. Tests might continue running in other workers despite a failure.","severity":"breaking","affected_versions":">=3.4.0"},{"fix":"Investigate logs for the specific tests running at the time of the timeout to identify the hanging test or process. Consider adding more granular timeouts at the test or fixture level (e.g., using `pytest-timeout`) if a specific test is the culprit. Review system resource utilization during the test run if the entire environment becomes unresponsive. Ensure external services or resources accessed by tests are stable and responsive.","message":"A test run resulted in a 'TIMEOUT'. This can be caused by various factors, including an individual test hanging indefinitely (e.g., due to an infinite loop, a deadlock, or waiting for an unresponsive external resource), or the overall test suite exceeding the configured maximum execution time in the CI environment.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T17:50:21.225Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"pip install pytest-xdist","cause":"The `pytest-xdist` plugin is not installed, so pytest does not recognize its command-line options like `-n` or `--dist`.","error":"pytest: error: unrecognized arguments: -n auto"},{"fix":"Remove `--looponfail` from your pytest command. Consider using alternative tools like `pytest-watch` for similar functionality.","cause":"The `--looponfail` feature, which automatically re-runs failing tests, was removed in `pytest-xdist` version 3.0.0.","error":"pytest: error: unrecognized arguments: --looponfail"},{"fix":"Remove the `--tx` option and explore alternative tools or methods for remote test execution.","cause":"Remote execution via the `--tx` option was removed in `pytest-xdist` version 3.0.0.","error":"pytest: error: unrecognized arguments: --tx ssh=user@host:python=python3"},{"fix":"Upgrade your Python installation to version 3.8 or newer to meet the library's requirements.","cause":"The Python version installed on your system is older than 3.8, which is the minimum requirement for `pytest-xdist`.","error":"ERROR: Package 'pytest-xdist' requires a different Python: X.Y.Z < 3.8"}],"ecosystem":"pypi","meta_description":null,"install_score":0,"install_tag":"stale","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":"3.8.0","install_checks":{"last_tested":"2026-05-12","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"31.3M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"32.1M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.7,"import_time_s":null,"mem_mb":null,"disk_size":"32M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":3,"import_time_s":null,"mem_mb":null,"disk_size":"33M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"34.4M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"35.3M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.8,"import_time_s":null,"mem_mb":null,"disk_size":"35M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":3,"import_time_s":null,"mem_mb":null,"disk_size":"36M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"26.0M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"26.9M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.6,"import_time_s":null,"mem_mb":null,"disk_size":"26M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.9,"import_time_s":null,"mem_mb":null,"disk_size":"27M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"25.8M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"26.6M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.7,"import_time_s":null,"mem_mb":null,"disk_size":"26M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.9,"import_time_s":null,"mem_mb":null,"disk_size":"27M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"30.7M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"31.4M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":3.3,"import_time_s":null,"mem_mb":null,"disk_size":"31M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"psutil","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":3.6,"import_time_s":null,"mem_mb":null,"disk_size":"32M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"psutil","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":-1},{"runtime":"python:3.10-slim","exit_code":-1},{"runtime":"python:3.11-alpine","exit_code":-1},{"runtime":"python:3.11-slim","exit_code":-1},{"runtime":"python:3.12-alpine","exit_code":-1},{"runtime":"python:3.12-slim","exit_code":-1},{"runtime":"python:3.13-alpine","exit_code":-1},{"runtime":"python:3.13-slim","exit_code":-1},{"runtime":"python:3.9-alpine","exit_code":-1},{"runtime":"python:3.9-slim","exit_code":-1}]}}