mirror of https://github.com/python/cpython
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:
parent
3d3027c5fc
commit
4823d9a512
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. "
|
||||
|
|
|
@ -61,6 +61,7 @@ class SetConfigTests(unittest.TestCase):
|
|||
'faulthandler',
|
||||
'tracemalloc',
|
||||
'import_time',
|
||||
'no_debug_ranges',
|
||||
'show_ref_count',
|
||||
'dump_refs',
|
||||
'malloc_stats',
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()`.
|
||||
|
|
|
@ -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'')
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue