Skip to content

Coverage reports line as uncovered when run. #1432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jmgurney opened this issue Aug 4, 2022 · 1 comment
Closed

Coverage reports line as uncovered when run. #1432

jmgurney opened this issue Aug 4, 2022 · 1 comment
Labels
bug Something isn't working duplicate This issue or pull request already exists

Comments

@jmgurney
Copy link

jmgurney commented Aug 4, 2022

Describe the bug

I have found that apparently coverage reports a continue statement as being uncovered even though it is run/executed.

To Reproduce

  1. Python 3.9.13 (main, May 30 2022, 11:25:38) (tested on Python 3.10.4 (main, May 23 2022, 16:05:39) and doesn't reproduce)
-- sys -------------------------------------------------------
               coverage_version: 6.4.2
                coverage_module: /private/tmp/cv/p/lib/python3.9/site-packages/coverage/__init__.py
                         tracer: -none-
                        CTracer: available
           plugins.file_tracers: -none-
            plugins.configurers: -none-
      plugins.context_switchers: -none-
              configs_attempted: .coveragerc
                                 setup.cfg
                                 tox.ini
                                 pyproject.toml
                   configs_read: -none-
                    config_file: None
                config_contents: -none-
                      data_file: -none-
                         python: 3.9.13 (main, May 30 2022, 11:25:38) [Clang 13.0.0 (clang-1300.0.29.30)]
                       platform: macOS-12.4-x86_64-i386-64bit
                 implementation: CPython
                     executable: /private/tmp/cv/p/bin/python
                   def_encoding: utf-8
                    fs_encoding: utf-8
                            pid: 29182
                            cwd: /private/tmp/cv
                           path: /private/tmp/cv
                                 /opt/local/Library/Frameworks/Python.framework/Versions/3.9/lib/python39.zip
                                 /opt/local/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
                                 /opt/local/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload
                                 /private/tmp/cv/p/lib/python3.9/site-packages
                    environment: HOME = /Users/jmg
                   command_line: /private/tmp/cv/p/lib/python3.9/site-packages/coverage/__main__.py debug sys
                sqlite3_version: 2.6.0
         sqlite3_sqlite_version: 3.38.5
             sqlite3_temp_store: 0
        sqlite3_compile_options: ATOMIC_INTRINSICS=1, COMPILER=clang-13.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, DISABLE_INTRINSIC, ENABLE_COLUMN_METADATA,
                                 ENABLE_FTS3, ENABLE_FTS3_PARENTHESIS, ENABLE_FTS4, ENABLE_FTS5,
                                 ENABLE_GEOPOLY, ENABLE_MATH_FUNCTIONS, ENABLE_RTREE, ENABLE_STAT4,
                                 ENABLE_UNLOCK_NOTIFY, 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=32766, MAX_VDBE_OP=250000000,
                                 MAX_WORKER_THREADS=8, MUTEX_PTHREADS, SECURE_DELETE, SOUNDEX,
                                 SYSTEM_MALLOC, TEMP_STORE=1, THREADSAFE=1
coverage==6.4.2
  1. What code shows the problem?
for i in ['a', 'b', 'c']:
	if i == 'a':
		print('d')
	elif i == 'b':
		print('e')
	elif i == 'c':
		print('f')
	else: # pragma: no cover
		raise ValueError('unhnadled')

	continue

	raise Exception('an exception')
  1. What commands did you run?
python -m coverage run t.py
python -m coverage report -m

Expected behavior

I expect that the report only shows that line 13 is uncovered, but line 11 should be covered as it is the continue that prevents line 13 from running.

Additional context
Add any other context about the problem here.

@jmgurney jmgurney added bug Something isn't working needs triage labels Aug 4, 2022
@nedbat
Copy link
Owner

nedbat commented Aug 4, 2022

This is because jumps to jumps are often optimized away:

% python3.9 -m dis bug1432.py
  1           0 LOAD_CONST               0 (('a', 'b', 'c'))
              2 GET_ITER
        >>    4 FOR_ITER                76 (to 82)
              6 STORE_NAME               0 (i)

  2           8 LOAD_NAME                0 (i)
             10 LOAD_CONST               1 ('a')
             12 COMPARE_OP               2 (==)
             14 POP_JUMP_IF_FALSE       26

  3          16 LOAD_NAME                1 (print)
             18 LOAD_CONST               2 ('d')
             20 CALL_FUNCTION            1
             22 POP_TOP
             24 JUMP_ABSOLUTE            4

  4     >>   26 LOAD_NAME                0 (i)
             28 LOAD_CONST               3 ('b')
             30 COMPARE_OP               2 (==)
             32 POP_JUMP_IF_FALSE       44

  5          34 LOAD_NAME                1 (print)
             36 LOAD_CONST               4 ('e')
             38 CALL_FUNCTION            1
             40 POP_TOP
             42 JUMP_ABSOLUTE            4

  6     >>   44 LOAD_NAME                0 (i)
             46 LOAD_CONST               5 ('c')
             48 COMPARE_OP               2 (==)
             50 POP_JUMP_IF_FALSE       62

  7          52 LOAD_NAME                1 (print)
             54 LOAD_CONST               6 ('f')
             56 CALL_FUNCTION            1
             58 POP_TOP
             60 JUMP_ABSOLUTE            4

  9     >>   62 LOAD_NAME                2 (ValueError)
             64 LOAD_CONST               7 ('unhnadled')
             66 CALL_FUNCTION            1
             68 RAISE_VARARGS            1

 11          70 JUMP_ABSOLUTE            4

 13          72 LOAD_NAME                3 (Exception)
             74 LOAD_CONST               8 ('an exception')
             76 CALL_FUNCTION            1
             78 RAISE_VARARGS            1
             80 JUMP_ABSOLUTE            4
        >>   82 LOAD_CONST               9 (None)
             84 RETURN_VALUE

Notice that nothing jumps to offset 70 (line 11), and all the places that should have be re-written as jumps to offset 4, where line 11 would jump to.

This is being changed in Python 3.10 and later. If you use that version, you should see line 11 covered.

(Duplicate of #198)

@nedbat nedbat added duplicate This issue or pull request already exists and removed needs triage labels Aug 4, 2022
@nedbat nedbat closed this as not planned Won't fix, can't repro, duplicate, stale Aug 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants