Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9d279b8

Browse files
committed
(Merge 3.4) Issue #22599: Enhance tokenize.open() to be able to call it during
Python finalization. Before the module kept a reference to the builtins module, but the module attributes are cleared during Python finalization. Instead, keep directly a reference to the open() function. This enhancement is not perfect, calling tokenize.open() can still fail if called very late during Python finalization. Usually, the function is called by the linecache module which is called to display a traceback or emit a warning.
2 parents 02d37d5 + 9691750 commit 9d279b8

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

Lib/test/test_traceback.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import re
77
from test.support import run_unittest, Error, captured_output
88
from test.support import TESTFN, unlink, cpython_only
9+
from test.script_helper import assert_python_ok
10+
import textwrap
911

1012
import traceback
1113

@@ -169,6 +171,37 @@ def do_test(firstlines, message, charset, lineno):
169171
# Issue #18960: coding spec should has no effect
170172
do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
171173

174+
def test_print_traceback_at_exit(self):
175+
# Issue #22599: Ensure that it is possible to use the traceback module
176+
# to display an exception at Python exit
177+
code = textwrap.dedent("""
178+
import sys
179+
import traceback
180+
181+
class PrintExceptionAtExit(object):
182+
def __init__(self):
183+
try:
184+
x = 1 / 0
185+
except Exception:
186+
self.exc_info = sys.exc_info()
187+
# self.exc_info[1] (traceback) contains frames:
188+
# explicitly clear the reference to self in the current
189+
# frame to break a reference cycle
190+
self = None
191+
192+
def __del__(self):
193+
traceback.print_exception(*self.exc_info)
194+
195+
# Keep a reference in the module namespace to call the destructor
196+
# when the module is unloaded
197+
obj = PrintExceptionAtExit()
198+
""")
199+
rc, stdout, stderr = assert_python_ok('-c', code)
200+
expected = [b'Traceback (most recent call last):',
201+
b' File "<string>", line 8, in __init__',
202+
b'ZeroDivisionError: division by zero']
203+
self.assertEqual(stderr.splitlines(), expected)
204+
172205

173206
class TracebackFormatTests(unittest.TestCase):
174207

Lib/tokenize.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
__credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, '
2525
'Skip Montanaro, Raymond Hettinger, Trent Nelson, '
2626
'Michael Foord')
27-
import builtins
2827
from codecs import lookup, BOM_UTF8
2928
import collections
3029
from io import TextIOWrapper
@@ -431,11 +430,13 @@ def find_cookie(line):
431430
return default, [first, second]
432431

433432

433+
_builtin_open = open
434+
434435
def open(filename):
435436
"""Open a file in read only mode using the encoding detected by
436437
detect_encoding().
437438
"""
438-
buffer = builtins.open(filename, 'rb')
439+
buffer = _builtin_open(filename, 'rb')
439440
encoding, lines = detect_encoding(buffer.readline)
440441
buffer.seek(0)
441442
text = TextIOWrapper(buffer, encoding, line_buffering=True)
@@ -658,7 +659,7 @@ def error(message, filename=None, location=None):
658659
# Tokenize the input
659660
if args.filename:
660661
filename = args.filename
661-
with builtins.open(filename, 'rb') as f:
662+
with _builtin_open(filename, 'rb') as f:
662663
tokens = list(tokenize(f.readline))
663664
else:
664665
filename = "<stdin>"

0 commit comments

Comments
 (0)