From 751bacf740e0ef9911dbbd991bffcf1e72d6216b Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 14 Jul 2022 10:08:07 -0400 Subject: [PATCH 1/4] gh-94816: Improve coverage of decode_linetable This makes calls to co_lnotab to exercise this code, as well as generating synthetically large code to exercise the corner cases where line numbers need multiple bytes. --- Lib/test/test_code.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index fd68f6dee7915a..c3ce933d4f655d 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -17,6 +17,7 @@ freevars: () nlocals: 2 flags: 3 +lnotab: b'\\x04\\x01\\n\\x02' consts: ('None', '') >>> dump(f(4).__code__) @@ -30,6 +31,7 @@ freevars: ('x',) nlocals: 1 flags: 19 +lnotab: b'\\x04\\x01' consts: ('None',) >>> def h(x, y): @@ -50,6 +52,7 @@ freevars: () nlocals: 5 flags: 3 +lnotab: b'\\x02\\x01\\n\\x01\\n\\x01\\n\\x01' consts: ('None',) >>> def attrs(obj): @@ -68,6 +71,7 @@ freevars: () nlocals: 1 flags: 3 +lnotab: b'\\x02\\x01.\\x01.\\x01' consts: ('None',) >>> def optimize_away(): @@ -87,6 +91,7 @@ freevars: () nlocals: 0 flags: 3 +lnotab: b'\\x02\\x02\\x02\\x01\\x02\\x01' consts: ("'doc string'", 'None') >>> def keywordonly_args(a,b,*,k1): @@ -104,6 +109,7 @@ freevars: () nlocals: 3 flags: 3 +lnotab: b'\\x02\\x01' consts: ('None',) >>> def posonly_args(a,b,/,c): @@ -121,6 +127,7 @@ freevars: () nlocals: 3 flags: 3 +lnotab: b'\\x02\\x01' consts: ('None',) """ @@ -159,7 +166,8 @@ def dump(co): """Print out a text representation of a code object.""" for attr in ["name", "argcount", "posonlyargcount", "kwonlyargcount", "names", "varnames", - "cellvars", "freevars", "nlocals", "flags"]: + "cellvars", "freevars", "nlocals", "flags", + "lnotab"]: print("%s: %s" % (attr, getattr(co, "co_" + attr))) print("consts:", tuple(consts(co.co_consts))) @@ -428,6 +436,21 @@ def func(): self.assertIsNone(line) self.assertEqual(end_line, new_code.co_firstlineno + 1) + def test_large_lnotab(self): + d = {} + lines = ( + ["def f():"] + + [' """'] + + [' .'] * (1 << 17) + + [' """'] + + [" x = 1"] * (1 << 17) + ) + source = "\n".join(lines) + exec(source, d) + code = d["f"].__code__ + + self.assertEqual(len(code.co_lnotab), 264208) + def isinterned(s): return s is sys.intern(('_' + s + '_')[1:-1]) From 5e8ec4224b0338ad87b48379e40b5beec9166eb0 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 14 Jul 2022 11:41:01 -0400 Subject: [PATCH 2/4] Make work without default encoding --- Lib/test/test_code.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index c3ce933d4f655d..952de7c7819977 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -166,9 +166,9 @@ def dump(co): """Print out a text representation of a code object.""" for attr in ["name", "argcount", "posonlyargcount", "kwonlyargcount", "names", "varnames", - "cellvars", "freevars", "nlocals", "flags", - "lnotab"]: + "cellvars", "freevars", "nlocals", "flags"]: print("%s: %s" % (attr, getattr(co, "co_" + attr))) + print("lnotab:", repr(co.co_lnotab)) print("consts:", tuple(consts(co.co_consts))) # Needed for test_closure_injection below From 7857e5047731714a39a064f0392bcd9430712170 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 14 Jul 2022 15:16:41 -0400 Subject: [PATCH 3/4] Use `list` rather than `repr`. Use blank lines rather than docstring. --- Lib/test/test_code.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 952de7c7819977..10d155a844a689 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -17,7 +17,7 @@ freevars: () nlocals: 2 flags: 3 -lnotab: b'\\x04\\x01\\n\\x02' +lnotab: [4, 1, 10, 2] consts: ('None', '') >>> dump(f(4).__code__) @@ -31,7 +31,7 @@ freevars: ('x',) nlocals: 1 flags: 19 -lnotab: b'\\x04\\x01' +lnotab: [4, 1] consts: ('None',) >>> def h(x, y): @@ -52,7 +52,7 @@ freevars: () nlocals: 5 flags: 3 -lnotab: b'\\x02\\x01\\n\\x01\\n\\x01\\n\\x01' +lnotab: [2, 1, 10, 1, 10, 1, 10, 1] consts: ('None',) >>> def attrs(obj): @@ -71,7 +71,7 @@ freevars: () nlocals: 1 flags: 3 -lnotab: b'\\x02\\x01.\\x01.\\x01' +lnotab: [2, 1, 46, 1, 46, 1] consts: ('None',) >>> def optimize_away(): @@ -91,7 +91,7 @@ freevars: () nlocals: 0 flags: 3 -lnotab: b'\\x02\\x02\\x02\\x01\\x02\\x01' +lnotab: [2, 2, 2, 1, 2, 1] consts: ("'doc string'", 'None') >>> def keywordonly_args(a,b,*,k1): @@ -109,7 +109,7 @@ freevars: () nlocals: 3 flags: 3 -lnotab: b'\\x02\\x01' +lnotab: [2, 1] consts: ('None',) >>> def posonly_args(a,b,/,c): @@ -127,7 +127,7 @@ freevars: () nlocals: 3 flags: 3 -lnotab: b'\\x02\\x01' +lnotab: [2, 1] consts: ('None',) """ @@ -168,7 +168,7 @@ def dump(co): "kwonlyargcount", "names", "varnames", "cellvars", "freevars", "nlocals", "flags"]: print("%s: %s" % (attr, getattr(co, "co_" + attr))) - print("lnotab:", repr(co.co_lnotab)) + print("lnotab:", list(co.co_lnotab)) print("consts:", tuple(consts(co.co_consts))) # Needed for test_closure_injection below @@ -440,10 +440,8 @@ def test_large_lnotab(self): d = {} lines = ( ["def f():"] + - [' """'] + - [' .'] * (1 << 17) + - [' """'] + - [" x = 1"] * (1 << 17) + [""] * (1 << 17) + + [" pass"] * (1 << 17) ) source = "\n".join(lines) exec(source, d) From c77f49d05c550bac126df5a678ceb1371e8c4a63 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 14 Jul 2022 15:43:18 -0400 Subject: [PATCH 4/4] Update Lib/test/test_code.py Co-authored-by: Brandt Bucher --- Lib/test/test_code.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 10d155a844a689..9110ce062b3989 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -447,7 +447,9 @@ def test_large_lnotab(self): exec(source, d) code = d["f"].__code__ - self.assertEqual(len(code.co_lnotab), 264208) + expected = 1032 * [0, 127] + [0, 9] + ((1 << 17) - 1) * [2, 1] + expected[0] = 2 + self.assertEqual(list(code.co_lnotab), expected) def isinterned(s):