diff options
author | Cheryl Sabella <cheryl.sabella@gmail.com> | 2017-08-27 22:06:00 (GMT) |
---|---|---|
committer | Terry Jan Reedy <tjreedy@udel.edu> | 2017-08-27 22:06:00 (GMT) |
commit | 998f4966bf0c591f3e8b3d07eccad7501f60f524 (patch) | |
tree | c7b4bfc4b50dc6d5be1f2b388c84b3c15009d275 /Lib/idlelib/outwin.py | |
parent | 3457f428964d0fd6ab601272ead276a9bf8b1eaf (diff) | |
download | cpython-998f4966bf0c591f3e8b3d07eccad7501f60f524.zip cpython-998f4966bf0c591f3e8b3d07eccad7501f60f524.tar.gz cpython-998f4966bf0c591f3e8b3d07eccad7501f60f524.tar.bz2 |
bpo-30617: IDLE: docstrings and unittest for outwin.py (#2046)
Move some data and functions from the class to module level. Patch by Cheryl Sabella.
Diffstat (limited to 'Lib/idlelib/outwin.py')
-rw-r--r-- | Lib/idlelib/outwin.py | 164 |
1 files changed, 102 insertions, 62 deletions
diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index f6d2915..5f7c09f 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -1,43 +1,113 @@ +"""Editor window that can serve as an output file. +""" + import re -from tkinter import * -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox from idlelib.editor import EditorWindow from idlelib import iomenu -class OutputWindow(EditorWindow): +file_line_pats = [ + # order of patterns matters + r'file "([^"]*)", line (\d+)', + r'([^\s]+)\((\d+)\)', + r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces + r'([^\s]+):\s*(\d+):', # filename or path, ltrim + r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim +] + +file_line_progs = None + + +def compile_progs(): + "Compile the patterns for matching to file name and line number." + global file_line_progs + file_line_progs = [re.compile(pat, re.IGNORECASE) + for pat in file_line_pats] + +def file_line_helper(line): + """Extract file name and line number from line of text. + + Check if line of text contains one of the file/line patterns. + If it does and if the file and line are valid, return + a tuple of the file name and line number. If it doesn't match + or if the file or line is invalid, return None. + """ + if not file_line_progs: + compile_progs() + for prog in file_line_progs: + match = prog.search(line) + if match: + filename, lineno = match.group(1, 2) + try: + f = open(filename, "r") + f.close() + break + except OSError: + continue + else: + return None + try: + return filename, int(lineno) + except TypeError: + return None + + +class OutputWindow(EditorWindow): """An editor window that can serve as an output file. Also the future base class for the Python shell window. This class has no input facilities. + + Adds binding to open a file at a line to the text widget. """ + # Our own right-button menu + rmenu_specs = [ + ("Cut", "<<cut>>", "rmenu_check_cut"), + ("Copy", "<<copy>>", "rmenu_check_copy"), + ("Paste", "<<paste>>", "rmenu_check_paste"), + (None, None, None), + ("Go to file/line", "<<goto-file-line>>", None), + ] + def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<<goto-file-line>>", self.goto_file_line) # Customize EditorWindow - def ispythonsource(self, filename): - # No colorization needed - return 0 + "Python source is only part of output: do not colorize." + return False def short_title(self): + "Customize EditorWindow title." return "Output" def maybesave(self): - # Override base class method -- don't ask any questions - if self.get_saved(): - return "yes" - else: - return "no" + "Customize EditorWindow to not display save file messagebox." + return 'yes' if self.get_saved() else 'no' # Act as output file - def write(self, s, tags=(), mark="insert"): + """Write text to text widget. + + The text is inserted at the given index with the provided + tags. The text widget is then scrolled to make it visible + and updated to display it, giving the effect of seeing each + line as it is added. + + Args: + s: Text to insert into text widget. + tags: Tuple of tag strings to apply on the insert. + mark: Index for the insert. + + Return: + Length of text inserted. + """ if isinstance(s, (bytes, bytes)): s = s.decode(iomenu.encoding, "replace") self.text.insert(mark, s, tags) @@ -46,80 +116,46 @@ class OutputWindow(EditorWindow): return len(s) def writelines(self, lines): + "Write each item in lines iterable." for line in lines: self.write(line) def flush(self): + "No flushing needed as write() directly writes to widget." pass - # Our own right-button menu - - rmenu_specs = [ - ("Cut", "<<cut>>", "rmenu_check_cut"), - ("Copy", "<<copy>>", "rmenu_check_copy"), - ("Paste", "<<paste>>", "rmenu_check_paste"), - (None, None, None), - ("Go to file/line", "<<goto-file-line>>", None), - ] + def showerror(self, *args, **kwargs): + messagebox.showerror(*args, **kwargs) - file_line_pats = [ - # order of patterns matters - r'file "([^"]*)", line (\d+)', - r'([^\s]+)\((\d+)\)', - r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces - r'([^\s]+):\s*(\d+):', # filename or path, ltrim - r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim - ] + def goto_file_line(self, event=None): + """Handle request to open file/line. - file_line_progs = None + If the selected or previous line in the output window + contains a file name and line number, then open that file + name in a new window and position on the line number. - def goto_file_line(self, event=None): - if self.file_line_progs is None: - l = [] - for pat in self.file_line_pats: - l.append(re.compile(pat, re.IGNORECASE)) - self.file_line_progs = l - # x, y = self.event.x, self.event.y - # self.text.mark_set("insert", "@%d,%d" % (x, y)) + Otherwise, display an error messagebox. + """ line = self.text.get("insert linestart", "insert lineend") - result = self._file_line_helper(line) + result = file_line_helper(line) if not result: # Try the previous line. This is handy e.g. in tracebacks, # where you tend to right-click on the displayed source line line = self.text.get("insert -1line linestart", "insert -1line lineend") - result = self._file_line_helper(line) + result = file_line_helper(line) if not result: - tkMessageBox.showerror( + self.showerror( "No special line", "The line you point at doesn't look like " "a valid file name followed by a line number.", parent=self.text) return filename, lineno = result - edit = self.flist.open(filename) - edit.gotoline(lineno) - - def _file_line_helper(self, line): - for prog in self.file_line_progs: - match = prog.search(line) - if match: - filename, lineno = match.group(1, 2) - try: - f = open(filename, "r") - f.close() - break - except OSError: - continue - else: - return None - try: - return filename, int(lineno) - except TypeError: - return None + self.flist.gotofileline(filename, lineno) -# These classes are currently not used but might come in handy +# These classes are currently not used but might come in handy class OnDemandOutputWindow: tagdefs = { @@ -145,3 +181,7 @@ class OnDemandOutputWindow: text.tag_configure(tag, **cnf) text.tag_raise('sel') self.write = self.owin.write + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_outwin', verbosity=2, exit=False) |