Commit 8eab008b authored by Terry Jan Reedy's avatar Terry Jan Reedy

Issue *18081, #18242: Change Idle warnings capture in PyShell and run to stop

replacing warnings.formatwarnings and to reverse replacement of
warnings.showwarnings when import is complete and when main function exits.
Add test_warning.py. Vinay Sajip provided capture_warnings function.
parent 35115e6a
...@@ -50,25 +50,12 @@ except ImportError: ...@@ -50,25 +50,12 @@ except ImportError:
# internal warnings to the console. ScriptBinding.check_syntax() will # internal warnings to the console. ScriptBinding.check_syntax() will
# temporarily redirect the stream to the shell window to display warnings when # temporarily redirect the stream to the shell window to display warnings when
# checking user's code. # checking user's code.
global warning_stream warning_stream = sys.__stderr__ # None, at least on Windows, if no console.
warning_stream = sys.__stderr__ import warnings
try:
import warnings def idle_formatwarning(message, category, filename, lineno, line=None):
except ImportError: """Format warnings the IDLE way."""
pass
else:
def idle_showwarning(message, category, filename, lineno,
file=None, line=None):
if file is None:
file = warning_stream
try:
file.write(warnings.formatwarning(message, category, filename,
lineno, line=line))
except IOError:
pass ## file (probably __stderr__) is invalid, warning dropped.
warnings.showwarning = idle_showwarning
def idle_formatwarning(message, category, filename, lineno, line=None):
"""Format warnings the IDLE way"""
s = "\nWarning (from warnings module):\n" s = "\nWarning (from warnings module):\n"
s += ' File \"%s\", line %s\n' % (filename, lineno) s += ' File \"%s\", line %s\n' % (filename, lineno)
if line is None: if line is None:
...@@ -76,9 +63,42 @@ else: ...@@ -76,9 +63,42 @@ else:
line = line.strip() line = line.strip()
if line: if line:
s += " %s\n" % line s += " %s\n" % line
s += "%s: %s\n>>> " % (category.__name__, message) s += "%s: %s\n" % (category.__name__, message)
return s return s
warnings.formatwarning = idle_formatwarning
def idle_showwarning(
message, category, filename, lineno, file=None, line=None):
"""Show Idle-format warning (after replacing warnings.showwarning).
The differences are the formatter called, the file=None replacement,
which can be None, the capture of the consequence AttributeError,
and the output of a hard-coded prompt.
"""
if file is None:
file = warning_stream
try:
file.write(idle_formatwarning(
message, category, filename, lineno, line=line))
file.write(">>> ")
except (AttributeError, IOError):
pass # if file (probably __stderr__) is invalid, skip warning.
_warnings_showwarning = None
def capture_warnings(capture):
"Replace warning.showwarning with idle_showwarning, or reverse."
global _warnings_showwarning
if capture:
if _warnings_showwarning is None:
_warnings_showwarning = warnings.showwarning
warnings.showwarning = idle_showwarning
else:
if _warnings_showwarning is not None:
warnings.showwarning = _warnings_showwarning
_warnings_showwarning = None
capture_warnings(True)
def extended_linecache_checkcache(filename=None, def extended_linecache_checkcache(filename=None,
orig_checkcache=linecache.checkcache): orig_checkcache=linecache.checkcache):
...@@ -1428,6 +1448,7 @@ echo "import sys; print sys.argv" | idle - "foobar" ...@@ -1428,6 +1448,7 @@ echo "import sys; print sys.argv" | idle - "foobar"
def main(): def main():
global flist, root, use_subprocess global flist, root, use_subprocess
capture_warnings(True)
use_subprocess = True use_subprocess = True
enable_shell = False enable_shell = False
enable_edit = False enable_edit = False
...@@ -1560,7 +1581,10 @@ def main(): ...@@ -1560,7 +1581,10 @@ def main():
while flist.inversedict: # keep IDLE running while files are open. while flist.inversedict: # keep IDLE running while files are open.
root.mainloop() root.mainloop()
root.destroy() root.destroy()
capture_warnings(False)
if __name__ == "__main__": if __name__ == "__main__":
sys.modules['PyShell'] = sys.modules['__main__'] sys.modules['PyShell'] = sys.modules['__main__']
main() main()
capture_warnings(False) # Make sure turned off; see issue 18081
'''Test warnings replacement in PyShell.py and run.py.
This file could be expanded to include traceback overrides
(in same two modules). If so, change name.
Revise if output destination changes (http://bugs.python.org/issue18318).
Make sure warnings module is left unaltered (http://bugs.python.org/issue18081).
'''
import unittest
from test.test_support import captured_stderr
import warnings
# Try to capture default showwarning before Idle modules are imported.
showwarning = warnings.showwarning
# But if we run this file within idle, we are in the middle of the run.main loop
# and default showwarnings has already been replaced.
running_in_idle = 'idle' in showwarning.__name__
from idlelib import run
from idlelib import PyShell as shell
# The following was generated from PyShell.idle_formatwarning
# and checked as matching expectation.
idlemsg = '''
Warning (from warnings module):
File "test_warning.py", line 99
Line of code
UserWarning: Test
'''
shellmsg = idlemsg + ">>> "
class RunWarnTest(unittest.TestCase):
@unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
def test_showwarnings(self):
self.assertIs(warnings.showwarning, showwarning)
run.capture_warnings(True)
self.assertIs(warnings.showwarning, run.idle_showwarning_subproc)
run.capture_warnings(False)
self.assertIs(warnings.showwarning, showwarning)
def test_run_show(self):
with captured_stderr() as f:
run.idle_showwarning_subproc(
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
# The following uses .splitlines to erase line-ending differences
self.assertEqual(idlemsg.splitlines(), f.getvalue().splitlines())
class ShellWarnTest(unittest.TestCase):
@unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
def test_showwarnings(self):
self.assertIs(warnings.showwarning, showwarning)
shell.capture_warnings(True)
self.assertIs(warnings.showwarning, shell.idle_showwarning)
shell.capture_warnings(False)
self.assertIs(warnings.showwarning, showwarning)
def test_idle_formatter(self):
# Will fail if format changed without regenerating idlemsg
s = shell.idle_formatwarning(
'Test', UserWarning, 'test_warning.py', 99, 'Line of code')
self.assertEqual(idlemsg, s)
def test_shell_show(self):
with captured_stderr() as f:
shell.idle_showwarning(
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
self.assertEqual(shellmsg.splitlines(), f.getvalue().splitlines())
if __name__ == '__main__':
unittest.main(verbosity=2, exit=False)
...@@ -22,24 +22,38 @@ import __main__ ...@@ -22,24 +22,38 @@ import __main__
LOCALHOST = '127.0.0.1' LOCALHOST = '127.0.0.1'
try: import warnings
import warnings
except ImportError: def idle_showwarning_subproc(
pass message, category, filename, lineno, file=None, line=None):
else: """Show Idle-format warning after replacing warnings.showwarning.
def idle_formatwarning_subproc(message, category, filename, lineno,
line=None): The only difference is the formatter called.
"""Format warnings the IDLE way""" """
s = "\nWarning (from warnings module):\n" if file is None:
s += ' File \"%s\", line %s\n' % (filename, lineno) file = sys.stderr
if line is None: try:
line = linecache.getline(filename, lineno) file.write(PyShell.idle_formatwarning(
line = line.strip() message, category, filename, lineno, line))
if line: except IOError:
s += " %s\n" % line pass # the file (probably stderr) is invalid - this warning gets lost.
s += "%s: %s\n" % (category.__name__, message)
return s _warnings_showwarning = None
warnings.formatwarning = idle_formatwarning_subproc
def capture_warnings(capture):
"Replace warning.showwarning with idle_showwarning_subproc, or reverse."
global _warnings_showwarning
if capture:
if _warnings_showwarning is None:
_warnings_showwarning = warnings.showwarning
warnings.showwarning = idle_showwarning_subproc
else:
if _warnings_showwarning is not None:
warnings.showwarning = _warnings_showwarning
_warnings_showwarning = None
capture_warnings(True)
# Thread shared globals: Establish a queue between a subthread (which handles # Thread shared globals: Establish a queue between a subthread (which handles
# the socket) and the main thread (which runs user code), plus global # the socket) and the main thread (which runs user code), plus global
...@@ -78,6 +92,8 @@ def main(del_exitfunc=False): ...@@ -78,6 +92,8 @@ def main(del_exitfunc=False):
except: except:
print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv." print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv."
return return
capture_warnings(True)
sys.argv[:] = [""] sys.argv[:] = [""]
sockthread = threading.Thread(target=manage_socket, sockthread = threading.Thread(target=manage_socket,
name='SockThread', name='SockThread',
...@@ -104,6 +120,7 @@ def main(del_exitfunc=False): ...@@ -104,6 +120,7 @@ def main(del_exitfunc=False):
exit_now = True exit_now = True
continue continue
except SystemExit: except SystemExit:
capture_warnings(False)
raise raise
except: except:
type, value, tb = sys.exc_info() type, value, tb = sys.exc_info()
...@@ -219,6 +236,7 @@ def exit(): ...@@ -219,6 +236,7 @@ def exit():
del sys.exitfunc del sys.exitfunc
except AttributeError: except AttributeError:
pass pass
capture_warnings(False)
sys.exit(0) sys.exit(0)
class MyRPCServer(rpc.RPCServer): class MyRPCServer(rpc.RPCServer):
...@@ -352,3 +370,5 @@ class Executive(object): ...@@ -352,3 +370,5 @@ class Executive(object):
sys.last_value = val sys.last_value = val
item = StackViewer.StackTreeItem(flist, tb) item = StackViewer.StackTreeItem(flist, tb)
return RemoteObjectBrowser.remote_object_tree_item(item) return RemoteObjectBrowser.remote_object_tree_item(item)
capture_warnings(False) # Make sure turned off; see issue 18081
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment