summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmmar Askar <ammar@ammaraskar.com>2021-07-07 19:07:12 (GMT)
committerGitHub <noreply@github.com>2021-07-07 19:07:12 (GMT)
commit4823d9a51281ebbc8e8d82a0dd3edc7d13ea8ac7 (patch)
treeb7c97af7b1d15da75321e1434997163cd8c6b9d0
parent3d3027c5fcc683c14ee55ad231d79971ba12b24d (diff)
downloadcpython-4823d9a51281ebbc8e8d82a0dd3edc7d13ea8ac7.zip
cpython-4823d9a51281ebbc8e8d82a0dd3edc7d13ea8ac7.tar.gz
cpython-4823d9a51281ebbc8e8d82a0dd3edc7d13ea8ac7.tar.bz2
bpo-43950: Add option to opt-out of PEP-657 (GH-27023)
Co-authored-by: Pablo Galindo <Pablogsal@gmail.com> Co-authored-by: Batuhan Taskaya <batuhanosmantaskaya@gmail.com> Co-authored-by: Ammar Askar <ammar@ammaraskar.com>
-rw-r--r--Doc/c-api/init_config.rst10
-rw-r--r--Doc/using/cmdline.rst20
-rw-r--r--Include/cpython/initconfig.h1
-rw-r--r--Lib/idlelib/idle_test/test_run.py13
-rw-r--r--Lib/test/_test_embed_set_config.py1
-rw-r--r--Lib/test/support/__init__.py8
-rw-r--r--Lib/test/test_code.py30
-rw-r--r--Lib/test/test_compile.py4
-rw-r--r--Lib/test/test_dis.py4
-rw-r--r--Lib/test/test_doctest.py6
-rw-r--r--Lib/test/test_embed.py4
-rw-r--r--Lib/test/test_marshal.py29
-rw-r--r--Lib/test/test_traceback.py105
-rw-r--r--Lib/test/test_zipimport.py1
-rw-r--r--Objects/clinic/codeobject.c.h32
-rw-r--r--Objects/codeobject.c47
-rw-r--r--Programs/_testembed.c4
-rw-r--r--Python/initconfig.c21
18 files changed, 276 insertions, 64 deletions
diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst
index fe5b83a..2e52679 100644
--- a/Doc/c-api/init_config.rst
+++ b/Doc/c-api/init_config.rst
@@ -596,6 +596,16 @@ PyConfig
.. versionadded:: 3.10
+ .. c:member:: int no_debug_ranges
+
+ If equals to ``1``, disables the inclusion of the end line and column
+ mappings in code objects. Also disables traceback printing carets to
+ specific error locations.
+
+ Default: ``0``.
+
+ .. versionadded:: 3.11
+
.. c:member:: wchar_t* check_hash_pycs_mode
Control the validation behavior of hash-based ``.pyc`` files:
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index 25e05d4..98fdba2 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -474,6 +474,12 @@ Miscellaneous options
* ``-X warn_default_encoding`` issues a :class:`EncodingWarning` when the
locale-specific default encoding is used for opening files.
See also :envvar:`PYTHONWARNDEFAULTENCODING`.
+ * ``-X no_debug_ranges`` disables the inclusion of the tables mapping extra
+ location information (end line, start column offset and end column offset)
+ to every instruction in code objects. This is useful when smaller code
+ objects and pyc files are desired as well as supressing the extra visual
+ location indicators when the interpreter displays tracebacks. See also
+ :envvar:`PYTHONNODEBUGRANGES`.
It also allows passing arbitrary values and retrieving them through the
:data:`sys._xoptions` dictionary.
@@ -509,6 +515,9 @@ Miscellaneous options
.. deprecated-removed:: 3.9 3.10
The ``-X oldparser`` option.
+ .. versionadded:: 3.11
+ The ``-X no_debug_ranges`` option.
+
Options you shouldn't use
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -936,6 +945,17 @@ conflict.
.. versionadded:: 3.10
+.. envvar:: PYTHONNODEBUGRANGES
+
+ If this variable is set, it disables the inclusion of the tables mapping
+ extra location information (end line, start column offset and end column
+ offset) to every instruction in code objects. This is useful when smaller
+ code objects and pyc files are desired as well as supressing the extra visual
+ location indicators when the interpreter displays tracebacks.
+
+ .. versionadded:: 3.11
+
+
Debug-mode variables
~~~~~~~~~~~~~~~~~~~~
diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h
index 09f9a29..5f03b8c 100644
--- a/Include/cpython/initconfig.h
+++ b/Include/cpython/initconfig.h
@@ -140,6 +140,7 @@ typedef struct PyConfig {
int faulthandler;
int tracemalloc;
int import_time;
+ int no_debug_ranges;
int show_ref_count;
int dump_refs;
int malloc_stats;
diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py
index b289fa7..d859ffc 100644
--- a/Lib/idlelib/idle_test/test_run.py
+++ b/Lib/idlelib/idle_test/test_run.py
@@ -3,7 +3,7 @@
from idlelib import run
import io
import sys
-from test.support import captured_output, captured_stderr
+from test.support import captured_output, captured_stderr, has_no_debug_ranges
import unittest
from unittest import mock
import idlelib
@@ -33,9 +33,14 @@ class ExceptionTest(unittest.TestCase):
run.print_exception()
tb = output.getvalue().strip().splitlines()
- self.assertEqual(13, len(tb))
- self.assertIn('UnhashableException: ex2', tb[4])
- self.assertIn('UnhashableException: ex1', tb[12])
+ if has_no_debug_ranges():
+ self.assertEqual(11, len(tb))
+ self.assertIn('UnhashableException: ex2', tb[3])
+ self.assertIn('UnhashableException: ex1', tb[10])
+ else:
+ self.assertEqual(13, len(tb))
+ self.assertIn('UnhashableException: ex2', tb[4])
+ self.assertIn('UnhashableException: ex1', tb[12])
data = (('1/0', ZeroDivisionError, "division by zero\n"),
('abc', NameError, "name 'abc' is not defined. "
diff --git a/Lib/test/_test_embed_set_config.py b/Lib/test/_test_embed_set_config.py
index 82c5d82..23c927e 100644
--- a/Lib/test/_test_embed_set_config.py
+++ b/Lib/test/_test_embed_set_config.py
@@ -61,6 +61,7 @@ class SetConfigTests(unittest.TestCase):
'faulthandler',
'tracemalloc',
'import_time',
+ 'no_debug_ranges',
'show_ref_count',
'dump_refs',
'malloc_stats',
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 933c2c9..59b8f44 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -415,6 +415,14 @@ def requires_lzma(reason='requires lzma'):
lzma = None
return unittest.skipUnless(lzma, reason)
+def has_no_debug_ranges():
+ import _testinternalcapi
+ config = _testinternalcapi.get_config()
+ return bool(config['no_debug_ranges'])
+
+def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
+ return unittest.skipIf(has_no_debug_ranges(), reason)
+
requires_legacy_unicode_capi = unittest.skipUnless(unicode_legacy_string,
'requires legacy Unicode C API')
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py
index ccb8da6..988790b 100644
--- a/Lib/test/test_code.py
+++ b/Lib/test/test_code.py
@@ -137,7 +137,8 @@ try:
except ImportError:
ctypes = None
from test.support import (run_doctest, run_unittest, cpython_only,
- check_impl_detail)
+ check_impl_detail, requires_debug_ranges)
+from test.support.script_helper import assert_python_ok
def consts(t):
@@ -325,6 +326,7 @@ class CodeTest(unittest.TestCase):
new_code = code = func.__code__.replace(co_linetable=b'')
self.assertEqual(list(new_code.co_lines()), [])
+ @requires_debug_ranges()
def test_co_positions_artificial_instructions(self):
import dis
@@ -372,8 +374,32 @@ class CodeTest(unittest.TestCase):
]
)
+ def test_endline_and_columntable_none_when_no_debug_ranges(self):
+ # Make sure that if `-X no_debug_ranges` is used, the endlinetable and
+ # columntable are None.
+ code = textwrap.dedent("""
+ def f():
+ pass
+
+ assert f.__code__.co_endlinetable is None
+ assert f.__code__.co_columntable is None
+ """)
+ assert_python_ok('-X', 'no_debug_ranges', '-c', code, __cleanenv=True)
+
+ def test_endline_and_columntable_none_when_no_debug_ranges_env(self):
+ # Same as above but using the environment variable opt out.
+ code = textwrap.dedent("""
+ def f():
+ pass
+
+ assert f.__code__.co_endlinetable is None
+ assert f.__code__.co_columntable is None
+ """)
+ assert_python_ok('-c', code, PYTHONNODEBUGRANGES='1', __cleanenv=True)
+
# co_positions behavior when info is missing.
+ @requires_debug_ranges()
def test_co_positions_empty_linetable(self):
def func():
x = 1
@@ -382,6 +408,7 @@ class CodeTest(unittest.TestCase):
self.assertIsNone(line)
self.assertEqual(end_line, new_code.co_firstlineno + 1)
+ @requires_debug_ranges()
def test_co_positions_empty_endlinetable(self):
def func():
x = 1
@@ -390,6 +417,7 @@ class CodeTest(unittest.TestCase):
self.assertEqual(line, new_code.co_firstlineno + 1)
self.assertIsNone(end_line)
+ @requires_debug_ranges()
def test_co_positions_empty_columntable(self):
def func():
x = 1
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index bc8c57d..c994741 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -9,7 +9,7 @@ import tempfile
import types
import textwrap
from test import support
-from test.support import script_helper
+from test.support import script_helper, requires_debug_ranges
from test.support.os_helper import FakePath
@@ -985,7 +985,7 @@ if 1:
elif instr.opname in HANDLED_JUMPS:
self.assertNotEqual(instr.arg, (line + 1)*INSTR_SIZE)
-
+@requires_debug_ranges()
class TestSourcePositions(unittest.TestCase):
# Ensure that compiled code snippets have correct line and column numbers
# in `co_positions()`.
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 54a123e..d1f1eee 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1,6 +1,6 @@
# Minimal tests for dis module
-from test.support import captured_stdout
+from test.support import captured_stdout, requires_debug_ranges
from test.support.bytecode_helper import BytecodeTestCase
import unittest
import sys
@@ -1192,6 +1192,7 @@ class InstructionTests(InstructionTestCase):
actual = dis.get_instructions(jumpy, first_line=expected_jumpy_line)
self.assertInstructionsEqual(list(actual), expected_opinfo_jumpy)
+ @requires_debug_ranges()
def test_co_positions(self):
code = compile('f(\n x, y, z\n)', '<test>', 'exec')
positions = [
@@ -1210,6 +1211,7 @@ class InstructionTests(InstructionTestCase):
]
self.assertEqual(positions, expected)
+ @requires_debug_ranges()
def test_co_positions_missing_info(self):
code = compile('x, y, z', '<test>', 'exec')
code_without_column_table = code.replace(co_columntable=b'')
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 06d9d5d..642188f 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -2808,10 +2808,12 @@ out of the binary module.
try:
os.fsencode("foo-bär@baz.py")
+ supports_unicode = True
except UnicodeEncodeError:
# Skip the test: the filesystem encoding is unable to encode the filename
- pass
-else:
+ supports_unicode = False
+
+if supports_unicode and not support.has_no_debug_ranges():
def test_unicode(): """
Check doctest with a non-ascii filename:
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index c50defd..8e3dd50 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -369,6 +369,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'faulthandler': 0,
'tracemalloc': 0,
'import_time': 0,
+ 'no_debug_ranges': 0,
'show_ref_count': 0,
'dump_refs': 0,
'malloc_stats': 0,
@@ -798,6 +799,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'hash_seed': 123,
'tracemalloc': 2,
'import_time': 1,
+ 'no_debug_ranges': 1,
'show_ref_count': 1,
'malloc_stats': 1,
@@ -858,6 +860,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'hash_seed': 42,
'tracemalloc': 2,
'import_time': 1,
+ 'no_debug_ranges': 1,
'malloc_stats': 1,
'inspect': 1,
'optimization_level': 2,
@@ -887,6 +890,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'hash_seed': 42,
'tracemalloc': 2,
'import_time': 1,
+ 'no_debug_ranges': 1,
'malloc_stats': 1,
'inspect': 1,
'optimization_level': 2,
diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py
index 7bcf8e8..152301f 100644
--- a/Lib/test/test_marshal.py
+++ b/Lib/test/test_marshal.py
@@ -1,5 +1,6 @@
from test import support
-from test.support import os_helper
+from test.support import os_helper, requires_debug_ranges
+from test.support.script_helper import assert_python_ok
import array
import io
import marshal
@@ -7,6 +8,7 @@ import sys
import unittest
import os
import types
+import textwrap
try:
import _testcapi
@@ -126,6 +128,31 @@ class CodeTestCase(unittest.TestCase):
self.assertEqual(co1.co_filename, "f1")
self.assertEqual(co2.co_filename, "f2")
+ @requires_debug_ranges()
+ def test_no_columntable_and_endlinetable_with_no_debug_ranges(self):
+ # Make sure when demarshalling objects with `-X no_debug_ranges`
+ # that the columntable and endlinetable are None.
+ co = ExceptionTestCase.test_exceptions.__code__
+ code = textwrap.dedent("""
+ import sys
+ import marshal
+ with open(sys.argv[1], 'rb') as f:
+ co = marshal.load(f)
+
+ assert co.co_endlinetable is None
+ assert co.co_columntable is None
+ """)
+
+ try:
+ with open(os_helper.TESTFN, 'wb') as f:
+ marshal.dump(co, f)
+
+ assert_python_ok('-X', 'no_debug_ranges',
+ '-c', code, os_helper.TESTFN,
+ __cleanenv=True)
+ finally:
+ os_helper.unlink(os_helper.TESTFN)
+
@support.cpython_only
def test_same_filename_used(self):
s = """def f(): pass\ndef g(): pass"""
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 54f592a..610ae3f 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -8,9 +8,10 @@ import inspect
import unittest
import re
from test import support
-from test.support import Error, captured_output, cpython_only, ALWAYS_EQ
+from test.support import (Error, captured_output, cpython_only, ALWAYS_EQ,
+ requires_debug_ranges, has_no_debug_ranges)
from test.support.os_helper import TESTFN, unlink
-from test.support.script_helper import assert_python_ok
+from test.support.script_helper import assert_python_ok, assert_python_failure
import textwrap
import traceback
@@ -75,6 +76,49 @@ class TracebackCases(unittest.TestCase):
self.assertEqual(len(err), 3)
self.assertEqual(err[1].strip(), "bad syntax")
+ def test_no_caret_with_no_debug_ranges_flag(self):
+ # Make sure that if `-X no_debug_ranges` is used, there are no carets
+ # in the traceback.
+ try:
+ with open(TESTFN, 'w') as f:
+ f.write("x = 1 / 0\n")
+
+ _, _, stderr = assert_python_failure(
+ '-X', 'no_debug_ranges', TESTFN, __cleanenv=True)
+
+ lines = stderr.splitlines()
+ self.assertEqual(len(lines), 4)
+ self.assertEqual(lines[0], b'Traceback (most recent call last):')
+ self.assertIn(b'line 1, in <module>', lines[1])
+ self.assertEqual(lines[2], b' x = 1 / 0')
+ self.assertEqual(lines[3], b'ZeroDivisionError: division by zero')
+ finally:
+ unlink(TESTFN)
+
+ def test_no_caret_with_no_debug_ranges_flag_python_traceback(self):
+ code = textwrap.dedent("""
+ import traceback
+ try:
+ x = 1 / 0
+ except:
+ traceback.print_exc()
+ """)
+ try:
+ with open(TESTFN, 'w') as f:
+ f.write(code)
+
+ _, _, stderr = assert_python_ok(
+ '-X', 'no_debug_ranges', TESTFN, __cleanenv=True)
+
+ lines = stderr.splitlines()
+ self.assertEqual(len(lines), 4)
+ self.assertEqual(lines[0], b'Traceback (most recent call last):')
+ self.assertIn(b'line 4, in <module>', lines[1])
+ self.assertEqual(lines[2], b' x = 1 / 0')
+ self.assertEqual(lines[3], b'ZeroDivisionError: division by zero')
+ finally:
+ unlink(TESTFN)
+
def test_bad_indentation(self):
err = self.get_exception_format(self.syntax_error_bad_indentation,
IndentationError)
@@ -155,9 +199,10 @@ class TracebackCases(unittest.TestCase):
self.assertTrue(stdout[2].endswith(err_line),
"Invalid traceback line: {0!r} instead of {1!r}".format(
stdout[2], err_line))
- self.assertTrue(stdout[4] == err_msg,
+ actual_err_msg = stdout[3 if has_no_debug_ranges() else 4]
+ self.assertTrue(actual_err_msg == err_msg,
"Invalid error message: {0!r} instead of {1!r}".format(
- stdout[4], err_msg))
+ actual_err_msg, err_msg))
do_test("", "foo", "ascii", 3)
for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
@@ -273,6 +318,7 @@ class TracebackCases(unittest.TestCase):
'(exc, /, value=<implicit>)')
+@requires_debug_ranges()
class TracebackErrorLocationCaretTests(unittest.TestCase):
"""
Tests for printing code error expressions as part of PEP 657
@@ -362,6 +408,7 @@ class TracebackErrorLocationCaretTests(unittest.TestCase):
@cpython_only
+@requires_debug_ranges()
class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests):
"""
Same set of tests as above but with Python's internal traceback printing.
@@ -424,9 +471,13 @@ class TracebackFormatTests(unittest.TestCase):
# Make sure that the traceback is properly indented.
tb_lines = python_fmt.splitlines()
- self.assertEqual(len(tb_lines), 7)
banner = tb_lines[0]
- location, source_line = tb_lines[-3], tb_lines[-2]
+ if has_no_debug_ranges():
+ self.assertEqual(len(tb_lines), 5)
+ location, source_line = tb_lines[-2], tb_lines[-1]
+ else:
+ self.assertEqual(len(tb_lines), 7)
+ location, source_line = tb_lines[-3], tb_lines[-2]
self.assertTrue(banner.startswith('Traceback'))
self.assertTrue(location.startswith(' File'))
self.assertTrue(source_line.startswith(' raise'))
@@ -668,10 +719,12 @@ class TracebackFormatTests(unittest.TestCase):
actual = stderr_g.getvalue().splitlines()
self.assertEqual(actual, expected)
+ @requires_debug_ranges()
def test_recursive_traceback_python(self):
self._check_recursive_traceback_display(traceback.print_exc)
@cpython_only
+ @requires_debug_ranges()
def test_recursive_traceback_cpython_internal(self):
from _testcapi import exception_print
def render_exc():
@@ -713,11 +766,16 @@ class TracebackFormatTests(unittest.TestCase):
exception_print(exc_val)
tb = stderr_f.getvalue().strip().splitlines()
- self.assertEqual(13, len(tb))
- self.assertEqual(context_message.strip(), tb[6])
- self.assertIn('UnhashableException: ex2', tb[4])
- self.assertIn('UnhashableException: ex1', tb[12])
-
+ if has_no_debug_ranges():
+ self.assertEqual(11, len(tb))
+ self.assertEqual(context_message.strip(), tb[5])
+ self.assertIn('UnhashableException: ex2', tb[3])
+ self.assertIn('UnhashableException: ex1', tb[10])
+ else:
+ self.assertEqual(13, len(tb))
+ self.assertEqual(context_message.strip(), tb[6])
+ self.assertIn('UnhashableException: ex2', tb[4])
+ self.assertIn('UnhashableException: ex1', tb[12])
cause_message = (
"\nThe above exception was the direct cause "
@@ -746,8 +804,12 @@ class BaseExceptionReportingTests:
def check_zero_div(self, msg):
lines = msg.splitlines()
- self.assertTrue(lines[-4].startswith(' File'))
- self.assertIn('1/0 # In zero_div', lines[-3])
+ if has_no_debug_ranges():
+ self.assertTrue(lines[-3].startswith(' File'))
+ self.assertIn('1/0 # In zero_div', lines[-2])
+ else:
+ self.assertTrue(lines[-4].startswith(' File'))
+ self.assertIn('1/0 # In zero_div', lines[-3])
self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
def test_simple(self):
@@ -756,11 +818,15 @@ class BaseExceptionReportingTests:
except ZeroDivisionError as _:
e = _
lines = self.get_report(e).splitlines()
- self.assertEqual(len(lines), 5)
+ if has_no_debug_ranges():
+ self.assertEqual(len(lines), 4)
+ self.assertTrue(lines[3].startswith('ZeroDivisionError'))
+ else:
+ self.assertEqual(len(lines), 5)
+ self.assertTrue(lines[4].startswith('ZeroDivisionError'))
self.assertTrue(lines[0].startswith('Traceback'))
self.assertTrue(lines[1].startswith(' File'))
self.assertIn('1/0 # Marker', lines[2])
- self.assertTrue(lines[4].startswith('ZeroDivisionError'))
def test_cause(self):
def inner_raise():
@@ -799,11 +865,15 @@ class BaseExceptionReportingTests:
except ZeroDivisionError as _:
e = _
lines = self.get_report(e).splitlines()
- self.assertEqual(len(lines), 5)
+ if has_no_debug_ranges():
+ self.assertEqual(len(lines), 4)
+ self.assertTrue(lines[3].startswith('ZeroDivisionError'))
+ else:
+ self.assertEqual(len(lines), 5)
+ self.assertTrue(lines[4].startswith('ZeroDivisionError'))
self.assertTrue(lines[0].startswith('Traceback'))
self.assertTrue(lines[1].startswith(' File'))
self.assertIn('ZeroDivisionError from None', lines[2])
- self.assertTrue(lines[4].startswith('ZeroDivisionError'))
def test_cause_and_context(self):
# When both a cause and a context are set, only the cause should be
@@ -1527,6 +1597,7 @@ class TestTracebackException(unittest.TestCase):
exc = traceback.TracebackException(Exception, Exception("haven"), None)
self.assertEqual(list(exc.format()), ["Exception: haven\n"])
+ @requires_debug_ranges()
def test_print(self):
def f():
x = 12
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index 861ebe3..4e88902 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -718,6 +718,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
print_tb(tb, 1, s)
self.assertTrue(s.getvalue().endswith(
' def do_raise(): raise TypeError\n'
+ '' if support.has_no_debug_ranges() else
' ^^^^^^^^^^^^^^^\n'
))
else:
diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h
index d8a95ca..ac844b1 100644
--- a/Objects/clinic/codeobject.c.h
+++ b/Objects/clinic/codeobject.c.h
@@ -130,15 +130,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto exit;
}
linetable = PyTuple_GET_ITEM(args, 14);
- if (!PyBytes_Check(PyTuple_GET_ITEM(args, 15))) {
- _PyArg_BadArgument("code", "argument 16", "bytes", PyTuple_GET_ITEM(args, 15));
- goto exit;
- }
endlinetable = PyTuple_GET_ITEM(args, 15);
- if (!PyBytes_Check(PyTuple_GET_ITEM(args, 16))) {
- _PyArg_BadArgument("code", "argument 17", "bytes", PyTuple_GET_ITEM(args, 16));
- goto exit;
- }
columntable = PyTuple_GET_ITEM(args, 16);
if (!PyBytes_Check(PyTuple_GET_ITEM(args, 17))) {
_PyArg_BadArgument("code", "argument 18", "bytes", PyTuple_GET_ITEM(args, 17));
@@ -192,10 +184,8 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
PyObject *co_varnames, PyObject *co_freevars,
PyObject *co_cellvars, PyObject *co_filename,
PyObject *co_name, PyObject *co_qualname,
- PyBytesObject *co_linetable,
- PyBytesObject *co_endlinetable,
- PyBytesObject *co_columntable,
- PyBytesObject *co_exceptiontable);
+ PyBytesObject *co_linetable, PyObject *co_endlinetable,
+ PyObject *co_columntable, PyBytesObject *co_exceptiontable);
static PyObject *
code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@@ -222,8 +212,8 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
PyObject *co_name = self->co_name;
PyObject *co_qualname = self->co_qualname;
PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable;
- PyBytesObject *co_endlinetable = (PyBytesObject *)self->co_endlinetable;
- PyBytesObject *co_columntable = (PyBytesObject *)self->co_columntable;
+ PyObject *co_endlinetable = self->co_endlinetable;
+ PyObject *co_columntable = self->co_columntable;
PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf);
@@ -406,21 +396,13 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[17]) {
- if (!PyBytes_Check(args[17])) {
- _PyArg_BadArgument("replace", "argument 'co_endlinetable'", "bytes", args[17]);
- goto exit;
- }
- co_endlinetable = (PyBytesObject *)args[17];
+ co_endlinetable = args[17];
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
if (args[18]) {
- if (!PyBytes_Check(args[18])) {
- _PyArg_BadArgument("replace", "argument 'co_columntable'", "bytes", args[18]);
- goto exit;
- }
- co_columntable = (PyBytesObject *)args[18];
+ co_columntable = args[18];
if (!--noptargs) {
goto skip_optional_kwonly;
}
@@ -473,4 +455,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n
exit:
return return_value;
}
-/*[clinic end generated code: output=12b394f0212b1c1e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=18b9ddc86714e56e input=a9049054013a1b77]*/
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 140d5a0..a5120ec 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -379,6 +379,13 @@ _PyCode_New(struct _PyCodeConstructor *con)
return NULL;
}
+ // Discard the endlinetable and columntable if we are opted out of debug
+ // ranges.
+ if (_Py_GetConfig()->no_debug_ranges) {
+ con->endlinetable = Py_None;
+ con->columntable = Py_None;
+ }
+
PyCodeObject *co = PyObject_New(PyCodeObject, &PyCode_Type);
if (co == NULL) {
PyErr_NoMemory();
@@ -1222,8 +1229,8 @@ code.__new__ as code_new
qualname: unicode
firstlineno: int
linetable: object(subclass_of="&PyBytes_Type")
- endlinetable: object(subclass_of="&PyBytes_Type")
- columntable: object(subclass_of="&PyBytes_Type")
+ endlinetable: object
+ columntable: object
exceptiontable: object(subclass_of="&PyBytes_Type")
freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = ()
cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = ()
@@ -1241,7 +1248,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
PyObject *endlinetable, PyObject *columntable,
PyObject *exceptiontable, PyObject *freevars,
PyObject *cellvars)
-/*[clinic end generated code: output=e1d2086aa8da7c08 input=ba12d68bd8fa0620]*/
+/*[clinic end generated code: output=e1d2086aa8da7c08 input=a06cd92369134063]*/
{
PyObject *co = NULL;
PyObject *ournames = NULL;
@@ -1282,6 +1289,17 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
goto cleanup;
}
+ if (!Py_IsNone(endlinetable) && !PyBytes_Check(endlinetable)) {
+ PyErr_SetString(PyExc_ValueError,
+ "code: endlinetable must be None or bytes");
+ goto cleanup;
+ }
+ if (!Py_IsNone(columntable) && !PyBytes_Check(columntable)) {
+ PyErr_SetString(PyExc_ValueError,
+ "code: columntable must be None or bytes");
+ goto cleanup;
+ }
+
ournames = validate_and_copy_tuple(names);
if (ournames == NULL)
goto cleanup;
@@ -1585,8 +1603,8 @@ code.replace
co_name: unicode(c_default="self->co_name") = None
co_qualname: unicode(c_default="self->co_qualname") = None
co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None
- co_endlinetable: PyBytesObject(c_default="(PyBytesObject *)self->co_endlinetable") = None
- co_columntable: PyBytesObject(c_default="(PyBytesObject *)self->co_columntable") = None
+ co_endlinetable: object(c_default="self->co_endlinetable") = None
+ co_columntable: object(c_default="self->co_columntable") = None
co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None
Return a copy of the code object with new values for the specified fields.
@@ -1601,11 +1619,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
PyObject *co_varnames, PyObject *co_freevars,
PyObject *co_cellvars, PyObject *co_filename,
PyObject *co_name, PyObject *co_qualname,
- PyBytesObject *co_linetable,
- PyBytesObject *co_endlinetable,
- PyBytesObject *co_columntable,
- PyBytesObject *co_exceptiontable)
-/*[clinic end generated code: output=da699b6261fddc13 input=a8e93823df0aec35]*/
+ PyBytesObject *co_linetable, PyObject *co_endlinetable,
+ PyObject *co_columntable, PyBytesObject *co_exceptiontable)
+/*[clinic end generated code: output=f046bf0be3bab91f input=a63d09f248f00794]*/
{
#define CHECK_INT_ARG(ARG) \
if (ARG < 0) { \
@@ -1657,6 +1673,17 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
co_freevars = freevars;
}
+ if (!Py_IsNone(co_endlinetable) && !PyBytes_Check(co_endlinetable)) {
+ PyErr_SetString(PyExc_ValueError,
+ "co_endlinetable must be None or bytes");
+ goto error;
+ }
+ if (!Py_IsNone(co_columntable) && !PyBytes_Check(co_columntable)) {
+ PyErr_SetString(PyExc_ValueError,
+ "co_columntable must be None or bytes");
+ goto error;
+ }
+
co = PyCode_NewWithPosOnlyArgs(
co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals,
co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names,
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 64a8714..73e1f38 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -528,6 +528,9 @@ static int test_init_from_config(void)
putenv("PYTHONPROFILEIMPORTTIME=0");
config.import_time = 1;
+ putenv("PYTHONNODEBUGRANGES=0");
+ config.no_debug_ranges = 1;
+
config.show_ref_count = 1;
/* FIXME: test dump_refs: bpo-34223 */
@@ -686,6 +689,7 @@ static void set_most_env_vars(void)
putenv("PYTHONMALLOC=malloc");
putenv("PYTHONTRACEMALLOC=2");
putenv("PYTHONPROFILEIMPORTTIME=1");
+ putenv("PYTHONNODEBUGRANGES=1");
putenv("PYTHONMALLOCSTATS=1");
putenv("PYTHONUTF8=1");
putenv("PYTHONVERBOSE=1");
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 27ae48d..d328f22 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -95,6 +95,11 @@ static const char usage_3[] = "\
-X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the\n\
given directory instead of to the code tree\n\
-X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None'\n\
+ -X no_debug_ranges: disable the inclusion of the tables mapping extra location \n\
+ information (end line, start column offset and end column offset) to every \n\
+ instruction in code objects. This is useful when smaller code objects and pyc \n\
+ files are desired as well as supressing the extra visual location indicators \n\
+ when the interpreter displays tracebacks.\n\
\n\
--check-hash-based-pycs always|default|never:\n\
control how Python invalidates hash-based .pyc files\n\
@@ -131,7 +136,12 @@ static const char usage_6[] =
" debugger. It can be set to the callable of your debugger of choice.\n"
"PYTHONDEVMODE: enable the development mode.\n"
"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n"
-"PYTHONWARNDEFAULTENCODING: enable opt-in EncodingWarning for 'encoding=None'.\n";
+"PYTHONWARNDEFAULTENCODING: enable opt-in EncodingWarning for 'encoding=None'.\n"
+"PYTHONNODEBUGRANGES: If this variable is set, it disables the inclusion of the \n"
+" tables mapping extra location information (end line, start column offset \n"
+" and end column offset) to every instruction in code objects. This is useful \n"
+" when smaller cothe de objects and pyc files are desired as well as supressing the \n"
+" extra visual location indicators when the interpreter displays tracebacks.\n";
#if defined(MS_WINDOWS)
# define PYTHONHOMEHELP "<prefix>\\python{major}{minor}"
@@ -597,6 +607,7 @@ config_check_consistency(const PyConfig *config)
assert(config->faulthandler >= 0);
assert(config->tracemalloc >= 0);
assert(config->import_time >= 0);
+ assert(config->no_debug_ranges >= 0);
assert(config->show_ref_count >= 0);
assert(config->dump_refs >= 0);
assert(config->malloc_stats >= 0);
@@ -884,6 +895,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_ATTR(faulthandler);
COPY_ATTR(tracemalloc);
COPY_ATTR(import_time);
+ COPY_ATTR(no_debug_ranges);
COPY_ATTR(show_ref_count);
COPY_ATTR(dump_refs);
COPY_ATTR(malloc_stats);
@@ -988,6 +1000,7 @@ _PyConfig_AsDict(const PyConfig *config)
SET_ITEM_INT(faulthandler);
SET_ITEM_INT(tracemalloc);
SET_ITEM_INT(import_time);
+ SET_ITEM_INT(no_debug_ranges);
SET_ITEM_INT(show_ref_count);
SET_ITEM_INT(dump_refs);
SET_ITEM_INT(malloc_stats);
@@ -1264,6 +1277,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
GET_UINT(faulthandler);
GET_UINT(tracemalloc);
GET_UINT(import_time);
+ GET_UINT(no_debug_ranges);
GET_UINT(show_ref_count);
GET_UINT(dump_refs);
GET_UINT(malloc_stats);
@@ -1802,6 +1816,11 @@ config_read_complex_options(PyConfig *config)
config->import_time = 1;
}
+ if (config_get_env(config, "PYTHONNODEBUGRANGES")
+ || config_get_xoption(config, L"no_debug_ranges")) {
+ config->no_debug_ranges = 1;
+ }
+
PyStatus status;
if (config->tracemalloc < 0) {
status = config_init_tracemalloc(config);