Skip to content
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

IndexError in Python 3.5 #434

Closed
nedbat opened this issue Oct 19, 2015 · 10 comments
Closed

IndexError in Python 3.5 #434

nedbat opened this issue Oct 19, 2015 · 10 comments
Labels
bug Something isn't working

Comments

@nedbat
Copy link
Owner

nedbat commented Oct 19, 2015

Originally reported by Theron Luhn (Bitbucket: luhnatic, GitHub: Unknown)


When trying to generate HTML report (make html) for https://github.com/luhn/aiopg/tree/async-with in Python 3.5, I get the following exception.

Traceback (most recent call last):
  File "/home/travis/virtualenv/python3.5.0/bin/py.test", line 11, in <module>
    sys.exit(main())
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/config.py", line 48, in main
    return config.hook.pytest_cmdline_main(config=config)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
    res = hook_impl.function(*args)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/main.py", line 115, in pytest_cmdline_main
    return wrap_session(config, _main)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/main.py", line 110, in wrap_session
    exitstatus=session.exitstatus)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
    return _wrapped_call(hook_impl.function(*args), self.execute)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 249, in _wrapped_call
    wrap_controller.send(call_outcome)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/terminal.py", line 370, in pytest_sessionfinish
    self.config.hook.pytest_terminal_summary(terminalreporter=self)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
    res = hook_impl.function(*args)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/pytest_cov/plugin.py", line 168, in pytest_terminal_summary
    total = self.cov_controller.summary(terminalreporter.writer)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/pytest_cov/engine.py", line 92, in summary
    total = self.cov.html_report(ignore_errors=True)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/control.py", line 1016, in html_report
    return reporter.report(morfs)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/html.py", line 133, in report
    self.report_files(self.html_file, morfs, self.config.html_dir)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/report.py", line 83, in report_files
    report_fn(fr, self.coverage._analyze(fr))
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/control.py", line 893, in _analyze
    return Analysis(self.data, it)
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/results.py", line 28, in __init__
    self._arc_possibilities = sorted(self.file_reporter.arcs())
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/python.py", line 159, in arcs
    return self.parser.arcs()
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/parser.py", line 238, in arcs
    for l1, l2 in self.byte_parser._all_arcs():
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/parser.py", line 624, in _all_arcs
    arcs.update(bp._arcs())
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/parser.py", line 561, in _arcs
    chunks = self._split_into_chunks()
  File "/home/travis/virtualenv/python3.5.0/lib/python3.5/site-packages/coverage/parser.py", line 493, in _split_into_chunks
    block_stack.pop()
IndexError: pop from empty list

make report works fine.


@nedbat
Copy link
Owner Author

nedbat commented Oct 19, 2015

Original comment by Theron Luhn (Bitbucket: luhnatic, GitHub: Unknown)


Further investigation leads to here. I've been trying to write a simple Python script to isolate this particular case, but I can't seem to do it.

@nedbat
Copy link
Owner Author

nedbat commented Oct 20, 2015

You may be the first person to try coverage.py with the new 3.5 async support. Coverage.py will have to be updated to handle it. The more tests you can give me for it, the better :)

@nedbat
Copy link
Owner Author

nedbat commented Nov 14, 2015

@Luhnatic if you can provide instructions to reproduce the problem, I can take a look at it.

@nedbat
Copy link
Owner Author

nedbat commented Dec 11, 2015

Original comment by tehasdf (Bitbucket: tehasdf, GitHub: tehasdf)


Apparently you need to enable branch coverage to trigger this error (and indeed aiopg does include a branch=True in its .coveragerc).
For example, this code triggers the error for me:

#!python
async def go():
    async with x:
        pass

Run like:

#!bash
coverage run --branch thefile.py
coverage html

@nedbat
Copy link
Owner Author

nedbat commented Dec 17, 2015

Original comment by Aymeric Augustin (Bitbucket: aaugustin, GitHub: aaugustin)


I also hit this issue on https://github.com/aaugustin/websockets.

I implemented an async context manager and I get the same trace.

It's slightly vexing because my tox.ini specifies:

python -m coverage erase
python -m coverage run --branch --source=websockets -m unittest
python -m coverage report --fail-under=100

Thanks for a great tool anyway!

@nedbat
Copy link
Owner Author

nedbat commented Dec 17, 2015

Original comment by Aymeric Augustin (Bitbucket: aaugustin, GitHub: aaugustin)


So, the disassembly of:

async def a():
    async with b:
        pass

gives:

>>> dis.dis(asyncbranch.a)
  2           0 LOAD_GLOBAL              0 (b)
              3 BEFORE_ASYNC_WITH
              4 GET_AWAITABLE
              5 LOAD_CONST               0 (None)
              8 YIELD_FROM
              9 SETUP_ASYNC_WITH         5 (to 17)
             12 POP_TOP

  3          13 POP_BLOCK
             14 LOAD_CONST               0 (None)
        >>   17 WITH_CLEANUP_START
             18 GET_AWAITABLE
             19 LOAD_CONST               0 (None)
             22 YIELD_FROM
             23 WITH_CLEANUP_FINISH
             24 END_FINALLY
             25 LOAD_CONST               0 (None)
             28 RETURN_VALUE

This patch appears to fix the issue for me:

diff -r [b1f870cdcfb5 (bb)](https://bitbucket.org/ned/coveragepy/commits/b1f870cdcfb5) AUTHORS.txt
--- a/AUTHORS.txt	Fri Dec 11 09:01:51 2015 -0500
+++ b/AUTHORS.txt	Thu Dec 17 22:28:34 2015 +0100
@@ -8,6 +8,7 @@
 Alexander Todorov
 Anthony Sottile
 Arcadiy Ivanov
+Aymeric Augustin
 Ben Finney
 Bill Hart
 Brandon Rhodes
diff -r [b1f870cdcfb5 (bb)](https://bitbucket.org/ned/coveragepy/commits/b1f870cdcfb5) CHANGES.rst
--- a/CHANGES.rst	Fri Dec 11 09:01:51 2015 -0500
+++ b/CHANGES.rst	Thu Dec 17 22:28:34 2015 +0100
@@ -15,6 +15,11 @@
 
 .. _issue 131: https://bitbucket.org/ned/coveragepy/issues/131/pragma-on-a-decorator-line-should-affect
 
+- Fixed a crash in report generation when measuring branch coverage on Python
+  3.5 code including ``async with`` context managers (`issue 434`_).
+
+.. _issue 434: https://bitbucket.org/ned/coveragepy/issues/434/indexerror-in-python-35
+
 
 Version 4.0.3 --- 2015-11-24
 ----------------------------
diff -r [b1f870cdcfb5 (bb)](https://bitbucket.org/ned/coveragepy/commits/b1f870cdcfb5) coverage/parser.py
--- a/coverage/parser.py	Fri Dec 11 09:01:51 2015 -0500
+++ b/coverage/parser.py	Thu Dec 17 22:28:34 2015 +0100
@@ -321,7 +321,8 @@
 
 # Opcodes that push a block on the block stack.
 OPS_PUSH_BLOCK = _opcode_set(
-    'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_WITH'
+    'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_WITH',
+#    'SETUP_ASYNC_WITH',
 )
 
 # Block types for exception handling.
diff -r [b1f870cdcfb5 (bb)](https://bitbucket.org/ned/coveragepy/commits/b1f870cdcfb5) tests/test_parser.py
--- a/tests/test_parser.py	Fri Dec 11 09:01:51 2015 -0500
+++ b/tests/test_parser.py	Thu Dec 17 22:28:34 2015 +0100
@@ -3,6 +3,7 @@
 
 """Tests for coverage.py's code parsing."""
 
+import sys
 import textwrap
 
 from tests.coveragetest import CoverageTest
@@ -185,6 +186,18 @@
         self.assertEqual(parser.raw_statements, set([1, 2, 3, 5, 6, 7, 8]))
         self.assertEqual(parser.statements, set([1, 2, 3]))
 
+    def test_async_with(self):
+        # https://bitbucket.org/ned/coveragepy/issues/434/indexerror-in-python-35
+        if sys.version_info < (3, 5):
+            self.skip("async with syntax was introduced in Python3.5")
+        parser = self.parse_source("""\
+            async def a():
+                async with b:
+                    pass
+            """)
+        # This used to raise IndexError: pop from empty list
+        self.assertEqual(parser.exit_counts(), {1: 1, 2: 1, 3: 1})
+
 
 class ParserFileTest(CoverageTest):
     """Tests for coverage.py's code parsing from files."""

(Sorry for the inline diff, I have mostly stopped using hg and I'm too lazy at this time of the night to figure out how to make a pull request on BitBucket.)

@nedbat
Copy link
Owner Author

nedbat commented Jan 2, 2016

I'm working on a complete re-implementation of branch measurement that will include this.

@nedbat
Copy link
Owner Author

nedbat commented Jan 8, 2016

This is fixed in 44719bd (bb), which will be 4.1

@nedbat
Copy link
Owner Author

nedbat commented Jan 8, 2016

Original comment by Aymeric Augustin (Bitbucket: aaugustin, GitHub: aaugustin)


Thanks Ned! I'm looking forwards to 4.1.

@nedbat nedbat closed this as completed Jan 8, 2016
@nedbat
Copy link
Owner Author

nedbat commented Jan 10, 2016

Issue #464 was marked as a duplicate of this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant