Skip to content

Commit 4823d9a

Browse files
ammaraskarpablogsalisidentical
authored
bpo-43950: Add option to opt-out of PEP-657 (GH-27023)
Co-authored-by: Pablo Galindo <[email protected]> Co-authored-by: Batuhan Taskaya <[email protected]> Co-authored-by: Ammar Askar <[email protected]>
1 parent 3d3027c commit 4823d9a

File tree

18 files changed

+276
-64
lines changed

18 files changed

+276
-64
lines changed

Doc/c-api/init_config.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,16 @@ PyConfig
596596
597597
.. versionadded:: 3.10
598598
599+
.. c:member:: int no_debug_ranges
600+
601+
If equals to ``1``, disables the inclusion of the end line and column
602+
mappings in code objects. Also disables traceback printing carets to
603+
specific error locations.
604+
605+
Default: ``0``.
606+
607+
.. versionadded:: 3.11
608+
599609
.. c:member:: wchar_t* check_hash_pycs_mode
600610
601611
Control the validation behavior of hash-based ``.pyc`` files:

Doc/using/cmdline.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,12 @@ Miscellaneous options
474474
* ``-X warn_default_encoding`` issues a :class:`EncodingWarning` when the
475475
locale-specific default encoding is used for opening files.
476476
See also :envvar:`PYTHONWARNDEFAULTENCODING`.
477+
* ``-X no_debug_ranges`` disables the inclusion of the tables mapping extra
478+
location information (end line, start column offset and end column offset)
479+
to every instruction in code objects. This is useful when smaller code
480+
objects and pyc files are desired as well as supressing the extra visual
481+
location indicators when the interpreter displays tracebacks. See also
482+
:envvar:`PYTHONNODEBUGRANGES`.
477483

478484
It also allows passing arbitrary values and retrieving them through the
479485
:data:`sys._xoptions` dictionary.
@@ -509,6 +515,9 @@ Miscellaneous options
509515
.. deprecated-removed:: 3.9 3.10
510516
The ``-X oldparser`` option.
511517

518+
.. versionadded:: 3.11
519+
The ``-X no_debug_ranges`` option.
520+
512521

513522
Options you shouldn't use
514523
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -936,6 +945,17 @@ conflict.
936945

937946
.. versionadded:: 3.10
938947

948+
.. envvar:: PYTHONNODEBUGRANGES
949+
950+
If this variable is set, it disables the inclusion of the tables mapping
951+
extra location information (end line, start column offset and end column
952+
offset) to every instruction in code objects. This is useful when smaller
953+
code objects and pyc files are desired as well as supressing the extra visual
954+
location indicators when the interpreter displays tracebacks.
955+
956+
.. versionadded:: 3.11
957+
958+
939959

940960
Debug-mode variables
941961
~~~~~~~~~~~~~~~~~~~~

Include/cpython/initconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ typedef struct PyConfig {
140140
int faulthandler;
141141
int tracemalloc;
142142
int import_time;
143+
int no_debug_ranges;
143144
int show_ref_count;
144145
int dump_refs;
145146
int malloc_stats;

Lib/idlelib/idle_test/test_run.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from idlelib import run
44
import io
55
import sys
6-
from test.support import captured_output, captured_stderr
6+
from test.support import captured_output, captured_stderr, has_no_debug_ranges
77
import unittest
88
from unittest import mock
99
import idlelib
@@ -33,9 +33,14 @@ def __eq__(self, other):
3333
run.print_exception()
3434

3535
tb = output.getvalue().strip().splitlines()
36-
self.assertEqual(13, len(tb))
37-
self.assertIn('UnhashableException: ex2', tb[4])
38-
self.assertIn('UnhashableException: ex1', tb[12])
36+
if has_no_debug_ranges():
37+
self.assertEqual(11, len(tb))
38+
self.assertIn('UnhashableException: ex2', tb[3])
39+
self.assertIn('UnhashableException: ex1', tb[10])
40+
else:
41+
self.assertEqual(13, len(tb))
42+
self.assertIn('UnhashableException: ex2', tb[4])
43+
self.assertIn('UnhashableException: ex1', tb[12])
3944

4045
data = (('1/0', ZeroDivisionError, "division by zero\n"),
4146
('abc', NameError, "name 'abc' is not defined. "

Lib/test/_test_embed_set_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def test_set_invalid(self):
6161
'faulthandler',
6262
'tracemalloc',
6363
'import_time',
64+
'no_debug_ranges',
6465
'show_ref_count',
6566
'dump_refs',
6667
'malloc_stats',

Lib/test/support/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,14 @@ def requires_lzma(reason='requires lzma'):
415415
lzma = None
416416
return unittest.skipUnless(lzma, reason)
417417

418+
def has_no_debug_ranges():
419+
import _testinternalcapi
420+
config = _testinternalcapi.get_config()
421+
return bool(config['no_debug_ranges'])
422+
423+
def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
424+
return unittest.skipIf(has_no_debug_ranges(), reason)
425+
418426
requires_legacy_unicode_capi = unittest.skipUnless(unicode_legacy_string,
419427
'requires legacy Unicode C API')
420428

Lib/test/test_code.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@
137137
except ImportError:
138138
ctypes = None
139139
from test.support import (run_doctest, run_unittest, cpython_only,
140-
check_impl_detail)
140+
check_impl_detail, requires_debug_ranges)
141+
from test.support.script_helper import assert_python_ok
141142

142143

143144
def consts(t):
@@ -325,6 +326,7 @@ def func():
325326
new_code = code = func.__code__.replace(co_linetable=b'')
326327
self.assertEqual(list(new_code.co_lines()), [])
327328

329+
@requires_debug_ranges()
328330
def test_co_positions_artificial_instructions(self):
329331
import dis
330332

@@ -372,8 +374,32 @@ def test_co_positions_artificial_instructions(self):
372374
]
373375
)
374376

377+
def test_endline_and_columntable_none_when_no_debug_ranges(self):
378+
# Make sure that if `-X no_debug_ranges` is used, the endlinetable and
379+
# columntable are None.
380+
code = textwrap.dedent("""
381+
def f():
382+
pass
383+
384+
assert f.__code__.co_endlinetable is None
385+
assert f.__code__.co_columntable is None
386+
""")
387+
assert_python_ok('-X', 'no_debug_ranges', '-c', code, __cleanenv=True)
388+
389+
def test_endline_and_columntable_none_when_no_debug_ranges_env(self):
390+
# Same as above but using the environment variable opt out.
391+
code = textwrap.dedent("""
392+
def f():
393+
pass
394+
395+
assert f.__code__.co_endlinetable is None
396+
assert f.__code__.co_columntable is None
397+
""")
398+
assert_python_ok('-c', code, PYTHONNODEBUGRANGES='1', __cleanenv=True)
399+
375400
# co_positions behavior when info is missing.
376401

402+
@requires_debug_ranges()
377403
def test_co_positions_empty_linetable(self):
378404
def func():
379405
x = 1
@@ -382,6 +408,7 @@ def func():
382408
self.assertIsNone(line)
383409
self.assertEqual(end_line, new_code.co_firstlineno + 1)
384410

411+
@requires_debug_ranges()
385412
def test_co_positions_empty_endlinetable(self):
386413
def func():
387414
x = 1
@@ -390,6 +417,7 @@ def func():
390417
self.assertEqual(line, new_code.co_firstlineno + 1)
391418
self.assertIsNone(end_line)
392419

420+
@requires_debug_ranges()
393421
def test_co_positions_empty_columntable(self):
394422
def func():
395423
x = 1

Lib/test/test_compile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import types
1010
import textwrap
1111
from test import support
12-
from test.support import script_helper
12+
from test.support import script_helper, requires_debug_ranges
1313
from test.support.os_helper import FakePath
1414

1515

@@ -985,7 +985,7 @@ def if_else_break():
985985
elif instr.opname in HANDLED_JUMPS:
986986
self.assertNotEqual(instr.arg, (line + 1)*INSTR_SIZE)
987987

988-
988+
@requires_debug_ranges()
989989
class TestSourcePositions(unittest.TestCase):
990990
# Ensure that compiled code snippets have correct line and column numbers
991991
# in `co_positions()`.

Lib/test/test_dis.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Minimal tests for dis module
22

3-
from test.support import captured_stdout
3+
from test.support import captured_stdout, requires_debug_ranges
44
from test.support.bytecode_helper import BytecodeTestCase
55
import unittest
66
import sys
@@ -1192,6 +1192,7 @@ def test_jumpy(self):
11921192
actual = dis.get_instructions(jumpy, first_line=expected_jumpy_line)
11931193
self.assertInstructionsEqual(list(actual), expected_opinfo_jumpy)
11941194

1195+
@requires_debug_ranges()
11951196
def test_co_positions(self):
11961197
code = compile('f(\n x, y, z\n)', '<test>', 'exec')
11971198
positions = [
@@ -1210,6 +1211,7 @@ def test_co_positions(self):
12101211
]
12111212
self.assertEqual(positions, expected)
12121213

1214+
@requires_debug_ranges()
12131215
def test_co_positions_missing_info(self):
12141216
code = compile('x, y, z', '<test>', 'exec')
12151217
code_without_column_table = code.replace(co_columntable=b'')

Lib/test/test_doctest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,10 +2808,12 @@ def test_testmod(): r"""
28082808

28092809
try:
28102810
os.fsencode("foo-bä[email protected]")
2811+
supports_unicode = True
28112812
except UnicodeEncodeError:
28122813
# Skip the test: the filesystem encoding is unable to encode the filename
2813-
pass
2814-
else:
2814+
supports_unicode = False
2815+
2816+
if supports_unicode and not support.has_no_debug_ranges():
28152817
def test_unicode(): """
28162818
Check doctest with a non-ascii filename:
28172819

Lib/test/test_embed.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
369369
'faulthandler': 0,
370370
'tracemalloc': 0,
371371
'import_time': 0,
372+
'no_debug_ranges': 0,
372373
'show_ref_count': 0,
373374
'dump_refs': 0,
374375
'malloc_stats': 0,
@@ -798,6 +799,7 @@ def test_init_from_config(self):
798799
'hash_seed': 123,
799800
'tracemalloc': 2,
800801
'import_time': 1,
802+
'no_debug_ranges': 1,
801803
'show_ref_count': 1,
802804
'malloc_stats': 1,
803805

@@ -858,6 +860,7 @@ def test_init_compat_env(self):
858860
'hash_seed': 42,
859861
'tracemalloc': 2,
860862
'import_time': 1,
863+
'no_debug_ranges': 1,
861864
'malloc_stats': 1,
862865
'inspect': 1,
863866
'optimization_level': 2,
@@ -887,6 +890,7 @@ def test_init_python_env(self):
887890
'hash_seed': 42,
888891
'tracemalloc': 2,
889892
'import_time': 1,
893+
'no_debug_ranges': 1,
890894
'malloc_stats': 1,
891895
'inspect': 1,
892896
'optimization_level': 2,

Lib/test/test_marshal.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from test import support
2-
from test.support import os_helper
2+
from test.support import os_helper, requires_debug_ranges
3+
from test.support.script_helper import assert_python_ok
34
import array
45
import io
56
import marshal
67
import sys
78
import unittest
89
import os
910
import types
11+
import textwrap
1012

1113
try:
1214
import _testcapi
@@ -126,6 +128,31 @@ def test_different_filenames(self):
126128
self.assertEqual(co1.co_filename, "f1")
127129
self.assertEqual(co2.co_filename, "f2")
128130

131+
@requires_debug_ranges()
132+
def test_no_columntable_and_endlinetable_with_no_debug_ranges(self):
133+
# Make sure when demarshalling objects with `-X no_debug_ranges`
134+
# that the columntable and endlinetable are None.
135+
co = ExceptionTestCase.test_exceptions.__code__
136+
code = textwrap.dedent("""
137+
import sys
138+
import marshal
139+
with open(sys.argv[1], 'rb') as f:
140+
co = marshal.load(f)
141+
142+
assert co.co_endlinetable is None
143+
assert co.co_columntable is None
144+
""")
145+
146+
try:
147+
with open(os_helper.TESTFN, 'wb') as f:
148+
marshal.dump(co, f)
149+
150+
assert_python_ok('-X', 'no_debug_ranges',
151+
'-c', code, os_helper.TESTFN,
152+
__cleanenv=True)
153+
finally:
154+
os_helper.unlink(os_helper.TESTFN)
155+
129156
@support.cpython_only
130157
def test_same_filename_used(self):
131158
s = """def f(): pass\ndef g(): pass"""

0 commit comments

Comments
 (0)