{"id":8037,"library":"coverage-enable-subprocess","title":"coverage-enable-subprocess","description":"This package installs a .pth file that enables the coverage.py process_startup feature in the Python prefix/virtualenv in subsequent runs, facilitating automatic coverage measurement for subprocesses. It is currently stable and actively maintained, with version 1.0 being the latest. The release cadence is as-needed for stability and compatibility with coverage.py.","status":"active","version":"1.0","language":"en","source_language":"en","source_url":"https://github.com/bukzor/python-coverage-enable-subprocess","tags":["coverage","subprocess","testing","pth","automation"],"install":[{"cmd":"pip install coverage-enable-subprocess","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"This library enables a feature of coverage.py; coverage.py must be installed and configured separately for actual coverage measurement to occur.","package":"coverage","optional":false}],"imports":[{"note":"The library itself does not expose Python symbols for direct programmatic interaction. Its effect is through Python's startup mechanism.","symbol":"No direct Python imports","correct":"This package works by installing a .pth file that automatically invokes `coverage.process_startup()` when Python starts, rather than providing symbols for direct import into user code. Its primary interaction is via environment variables and a `.coveragerc` file."}],"quickstart":{"code":"import os\nimport subprocess\nimport sys\n\n# 1. Create a dummy .coveragerc for demonstration\nwith open('.coveragerc', 'w') as f:\n    f.write('[run]\\n')\n    f.write('parallel = True\\n')\n    f.write('data_file = .coverage.subprocess\\n')\n\n# 2. Set the environment variable to point coverage.py to the config file\nos.environ['COVERAGE_PROCESS_START'] = os.path.abspath('.coveragerc')\n\n# 3. Create a simple script to run as a subprocess\nscript_content = \"\"\"\nimport os\nimport sys\n\n# This line should be covered by the subprocess\ndef my_subprocess_func():\n    print(\"Hello from subprocess!\")\n\nif __name__ == '__main__':\n    my_subprocess_func()\n\"\"\"\nwith open('subprocess_target.py', 'w') as f:\n    f.write(script_content)\n\nprint(\"Running subprocess...\")\n\n# 4. Run the script as a subprocess\n# Ensure environment variables are passed correctly\nsubprocess_env = os.environ.copy()\nprocess = subprocess.Popen([sys.executable, 'subprocess_target.py'], env=subprocess_env)\nprocess.wait()\n\nprint(\"Subprocess finished. Check for .coverage.subprocess file.\")\n\n# Clean up (optional for quickstart, but good practice)\nos.remove('.coveragerc')\nos.remove('subprocess_target.py')\n\n# To see the actual coverage report, you would then run:\n# coverage combine --data-file=.coverage.subprocess\n# coverage report --data-file=.coverage.subprocess\n","lang":"python","description":"This quickstart demonstrates how to enable coverage for a Python subprocess. It involves installing `coverage-enable-subprocess`, creating a `.coveragerc` file, setting the `COVERAGE_PROCESS_START` environment variable, and then running a Python script as a subprocess. The installed `.pth` file ensures that `coverage.py`'s `process_startup()` function is called, initiating coverage collection in the subprocess."},"warnings":[{"fix":"When creating subprocesses with `subprocess.Popen`, explicitly pass `env=os.environ.copy()` or carefully merge environment variables, e.g., `subprocess_env = os.environ.copy(); subprocess_env.update({'MY_VAR': 'value'}); subprocess.Popen(..., env=subprocess_env)`.","message":"Coverage data may not be collected from subprocesses if `COV_*` environment variables are not correctly passed to them. When using `subprocess.Popen`, ensure the `env` argument includes all relevant `os.environ` variables, especially those starting with `COV_`.","severity":"gotcha","affected_versions":"1.0"},{"fix":"Always use `process.terminate()` (sends SIGTERM) and then `process.wait()` with a timeout. Only resort to `process.kill()` if `terminate` and `wait` fail. Example: `process.terminate(); try: process.wait(timeout=1.0) except subprocess.TimeoutExpired: process.kill()`.","message":"Using `process.kill()` to terminate subprocesses will prevent `coverage.py` from writing out its collected data, resulting in missing or incomplete coverage reports for those processes. Coverage.py relies on a clean shutdown (via `atexit` or `SIGTERM`) to write its data file.","severity":"gotcha","affected_versions":"1.0"},{"fix":"Add `parallel = True` under the `[run]` section in your `.coveragerc` file. After all processes complete, use `coverage combine` to merge the individual data files.","message":"When measuring coverage across multiple subprocesses, `coverage.py` requires `parallel = True` in the `[run]` section of your `.coveragerc` file. Without this, subprocesses might overwrite each other's data files, or data might not be collected correctly.","severity":"gotcha","affected_versions":"1.0"},{"fix":"Ensure that `COVERAGE_PROCESS_START` is set to the absolute path of your `.coveragerc` file before launching subprocesses. Verify the file exists and is readable.","message":"The `COVERAGE_PROCESS_START` environment variable *must* point to an existing and accessible `coverage.py` configuration file (e.g., `.coveragerc`). If the file is missing or the path is incorrect, coverage will not be enabled in subprocesses.","severity":"gotcha","affected_versions":"1.0"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Ensure `COVERAGE_PROCESS_START` points to a valid `.coveragerc` with `parallel = True` and that subprocesses terminate cleanly (using `terminate()` instead of `kill()`). Check if `COV_*` environment variables are correctly passed to subprocesses.","cause":"This often happens when subprocesses are not actually collecting coverage data, or when `coverage combine` is run without any `.coverage` files being generated by the subprocesses. Common causes include missing `COVERAGE_PROCESS_START` env var, incorrect `.coveragerc` path, or improper subprocess termination preventing data write.","error":"No data to combine"},{"fix":"Verify that `os.environ['COVERAGE_PROCESS_START']` is set to the absolute path of your `.coveragerc` before `subprocess.Popen`. Ensure `env=os.environ.copy()` is passed to `subprocess.Popen` to inherit all environment variables, including `COV_*`.","cause":"Despite `coverage-enable-subprocess` being installed, `coverage.py` might not be initialized in the subprocess. This typically occurs if `COVERAGE_PROCESS_START` is not set or points to an invalid configuration file, or if the subprocess environment doesn't inherit the necessary `COV_*` variables.","error":"Subprocess code shows 0% coverage"},{"fix":"Add `parallel = True` under the `[run]` section of your `.coveragerc` file. After all subprocesses complete, use `coverage combine` to merge the individual `.coverage.<hostname>.<pid>.<timestamp>` files into a single `.coverage` file.","cause":"When multiple subprocesses run, `coverage.py` needs to write separate data files for each. If `parallel = True` is not set in the `.coveragerc`, subprocesses might attempt to write to the same `.coverage` file, leading to corruption or loss of data.","error":"Coverage data from multiple subprocesses is incomplete or overwritten"}]}