coveragepy icon indicating copy to clipboard operation
coveragepy copied to clipboard

Partial branches not detected in comprehensions

Open caleb531 opened this issue 3 years ago • 1 comments

First of all, thank you for creating coverage.py—it is a fantastic tool, and I use it in all of my Python projects (at least those that have tests 😅).

Describe the bug

I discovered a situation in my code recently where I had a missing test that should have been flagged as a partial branch in my function. However, coverage.py did not flag the branch as partial, and I posited it was because I had written my function as a comprehension.

Screenshot 2022-12-04 at 3 46 43 PM

To confirm this theory, if I rewrite the above as multiline for loop, coverage.py correctly flags the partial branch:

Screenshot 2022-12-04 at 3 47 23 PM

To Reproduce

  1. What version of Python are you using? 3.11.0
  2. What version of coverage.py shows the problem? The output of coverage debug sys is helpful. A. See command output below
  3. What versions of what packages do you have installed? The output of pip freeze is helpful. A. See requirements.txt in my demo repository (link below)
  4. What code shows the problem? Give us a specific commit of a specific repo that we can check out. If you've already worked around the problem, please provide a commit before that fix. A. See my demo repo below
  5. What commands did you run?
    1. Write some code using any type of comprehension that contains an if clause
    2. Write a test where the result of the comprehension has the same length as the input iterable (meaning the if condition always evaluated to True during the test run)
    3. Run coverage run -m unittest && coverage report && coverage html
    4. Open htmlcov/index.html in your browser; if you view your source file, you'll see that coverage.py covered all lines in your function
    5. Now, if you rewrite that list comprehension as a multiline for loop (i.e. appending to a list on each iteration), and rerun the command from step (4), you'll see that coverage.py does flag the partial branch (as expected)

Expected behavior

I expect that a partial branch in a comprehension (of any type) to be recognized/flagged by the coverage report.

Additional context

I've created a repository to demonstrate the issue, including code, tests, and instructions. https://github.com/caleb531/coverage-partial-branch-bug

Debug output

Output of running coverage debug sys:

-- sys -------------------------------------------------------
               coverage_version: 6.5.0
                coverage_module: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.virtualenv/lib/python3.11/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: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.coveragerc
                    config_file: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.coveragerc
                config_contents: b'# Configuration for coverage.py (https://pypi.python.org/pypi/coverage)\n\n[run]\n# Enable branch coverage\nbranch = True\n\n[report]\n\n# Regexes for lines to exclude from consideration\nexclude_lines =\n\n    pragma: no cover\n\n    # Ignore non-runnable code\n    if __name__ == .__main__.:\n\n    pass\n\n# Only check coverage for source files\ninclude =\n    animal_utils.py\n'
                      data_file: -none-
                         python: 3.11.0 (main, Oct 26 2022, 19:06:18) [Clang 14.0.0 (clang-1400.0.29.202)]
                       platform: macOS-13.0.1-arm64-arm-64bit
                 implementation: CPython
                     executable: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.virtualenv/bin/python
                   def_encoding: utf-8
                    fs_encoding: utf-8
                            pid: 16250
                            cwd: /Users/caleb/Repositories/personal/coverage-partial-branch-bug
                           path: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.virtualenv/bin
                                 /opt/homebrew/Cellar/[email protected]/3.11.0/Frameworks/Python.framework/Versions/3.11/lib/python311.zip
                                 /opt/homebrew/Cellar/[email protected]/3.11.0/Frameworks/Python.framework/Versions/3.11/lib/python3.11
                                 /opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.11/lib/python3.11/lib-dynload
                                 /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.virtualenv/lib/python3.11/site-packages
                    environment: HOME = /Users/caleb
                                 PYTHONDONTWRITEBYTECODE = 1
                   command_line: /Users/caleb/Repositories/personal/coverage-partial-branch-bug/.virtualenv/bin/coverage debug sys
                sqlite3_version: 2.6.0
         sqlite3_sqlite_version: 3.39.4
             sqlite3_temp_store: 0
        sqlite3_compile_options: ATOMIC_INTRINSICS=1, COMPILER=clang-14.0.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_FTS3,
                                 ENABLE_FTS3_PARENTHESIS, ENABLE_FTS4, ENABLE_FTS5, ENABLE_GEOPOLY,
                                 ENABLE_MATH_FUNCTIONS, ENABLE_PREUPDATE_HOOK, ENABLE_RTREE, ENABLE_SESSION,
                                 MALLOC_SOFT_LIMIT=1024, MAX_ATTACHED=10, MAX_COLUMN=2000,
                                 MAX_COMPOUND_SELECT=500, MAX_DEFAULT_PAGE_SIZE=8192, MAX_EXPR_DEPTH=1000,
                                 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, SYSTEM_MALLOC, TEMP_STORE=1, THREADSAFE=1

caleb531 avatar Dec 05 '22 00:12 caleb531

Hey @nedbat I would appreciate your thoughts on this.

I'm also seeing this behaviour on a project using Python 3.10.13 with Coverage.py 7.3.1 on debian bullseye

For anyone else coming to this, it's noted in the version 7.2.6 release notes that:

Python 3.12 beta 1 now inlines comprehensions. Previously they were compiled as invisible functions and coverage.py would warn you if they weren't completely executed. This no longer happens under Python 3.12.

So I am assuming it is indeed expected behaviour that comprehensions should be flagged as uncovered correctly in earlier versions of Python?

davidjrice avatar Nov 03 '23 18:11 davidjrice