nedbat / coveragepy

The code coverage tool for Python
https://coverage.readthedocs.io
Apache License 2.0
3.03k stars 436 forks source link

Although statements in subprocess get executed, coverage still reports them as missing. #1520

Open flixman opened 1 year ago

flixman commented 1 year ago

Describe the bug In my flask project, running through pytest with the pytest-flask plugin, a subprocess gets started. I get two .coverage..* files that get generated, but the report shows lines as missing. If I run the same tests on a debugger and set a breakpoint there, those lines are hit... but still report missing.

UPDATE: There is one of the methods on a specific file in which a single line is reported missing (out of 5). I have checked the plugin starting the subprocess, (pytest-flask, file live_server.py) and it sends a SIGINT to the process before calling the join (which seems to be aligned with coverage recommendations).

To Reproduce How can we reproduce the problem? Please be specific. Don't link to a failing CI job. Answer the questions below:

  1. What version of Python are you using? 3.10.8
  2. What version of coverage.py shows the problem?
    -- sys -------------------------------------------------------
               coverage_version: 7.0.1
                coverage_module: ~/venv/lib/python3.10/site-packages/coverage/__init__.py
                         tracer: -none-
                        CTracer: available
           plugins.file_tracers: -none-
            plugins.configurers: -none-
      plugins.context_switchers: -none-
              configs_attempted: .coveragerc
                   configs_read: ~/.coveragerc
                    config_file: ~/.coveragerc
                config_contents: b"[run]\nbranch = true\nparallel = true\nconcurrency = multiprocessing\nsigterm = true\nomit =\n    backend/tests/*\n    backend/blueprints/test.py\n    backend/blueprints/checkout.py\n\n[report]\nfail_under = 90\nshow_missing = True\n\n# Regexes for lines to exclude from consideration\nexclude_lines =\n    # Have to re-enable the standard pragma\n    pragma: no cover\n\n    # Don't complain about missing debug-only code:\n    def __repr__\n    if self\\.debug\n\n    # Don't complain if non-runnable code isn't run:\n    if 0:\n    if __name__ == .__main__.:\n\n    # Don't complain about abstract methods, they aren't run:\n    @(abc\\.)?abstractmethod\n"
                      data_file: -none-
                         python: 3.10.8 (main, Nov  1 2022, 14:18:21) [GCC 12.2.0]
                       platform: Linux-6.0.12-arch1-1-x86_64-with-glibc2.36
                 implementation: CPython
                     executable: ~/venv/bin/python
                   def_encoding: utf-8
                    fs_encoding: utf-8
                            pid: 154712
                            cwd: ~
                           path: ~/venv/bin
                                 /usr/lib/python310.zip
                                 /usr/lib/python3.10
                                 /usr/lib/python3.10/lib-dynload
                                 ~/venv/lib/python3.10/site-packages
                    environment: HOME = ~
                   command_line: ~/venv/bin/coverage debug sys
                sqlite3_version: 2.6.0
         sqlite3_sqlite_version: 3.40.0
             sqlite3_temp_store: 0
        sqlite3_compile_options: ATOMIC_INTRINSICS=1, COMPILER=gcc-12.2.0, DEFAULT_AUTOVACUUM,
                                 DEFAULT_CACHE_SIZE=-2000, DEFAULT_FILE_FORMAT=4,
                                 DEFAULT_JOURNAL_SIZE_LIMIT=-1, DEFAULT_MMAP_SIZE=0, DEFAULT_PAGE_SIZE=4096,
                                 DEFAULT_PCACHE_INITSZ=20, DEFAULT_RECURSIVE_TRIGGERS,
                                 DEFAULT_SECTOR_SIZE=4096, DEFAULT_SYNCHRONOUS=2,
                                 DEFAULT_WAL_AUTOCHECKPOINT=1000, DEFAULT_WAL_SYNCHRONOUS=2,
                                 DEFAULT_WORKER_THREADS=0, ENABLE_COLUMN_METADATA, ENABLE_DBSTAT_VTAB,
                                 ENABLE_FTS3, ENABLE_FTS3_PARENTHESIS, ENABLE_FTS3_TOKENIZER, ENABLE_FTS4,
                                 ENABLE_FTS5, ENABLE_MATH_FUNCTIONS, ENABLE_RTREE, ENABLE_STMTVTAB,
                                 ENABLE_UNLOCK_NOTIFY, HAVE_ISNAN, MALLOC_SOFT_LIMIT=1024, MAX_ATTACHED=10,
                                 MAX_COLUMN=2000, MAX_COMPOUND_SELECT=500, MAX_DEFAULT_PAGE_SIZE=8192,
                                 MAX_EXPR_DEPTH=10000, MAX_FUNCTION_ARG=127, MAX_LENGTH=1000000000,
                                 MAX_LIKE_PATTERN_LENGTH=50000, MAX_MMAP_SIZE=0x7fff0000,
                                 MAX_PAGE_COUNT=1073741823, MAX_PAGE_SIZE=65536, MAX_SQL_LENGTH=1000000000,
                                 MAX_TRIGGER_DEPTH=1000, MAX_VARIABLE_NUMBER=250000, MAX_VDBE_OP=250000000,
                                 MAX_WORKER_THREADS=8, MUTEX_PTHREADS, SECURE_DELETE, SYSTEM_MALLOC,
                                 TEMP_STORE=1, THREADSAFE=1
  3. What versions of what packages do you have installed?
    attrs==22.2.0
    certifi==2022.12.7
    charset-normalizer==2.1.1
    click==8.1.3
    exceptiongroup==1.1.0
    Flask==2.2.2
    Flask-Cors==3.0.10
    Flask-Login==0.6.2
    Flask-SQLAlchemy==3.0.2
    Flask-WTF==1.0.1
    greenlet==2.0.1
    gunicorn==20.1.0
    idna==3.4
    iniconfig==1.1.1
    itsdangerous==2.1.2
    Jinja2==3.1.2
    MarkupSafe==2.1.1
    numpy==1.24.1
    packaging==22.0
    pluggy==1.0.0
    psycopg2==2.9.5
    pydot==1.4.2
    pyparsing==3.0.9
    pytest==7.2.0
    pytest-flask==1.2.0
    python-dotenv==0.21.0
    PyYAML==6.0
    requests==2.28.1
    six==1.16.0
    SQLAlchemy==1.4.45
    SQLAlchemy-Utils==0.39.0
    stripe==5.0.0
    tomli==2.0.1
    urllib3==1.26.13
    Werkzeug==2.2.2
    WTForms==3.0.1
  4. What code shows the problem? It is a private repo, I cannot share :-/
  5. What commands did you run?
    rm -f .coverage.*
    coverage run -m pytest
    coverage combine
    coverage report 

Expected behavior I get the information related to the coverage of the statements run by the subprocess that is running the flask server.

nedbat commented 1 year ago

I have checked the plugin starting the subprocess, (pytest-flask, file live_server.py) and it sends a SIGINT to the process before calling the join (which seems to be aligned with coverage recommendations).

Have you tried setting the [run] sigterm = true setting? (docs)

flixman commented 1 year ago

Hi @nedbat Indeed, this is my .coveragerc:

[run]
branch = true
parallel = true
concurrency = multiprocessing
sigterm = true
omit =
    backend/tests/*
    backend/blueprints/test.py
    backend/blueprints/checkout.py

[report]
fail_under = 90
show_missing = True

# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover

    # Don't complain about missing debug-only code:
    def __repr__
    if self\.debug

    # Don't complain if non-runnable code isn't run:
    if 0:
    if __name__ == .__main__.:

    # Don't complain about abstract methods, they aren't run:
    @(abc\.)?abstractmethod

Is there any way, if I set up a breakpoint inside the subprocess, to confirm that coverage is working there?

nedbat commented 1 year ago

Check if the server is started with debug=True: that can run your code inside a debugger, which interferes with coverage.py data collection. See #1663.