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>
This commit is contained in:
Ammar Askar 2021-07-07 15:07:12 -04:00 committed by GitHub
parent 3d3027c5fc
commit 4823d9a512
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 276 additions and 64 deletions

View file

@ -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:

View file

@ -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
~~~~~~~~~~~~~~~~~~~~

View file

@ -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;

View file

@ -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 @@ def __eq__(self, other):
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. "

View file

@ -61,6 +61,7 @@ def test_set_invalid(self):
'faulthandler',
'tracemalloc',
'import_time',
'no_debug_ranges',
'show_ref_count',
'dump_refs',
'malloc_stats',

View file

@ -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')

View file

@ -137,7 +137,8 @@
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 @@ def func():
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 @@ def test_co_positions_artificial_instructions(self):
]
)
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 @@ def func():
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 @@ def func():
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

View file

@ -9,7 +9,7 @@
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 @@ def if_else_break():
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()`.

View file

@ -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 @@ def test_jumpy(self):
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 @@ def test_co_positions(self):
]
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'')

View file

@ -2808,10 +2808,12 @@ def test_testmod(): r"""
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:

View file

@ -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 @@ def test_init_from_config(self):
'hash_seed': 123,
'tracemalloc': 2,
'import_time': 1,
'no_debug_ranges': 1,
'show_ref_count': 1,
'malloc_stats': 1,
@ -858,6 +860,7 @@ def test_init_compat_env(self):
'hash_seed': 42,
'tracemalloc': 2,
'import_time': 1,
'no_debug_ranges': 1,
'malloc_stats': 1,
'inspect': 1,
'optimization_level': 2,
@ -887,6 +890,7 @@ def test_init_python_env(self):
'hash_seed': 42,
'tracemalloc': 2,
'import_time': 1,
'no_debug_ranges': 1,
'malloc_stats': 1,
'inspect': 1,
'optimization_level': 2,

View file

@ -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 unittest
import os
import types
import textwrap
try:
import _testcapi
@ -126,6 +128,31 @@ def test_different_filenames(self):
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"""

View file

@ -8,9 +8,10 @@
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 @@ def test_nocaret(self):
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 @@ def do_test(firstlines, message, charset, lineno):
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 @@ def test_signatures(self):
'(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 @@ def f_with_multiline():
@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 @@ def check_traceback_format(self, cleanup_func=None):
# 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 @@ def h(count=10):
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 @@ def __eq__(self, other):
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 @@ def zero_div(self):
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 @@ def test_simple(self):
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 @@ def test_context_suppression(self):
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 @@ def test_traceback_header(self):
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

View file

@ -718,6 +718,7 @@ def doTraceback(self, module):
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:

View file

@ -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]*/

View file

@ -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,

View file

@ -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");

View file

@ -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);