Repository navigation
Crash on AST with misordered linenos #92597
Description
Activity
- addedtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump3.11only security fixesonly security fixes3.12only security fixesonly security fixes
on May 10, 2022 Simple repro:
from ast import * tree = Module(body=[ Import(names=[alias(name='builtins', lineno=1, col_offset=0)], lineno=1, col_offset=0), Import(names=[alias(name='traceback', lineno=0, col_offset=0)], lineno=0, col_offset=1) ], type_ignores=[]) compile(tree, "x", "exec", dont_inherit=True)
% ./python.exe wronglinenos.py Assertion failed: (i->i_end_lineno >= i->i_lineno), function write_location_info_long_form, file compile.c, line 7532. zsh: abort ./python.exe wronglinenos.py- changed the title
[-]Compiler crash on pytest-cov[/-][+]Crash on AST with misordered linenos[/+]on May 10, 2022 944fffe is the first bad commit
commit 944fffee8916cb94321fa33cd3a43f4108717746 Author: Mark Shannon <mark@hotpy.org> Date: Thu Apr 21 16:10:37 2022 +0100 GH-88116: Use a compact format to represent end line and column offsets. (GH-91666) * Stores all location info in linetable to conform to PEP 626. * Remove column table from code objects. * Remove end-line table from code objects. * Document new location table formatLooks like the ast defaults the end_lineno to None, which then becomes 0 in the compiler. Maybe the ast should default it be equal to lineno? Or should None become -1 in c?
Both, perhaps.
If noend_linenois specified, then it should probably default tolineno
If theend_linenoisNonethen we should convert that to -1, as we do forlineno.cc @pablogsal
Both, perhaps.
If noend_linenois specified, then it should probably default tolineno
If theend_linenoisNonethen we should convert that to -1, as we do forlineno.That's what we do in some parts that deal with these information. I can prepare a PR
The question is if we should manage this in the AST creation routines or in the compiler. I assume that the first is more resilient but what do others think?
FWIW, this seems to affect pytest's assertion rewriting as well. As soon as I'm running pytest against e.g.:
def test_foo(): pass
with a Python 3.11.0b1 configured with
--with-pydebug, I get:collecting ... python: Python/compile.c:7531: write_location_info_long_form: Assertion `i->i_end_lineno >= i->i_lineno' failed. Fatal Python error: Aborted Current thread 0x00007f2555185740 (most recent call first): File ".../site-packages/_pytest/assertion/rewrite.py", line 361 in _rewrite_test File ".../site-packages/_pytest/assertion/rewrite.py", line 159 in exec_module ...which is here:
and indeed
i_end_linenois 0:(gdb) pp i i = autoderefcount="1",[ i_opcode = <int> = {"100"} i_oparg = <int> = {"0"} i_target = <struct basicblock_*> = {"0x0"} i_except = <struct basicblock_*> = {"0x0"} i_lineno = <int> = {"1"} i_end_lineno = <int> = {"0"} i_col_offset = <int> = {"0"} i_end_col_offset = <int> = {"0"} ],<struct instr> = {"{...}"}Turning pytest's assertion rewriting off with
--assert=plainhelps, and I can confirm it works fine before 944fffe.Passing the
end_linenowhere we generate fake imports for the pytest assertion rewriting helpers seems to help:diff --git i/src/_pytest/assertion/rewrite.py w/src/_pytest/assertion/rewrite.py index 81096764e..5d7392dd4 100644 --- i/src/_pytest/assertion/rewrite.py +++ w/src/_pytest/assertion/rewrite.py @@ -727,7 +727,7 @@ def run(self, mod: ast.Module) -> None: ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), ] imports = [ - ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases + ast.Import([alias], lineno=lineno, end_lineno=item.end_lineno, col_offset=0) for alias in aliases ] mod.body[pos:pos] = imports
cc @bluetech - though I suppose we don't necessarily need to fix this in pytest, hence I didn' t open an issue over there.
Another project affected in the wild seems to be flask:
$ python3.11 -X faulthandler -c "import flask; flask.Flask('test')" python: Python/compile.c:7534: write_location_info_long_form: Assertion `i->i_end_lineno >= i->i_lineno' failed. Fatal Python error: Aborted Current thread 0x00007fd44da08740 (most recent call first): File ".../lib/python3.11/site-packages/werkzeug/routing.py", line 1073 in _compile_builder File ".../lib/python3.11/site-packages/werkzeug/routing.py", line 885 in compile File ".../lib/python3.11/site-packages/werkzeug/routing.py", line 805 in bind File ".../lib/python3.11/site-packages/werkzeug/routing.py", line 1546 in add File ".../lib/python3.11/site-packages/flask/app.py", line 1086 in add_url_rule File ".../lib/python3.11/site-packages/flask/scaffold.py", line 56 in wrapper_func File ".../lib/python3.11/site-packages/flask/app.py", line 511 in __init__ File "<string>", line 1 in <module>I'm working on a patch, will have a draft PR ready soon
Reacted by Freya Bruhin@pablogsal Do you have time to work on this, or should I fix it?
@pablogsal Do you have time to work on this, or should I fix it?
I have opened a PR for this.
- added a commit that references this issue
on May 31, 2022
./python.exe -m ensurepip./python.exe -m pip install pytest-cov./python.exe -m pytestCrashes with
Assertion failed: (i->i_end_lineno >= i->i_lineno), function write_location_info_long_form, file compile.c, line 7532.lldb stack trace:
Your environment
I put print statements in that show the file it crashes on is
'/usr/local/lib/python3.11/site-packages/pytest_cov/plugin.py', but just running Python on that file doesn't reproduce the crash. I'll try to debug some more.