8000 gh-82005: Properly handle user input warnings in IDLE shell. by terryjreedy · Pull Request #15311 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-82005: Properly handle user input warnings in IDLE shell. #15311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/idlelib/idle_test/test_warning.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
Line of code
UserWarning: Test
'''
shellmsg = idlemsg + ">>> "
shellmsg = idlemsg


class RunWarnTest(unittest.TestCase):
Expand Down
34 changes: 17 additions & 17 deletions Lib/idlelib/pyshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,24 @@
quit.eof = eof
except NameError: # In case python started with -S.
pass

# Override warnings module to write to warning_stream. Initialize to send IDLE
# internal warnings to the console. ScriptBinding.check_syntax() will
# temporarily redirect the stream to the shell window to display warnings when
# checking user's code.

# Override warnings.show_warning to write to IDLE format to warning_stream.
# Initially send IDLE internal warnings to the console, if present.
# Send to Shell.stderr (traceback stream) when available.
warning_stream = sys.__stderr__ # None, at least on Windows, if no console.

def idle_showwarning(
message, category, filename, lineno, file=None, line=None):
"""Show Idle-format warning (after replacing warnings.showwarning).
"""Print Idle-format warning to warning_stream.

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.
Difference from show_warning are the formatter, the file=None
replacement, and the capture of the consequence AttributeError.
"""
if file is None:
file = warning_stream
try:
file.write(idle_formatwarning(
message, category, filename, lineno, line=line))
file.write(">>> ")
except (AttributeError, OSError):
pass # if file (probably __stderr__) is invalid, skip warning.

Expand Down Expand Up @@ -684,13 +681,12 @@ def execfile(self, filename, source=None):
self.runcode(code)

def runsource(self, source):
"Extend base class method: Stuff the source in the line cache first"
"Return overriden base call after caching source."
filename = self.stuffsource(source)
# at the moment, InteractiveInterpreter expects str
assert isinstance(source, str)
# InteractiveInterpreter.runsource() calls its runcode() method,
# which is overridden (see below)
return InteractiveInterpreter.runsource(self, source, filename)
# II.runsource() calls .runcode(), overridden below.
with warnings.catch_warnings():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great!

Just remember to remove the commented out code above before merging...

Copy link
Member Author
@terryjreedy terryjreedy May 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I created the PR on 2019 Aug 16, runsource comprised stuffsource(), a comment and assert, the inactivated code, and a try-finally block that wapped the override comment II.runsource() and and that fiddled with warnings in 'finally'.

The comment and assert were added and the code inactivated by MvL in 2007. The commit message was "Expect that source strings are unicode". Since str was bytes at the time, the message, comment, and assert do not make much sense to me. In any case, the comment and assert can be deleted. If IDLE were to pass bytes, there would be an error anyway.

I removed the inactivated code on Sept 16 in #16198 for bpo-38183 as an unrelated change. (A separate PR would have been better.) I only left it in the merge resolution because I was not sure then it should go (it should).

The try-finally was from KBK in 2001 and 2004. On Aug 26, in #15500 for this same issue (bpo-37824), I redid shell warnings and removed 'try' and the finally block, leaving the comment and II.runsource(). As for Cheryl's addition of a warnings filter, I will test and see if it should go in the warnings code to be executed just once and before running any user code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that II.runsource is only use for code input after '>>>'. For instance, 'python -m idlelib -c "0 is "' only prints the warning once even without the filter. The patch otherwise moves it from the console to Shell. The current file level warnings manipulation does not save and restore filters. So I will leave the filter here until such time as code or codeop are patched (see issue) to make it unneeded in IDLE.

warnings.filterwarnings('once')
return InteractiveInterpreter.runsource(self, source, filename)

def stuffsource(self, source):
"Stuff source in the filename cache"
Expand Down Expand Up @@ -937,6 +933,8 @@ def __init__(self, flist=None):
sys.stdout = self.stdout
sys.stderr = self.stderr
sys.stdin = self.stdin
global warning_stream
warning_stream = self.stderr
try:
# page help() text to shell.
import pydoc # import must be done here to capture i/o rebinding.
Expand Down Expand Up @@ -1119,10 +1117,12 @@ def _close(self):
self.close_debugger()
if use_subprocess:
self.interp.kill_subprocess()
# Restore std streams
# Restore std streams and warning_stream
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
sys.stdin = self.save_stdin
global warning_stream
warning_stream = sys.__stderr__
# Break cycles
self.interp = None
self.console = None
Expand Down
0