Update Darwin to Python 3.5 (64bit)
233
Darwin/lib/python3.5/idlelib/AutoComplete.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
"""AutoComplete.py - An IDLE extension for automatically completing names.
|
||||
|
||||
This extension can complete either attribute names of file names. It can pop
|
||||
a window with all available names, for the user to select from.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
# This string includes all chars that may be in an identifier
|
||||
ID_CHARS = string.ascii_letters + string.digits + "_"
|
||||
|
||||
# These constants represent the two different types of completions
|
||||
COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1)
|
||||
|
||||
from idlelib import AutoCompleteWindow
|
||||
from idlelib.HyperParser import HyperParser
|
||||
|
||||
import __main__
|
||||
|
||||
SEPS = os.sep
|
||||
if os.altsep: # e.g. '/' on Windows...
|
||||
SEPS += os.altsep
|
||||
|
||||
class AutoComplete:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
("Show Completions", "<<force-open-completions>>"),
|
||||
])
|
||||
]
|
||||
|
||||
popupwait = idleConf.GetOption("extensions", "AutoComplete",
|
||||
"popupwait", type="int", default=0)
|
||||
|
||||
def __init__(self, editwin=None):
|
||||
self.editwin = editwin
|
||||
if editwin is None: # subprocess and test
|
||||
return
|
||||
self.text = editwin.text
|
||||
self.autocompletewindow = None
|
||||
|
||||
# id of delayed call, and the index of the text insert when the delayed
|
||||
# call was issued. If _delayed_completion_id is None, there is no
|
||||
# delayed call.
|
||||
self._delayed_completion_id = None
|
||||
self._delayed_completion_index = None
|
||||
|
||||
def _make_autocomplete_window(self):
|
||||
return AutoCompleteWindow.AutoCompleteWindow(self.text)
|
||||
|
||||
def _remove_autocomplete_window(self, event=None):
|
||||
if self.autocompletewindow:
|
||||
self.autocompletewindow.hide_window()
|
||||
self.autocompletewindow = None
|
||||
|
||||
def force_open_completions_event(self, event):
|
||||
"""Happens when the user really wants to open a completion list, even
|
||||
if a function call is needed.
|
||||
"""
|
||||
self.open_completions(True, False, True)
|
||||
|
||||
def try_open_completions_event(self, event):
|
||||
"""Happens when it would be nice to open a completion list, but not
|
||||
really necessary, for example after an dot, so function
|
||||
calls won't be made.
|
||||
"""
|
||||
lastchar = self.text.get("insert-1c")
|
||||
if lastchar == ".":
|
||||
self._open_completions_later(False, False, False,
|
||||
COMPLETE_ATTRIBUTES)
|
||||
elif lastchar in SEPS:
|
||||
self._open_completions_later(False, False, False,
|
||||
COMPLETE_FILES)
|
||||
|
||||
def autocomplete_event(self, event):
|
||||
"""Happens when the user wants to complete his word, and if necessary,
|
||||
open a completion list after that (if there is more than one
|
||||
completion)
|
||||
"""
|
||||
if hasattr(event, "mc_state") and event.mc_state:
|
||||
# A modifier was pressed along with the tab, continue as usual.
|
||||
return
|
||||
if self.autocompletewindow and self.autocompletewindow.is_active():
|
||||
self.autocompletewindow.complete()
|
||||
return "break"
|
||||
else:
|
||||
opened = self.open_completions(False, True, True)
|
||||
if opened:
|
||||
return "break"
|
||||
|
||||
def _open_completions_later(self, *args):
|
||||
self._delayed_completion_index = self.text.index("insert")
|
||||
if self._delayed_completion_id is not None:
|
||||
self.text.after_cancel(self._delayed_completion_id)
|
||||
self._delayed_completion_id = \
|
||||
self.text.after(self.popupwait, self._delayed_open_completions,
|
||||
*args)
|
||||
|
||||
def _delayed_open_completions(self, *args):
|
||||
self._delayed_completion_id = None
|
||||
if self.text.index("insert") != self._delayed_completion_index:
|
||||
return
|
||||
self.open_completions(*args)
|
||||
|
||||
def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
|
||||
"""Find the completions and create the AutoCompleteWindow.
|
||||
Return True if successful (no syntax error or so found).
|
||||
if complete is True, then if there's nothing to complete and no
|
||||
start of completion, won't open completions and return False.
|
||||
If mode is given, will open a completion list only in this mode.
|
||||
"""
|
||||
# Cancel another delayed call, if it exists.
|
||||
if self._delayed_completion_id is not None:
|
||||
self.text.after_cancel(self._delayed_completion_id)
|
||||
self._delayed_completion_id = None
|
||||
|
||||
hp = HyperParser(self.editwin, "insert")
|
||||
curline = self.text.get("insert linestart", "insert")
|
||||
i = j = len(curline)
|
||||
if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
|
||||
# Find the beginning of the string
|
||||
# fetch_completions will look at the file system to determine whether the
|
||||
# string value constitutes an actual file name
|
||||
# XXX could consider raw strings here and unescape the string value if it's
|
||||
# not raw.
|
||||
self._remove_autocomplete_window()
|
||||
mode = COMPLETE_FILES
|
||||
# Find last separator or string start
|
||||
while i and curline[i-1] not in "'\"" + SEPS:
|
||||
i -= 1
|
||||
comp_start = curline[i:j]
|
||||
j = i
|
||||
# Find string start
|
||||
while i and curline[i-1] not in "'\"":
|
||||
i -= 1
|
||||
comp_what = curline[i:j]
|
||||
elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
|
||||
self._remove_autocomplete_window()
|
||||
mode = COMPLETE_ATTRIBUTES
|
||||
while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127):
|
||||
i -= 1
|
||||
comp_start = curline[i:j]
|
||||
if i and curline[i-1] == '.':
|
||||
hp.set_index("insert-%dc" % (len(curline)-(i-1)))
|
||||
comp_what = hp.get_expression()
|
||||
if not comp_what or \
|
||||
(not evalfuncs and comp_what.find('(') != -1):
|
||||
return
|
||||
else:
|
||||
comp_what = ""
|
||||
else:
|
||||
return
|
||||
|
||||
if complete and not comp_what and not comp_start:
|
||||
return
|
||||
comp_lists = self.fetch_completions(comp_what, mode)
|
||||
if not comp_lists[0]:
|
||||
return
|
||||
self.autocompletewindow = self._make_autocomplete_window()
|
||||
return not self.autocompletewindow.show_window(
|
||||
comp_lists, "insert-%dc" % len(comp_start),
|
||||
complete, mode, userWantsWin)
|
||||
|
||||
def fetch_completions(self, what, mode):
|
||||
"""Return a pair of lists of completions for something. The first list
|
||||
is a sublist of the second. Both are sorted.
|
||||
|
||||
If there is a Python subprocess, get the comp. list there. Otherwise,
|
||||
either fetch_completions() is running in the subprocess itself or it
|
||||
was called in an IDLE EditorWindow before any script had been run.
|
||||
|
||||
The subprocess environment is that of the most recently run script. If
|
||||
two unrelated modules are being edited some calltips in the current
|
||||
module may be inoperative if the module was not the last to run.
|
||||
"""
|
||||
try:
|
||||
rpcclt = self.editwin.flist.pyshell.interp.rpcclt
|
||||
except:
|
||||
rpcclt = None
|
||||
if rpcclt:
|
||||
return rpcclt.remotecall("exec", "get_the_completion_list",
|
||||
(what, mode), {})
|
||||
else:
|
||||
if mode == COMPLETE_ATTRIBUTES:
|
||||
if what == "":
|
||||
namespace = __main__.__dict__.copy()
|
||||
namespace.update(__main__.__builtins__.__dict__)
|
||||
bigl = eval("dir()", namespace)
|
||||
bigl.sort()
|
||||
if "__all__" in bigl:
|
||||
smalll = sorted(eval("__all__", namespace))
|
||||
else:
|
||||
smalll = [s for s in bigl if s[:1] != '_']
|
||||
else:
|
||||
try:
|
||||
entity = self.get_entity(what)
|
||||
bigl = dir(entity)
|
||||
bigl.sort()
|
||||
if "__all__" in bigl:
|
||||
smalll = sorted(entity.__all__)
|
||||
else:
|
||||
smalll = [s for s in bigl if s[:1] != '_']
|
||||
except:
|
||||
return [], []
|
||||
|
||||
elif mode == COMPLETE_FILES:
|
||||
if what == "":
|
||||
what = "."
|
||||
try:
|
||||
expandedpath = os.path.expanduser(what)
|
||||
bigl = os.listdir(expandedpath)
|
||||
bigl.sort()
|
||||
smalll = [s for s in bigl if s[:1] != '.']
|
||||
except OSError:
|
||||
return [], []
|
||||
|
||||
if not smalll:
|
||||
smalll = bigl
|
||||
return smalll, bigl
|
||||
|
||||
def get_entity(self, name):
|
||||
"""Lookup name in a namespace spanning sys.modules and __main.dict__"""
|
||||
namespace = sys.modules.copy()
|
||||
namespace.update(__main__.__dict__)
|
||||
return eval(name, namespace)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from unittest import main
|
||||
main('idlelib.idle_test.test_autocomplete', verbosity=2)
|
||||
415
Darwin/lib/python3.5/idlelib/AutoCompleteWindow.py
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
"""
|
||||
An auto-completion window for IDLE, used by the AutoComplete extension
|
||||
"""
|
||||
from tkinter import *
|
||||
from idlelib.MultiCall import MC_SHIFT
|
||||
from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES
|
||||
|
||||
HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
|
||||
HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
|
||||
KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
|
||||
# We need to bind event beyond <Key> so that the function will be called
|
||||
# before the default specific IDLE function
|
||||
KEYPRESS_SEQUENCES = ("<Key>", "<Key-BackSpace>", "<Key-Return>", "<Key-Tab>",
|
||||
"<Key-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>",
|
||||
"<Key-Prior>", "<Key-Next>")
|
||||
KEYRELEASE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keyrelease>>"
|
||||
KEYRELEASE_SEQUENCE = "<KeyRelease>"
|
||||
LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>"
|
||||
WINCONFIG_SEQUENCE = "<Configure>"
|
||||
DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>"
|
||||
|
||||
class AutoCompleteWindow:
|
||||
|
||||
def __init__(self, widget):
|
||||
# The widget (Text) on which we place the AutoCompleteWindow
|
||||
self.widget = widget
|
||||
# The widgets we create
|
||||
self.autocompletewindow = self.listbox = self.scrollbar = None
|
||||
# The default foreground and background of a selection. Saved because
|
||||
# they are changed to the regular colors of list items when the
|
||||
# completion start is not a prefix of the selected completion
|
||||
self.origselforeground = self.origselbackground = None
|
||||
# The list of completions
|
||||
self.completions = None
|
||||
# A list with more completions, or None
|
||||
self.morecompletions = None
|
||||
# The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or
|
||||
# AutoComplete.COMPLETE_FILES
|
||||
self.mode = None
|
||||
# The current completion start, on the text box (a string)
|
||||
self.start = None
|
||||
# The index of the start of the completion
|
||||
self.startindex = None
|
||||
# The last typed start, used so that when the selection changes,
|
||||
# the new start will be as close as possible to the last typed one.
|
||||
self.lasttypedstart = None
|
||||
# Do we have an indication that the user wants the completion window
|
||||
# (for example, he clicked the list)
|
||||
self.userwantswindow = None
|
||||
# event ids
|
||||
self.hideid = self.keypressid = self.listupdateid = self.winconfigid \
|
||||
= self.keyreleaseid = self.doubleclickid = None
|
||||
# Flag set if last keypress was a tab
|
||||
self.lastkey_was_tab = False
|
||||
|
||||
def _change_start(self, newstart):
|
||||
min_len = min(len(self.start), len(newstart))
|
||||
i = 0
|
||||
while i < min_len and self.start[i] == newstart[i]:
|
||||
i += 1
|
||||
if i < len(self.start):
|
||||
self.widget.delete("%s+%dc" % (self.startindex, i),
|
||||
"%s+%dc" % (self.startindex, len(self.start)))
|
||||
if i < len(newstart):
|
||||
self.widget.insert("%s+%dc" % (self.startindex, i),
|
||||
newstart[i:])
|
||||
self.start = newstart
|
||||
|
||||
def _binary_search(self, s):
|
||||
"""Find the first index in self.completions where completions[i] is
|
||||
greater or equal to s, or the last index if there is no such
|
||||
one."""
|
||||
i = 0; j = len(self.completions)
|
||||
while j > i:
|
||||
m = (i + j) // 2
|
||||
if self.completions[m] >= s:
|
||||
j = m
|
||||
else:
|
||||
i = m + 1
|
||||
return min(i, len(self.completions)-1)
|
||||
|
||||
def _complete_string(self, s):
|
||||
"""Assuming that s is the prefix of a string in self.completions,
|
||||
return the longest string which is a prefix of all the strings which
|
||||
s is a prefix of them. If s is not a prefix of a string, return s."""
|
||||
first = self._binary_search(s)
|
||||
if self.completions[first][:len(s)] != s:
|
||||
# There is not even one completion which s is a prefix of.
|
||||
return s
|
||||
# Find the end of the range of completions where s is a prefix of.
|
||||
i = first + 1
|
||||
j = len(self.completions)
|
||||
while j > i:
|
||||
m = (i + j) // 2
|
||||
if self.completions[m][:len(s)] != s:
|
||||
j = m
|
||||
else:
|
||||
i = m + 1
|
||||
last = i-1
|
||||
|
||||
if first == last: # only one possible completion
|
||||
return self.completions[first]
|
||||
|
||||
# We should return the maximum prefix of first and last
|
||||
first_comp = self.completions[first]
|
||||
last_comp = self.completions[last]
|
||||
min_len = min(len(first_comp), len(last_comp))
|
||||
i = len(s)
|
||||
while i < min_len and first_comp[i] == last_comp[i]:
|
||||
i += 1
|
||||
return first_comp[:i]
|
||||
|
||||
def _selection_changed(self):
|
||||
"""Should be called when the selection of the Listbox has changed.
|
||||
Updates the Listbox display and calls _change_start."""
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
|
||||
self.listbox.see(cursel)
|
||||
|
||||
lts = self.lasttypedstart
|
||||
selstart = self.completions[cursel]
|
||||
if self._binary_search(lts) == cursel:
|
||||
newstart = lts
|
||||
else:
|
||||
min_len = min(len(lts), len(selstart))
|
||||
i = 0
|
||||
while i < min_len and lts[i] == selstart[i]:
|
||||
i += 1
|
||||
newstart = selstart[:i]
|
||||
self._change_start(newstart)
|
||||
|
||||
if self.completions[cursel][:len(self.start)] == self.start:
|
||||
# start is a prefix of the selected completion
|
||||
self.listbox.configure(selectbackground=self.origselbackground,
|
||||
selectforeground=self.origselforeground)
|
||||
else:
|
||||
self.listbox.configure(selectbackground=self.listbox.cget("bg"),
|
||||
selectforeground=self.listbox.cget("fg"))
|
||||
# If there are more completions, show them, and call me again.
|
||||
if self.morecompletions:
|
||||
self.completions = self.morecompletions
|
||||
self.morecompletions = None
|
||||
self.listbox.delete(0, END)
|
||||
for item in self.completions:
|
||||
self.listbox.insert(END, item)
|
||||
self.listbox.select_set(self._binary_search(self.start))
|
||||
self._selection_changed()
|
||||
|
||||
def show_window(self, comp_lists, index, complete, mode, userWantsWin):
|
||||
"""Show the autocomplete list, bind events.
|
||||
If complete is True, complete the text, and if there is exactly one
|
||||
matching completion, don't open a list."""
|
||||
# Handle the start we already have
|
||||
self.completions, self.morecompletions = comp_lists
|
||||
self.mode = mode
|
||||
self.startindex = self.widget.index(index)
|
||||
self.start = self.widget.get(self.startindex, "insert")
|
||||
if complete:
|
||||
completed = self._complete_string(self.start)
|
||||
start = self.start
|
||||
self._change_start(completed)
|
||||
i = self._binary_search(completed)
|
||||
if self.completions[i] == completed and \
|
||||
(i == len(self.completions)-1 or
|
||||
self.completions[i+1][:len(completed)] != completed):
|
||||
# There is exactly one matching completion
|
||||
return completed == start
|
||||
self.userwantswindow = userWantsWin
|
||||
self.lasttypedstart = self.start
|
||||
|
||||
# Put widgets in place
|
||||
self.autocompletewindow = acw = Toplevel(self.widget)
|
||||
# Put it in a position so that it is not seen.
|
||||
acw.wm_geometry("+10000+10000")
|
||||
# Make it float
|
||||
acw.wm_overrideredirect(1)
|
||||
try:
|
||||
# This command is only needed and available on Tk >= 8.4.0 for OSX
|
||||
# Without it, call tips intrude on the typing process by grabbing
|
||||
# the focus.
|
||||
acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w,
|
||||
"help", "noActivates")
|
||||
except TclError:
|
||||
pass
|
||||
self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL)
|
||||
self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set,
|
||||
exportselection=False, bg="white")
|
||||
for item in self.completions:
|
||||
listbox.insert(END, item)
|
||||
self.origselforeground = listbox.cget("selectforeground")
|
||||
self.origselbackground = listbox.cget("selectbackground")
|
||||
scrollbar.config(command=listbox.yview)
|
||||
scrollbar.pack(side=RIGHT, fill=Y)
|
||||
listbox.pack(side=LEFT, fill=BOTH, expand=True)
|
||||
|
||||
# Initialize the listbox selection
|
||||
self.listbox.select_set(self._binary_search(self.start))
|
||||
self._selection_changed()
|
||||
|
||||
# bind events
|
||||
self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
|
||||
self.hide_event)
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME,
|
||||
self.keypress_event)
|
||||
for seq in KEYPRESS_SEQUENCES:
|
||||
self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
|
||||
self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME,
|
||||
self.keyrelease_event)
|
||||
self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE)
|
||||
self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE,
|
||||
self.listselect_event)
|
||||
self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event)
|
||||
self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE,
|
||||
self.doubleclick_event)
|
||||
|
||||
def winconfig_event(self, event):
|
||||
if not self.is_active():
|
||||
return
|
||||
# Position the completion list window
|
||||
text = self.widget
|
||||
text.see(self.startindex)
|
||||
x, y, cx, cy = text.bbox(self.startindex)
|
||||
acw = self.autocompletewindow
|
||||
acw_width, acw_height = acw.winfo_width(), acw.winfo_height()
|
||||
text_width, text_height = text.winfo_width(), text.winfo_height()
|
||||
new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width))
|
||||
new_y = text.winfo_rooty() + y
|
||||
if (text_height - (y + cy) >= acw_height # enough height below
|
||||
or y < acw_height): # not enough height above
|
||||
# place acw below current line
|
||||
new_y += cy
|
||||
else:
|
||||
# place acw above current line
|
||||
new_y -= acw_height
|
||||
acw.wm_geometry("+%d+%d" % (new_x, new_y))
|
||||
|
||||
def hide_event(self, event):
|
||||
if not self.is_active():
|
||||
return
|
||||
self.hide_window()
|
||||
|
||||
def listselect_event(self, event):
|
||||
if not self.is_active():
|
||||
return
|
||||
self.userwantswindow = True
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
self._change_start(self.completions[cursel])
|
||||
|
||||
def doubleclick_event(self, event):
|
||||
# Put the selected completion in the text, and close the list
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
self._change_start(self.completions[cursel])
|
||||
self.hide_window()
|
||||
|
||||
def keypress_event(self, event):
|
||||
if not self.is_active():
|
||||
return
|
||||
keysym = event.keysym
|
||||
if hasattr(event, "mc_state"):
|
||||
state = event.mc_state
|
||||
else:
|
||||
state = 0
|
||||
if keysym != "Tab":
|
||||
self.lastkey_was_tab = False
|
||||
if (len(keysym) == 1 or keysym in ("underscore", "BackSpace")
|
||||
or (self.mode == COMPLETE_FILES and keysym in
|
||||
("period", "minus"))) \
|
||||
and not (state & ~MC_SHIFT):
|
||||
# Normal editing of text
|
||||
if len(keysym) == 1:
|
||||
self._change_start(self.start + keysym)
|
||||
elif keysym == "underscore":
|
||||
self._change_start(self.start + '_')
|
||||
elif keysym == "period":
|
||||
self._change_start(self.start + '.')
|
||||
elif keysym == "minus":
|
||||
self._change_start(self.start + '-')
|
||||
else:
|
||||
# keysym == "BackSpace"
|
||||
if len(self.start) == 0:
|
||||
self.hide_window()
|
||||
return
|
||||
self._change_start(self.start[:-1])
|
||||
self.lasttypedstart = self.start
|
||||
self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
|
||||
self.listbox.select_set(self._binary_search(self.start))
|
||||
self._selection_changed()
|
||||
return "break"
|
||||
|
||||
elif keysym == "Return":
|
||||
self.hide_window()
|
||||
return
|
||||
|
||||
elif (self.mode == COMPLETE_ATTRIBUTES and keysym in
|
||||
("period", "space", "parenleft", "parenright", "bracketleft",
|
||||
"bracketright")) or \
|
||||
(self.mode == COMPLETE_FILES and keysym in
|
||||
("slash", "backslash", "quotedbl", "apostrophe")) \
|
||||
and not (state & ~MC_SHIFT):
|
||||
# If start is a prefix of the selection, but is not '' when
|
||||
# completing file names, put the whole
|
||||
# selected completion. Anyway, close the list.
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
if self.completions[cursel][:len(self.start)] == self.start \
|
||||
and (self.mode == COMPLETE_ATTRIBUTES or self.start):
|
||||
self._change_start(self.completions[cursel])
|
||||
self.hide_window()
|
||||
return
|
||||
|
||||
elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \
|
||||
not state:
|
||||
# Move the selection in the listbox
|
||||
self.userwantswindow = True
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
if keysym == "Home":
|
||||
newsel = 0
|
||||
elif keysym == "End":
|
||||
newsel = len(self.completions)-1
|
||||
elif keysym in ("Prior", "Next"):
|
||||
jump = self.listbox.nearest(self.listbox.winfo_height()) - \
|
||||
self.listbox.nearest(0)
|
||||
if keysym == "Prior":
|
||||
newsel = max(0, cursel-jump)
|
||||
else:
|
||||
assert keysym == "Next"
|
||||
newsel = min(len(self.completions)-1, cursel+jump)
|
||||
elif keysym == "Up":
|
||||
newsel = max(0, cursel-1)
|
||||
else:
|
||||
assert keysym == "Down"
|
||||
newsel = min(len(self.completions)-1, cursel+1)
|
||||
self.listbox.select_clear(cursel)
|
||||
self.listbox.select_set(newsel)
|
||||
self._selection_changed()
|
||||
self._change_start(self.completions[newsel])
|
||||
return "break"
|
||||
|
||||
elif (keysym == "Tab" and not state):
|
||||
if self.lastkey_was_tab:
|
||||
# two tabs in a row; insert current selection and close acw
|
||||
cursel = int(self.listbox.curselection()[0])
|
||||
self._change_start(self.completions[cursel])
|
||||
self.hide_window()
|
||||
return "break"
|
||||
else:
|
||||
# first tab; let AutoComplete handle the completion
|
||||
self.userwantswindow = True
|
||||
self.lastkey_was_tab = True
|
||||
return
|
||||
|
||||
elif any(s in keysym for s in ("Shift", "Control", "Alt",
|
||||
"Meta", "Command", "Option")):
|
||||
# A modifier key, so ignore
|
||||
return
|
||||
|
||||
elif event.char and event.char >= ' ':
|
||||
# Regular character with a non-length-1 keycode
|
||||
self._change_start(self.start + event.char)
|
||||
self.lasttypedstart = self.start
|
||||
self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
|
||||
self.listbox.select_set(self._binary_search(self.start))
|
||||
self._selection_changed()
|
||||
return "break"
|
||||
|
||||
else:
|
||||
# Unknown event, close the window and let it through.
|
||||
self.hide_window()
|
||||
return
|
||||
|
||||
def keyrelease_event(self, event):
|
||||
if not self.is_active():
|
||||
return
|
||||
if self.widget.index("insert") != \
|
||||
self.widget.index("%s+%dc" % (self.startindex, len(self.start))):
|
||||
# If we didn't catch an event which moved the insert, close window
|
||||
self.hide_window()
|
||||
|
||||
def is_active(self):
|
||||
return self.autocompletewindow is not None
|
||||
|
||||
def complete(self):
|
||||
self._change_start(self._complete_string(self.start))
|
||||
# The selection doesn't change.
|
||||
|
||||
def hide_window(self):
|
||||
if not self.is_active():
|
||||
return
|
||||
|
||||
# unbind events
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
|
||||
self.hideid = None
|
||||
for seq in KEYPRESS_SEQUENCES:
|
||||
self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid)
|
||||
self.keypressid = None
|
||||
self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME,
|
||||
KEYRELEASE_SEQUENCE)
|
||||
self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid)
|
||||
self.keyreleaseid = None
|
||||
self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid)
|
||||
self.listupdateid = None
|
||||
self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
|
||||
self.winconfigid = None
|
||||
|
||||
# destroy widgets
|
||||
self.scrollbar.destroy()
|
||||
self.scrollbar = None
|
||||
self.listbox.destroy()
|
||||
self.listbox = None
|
||||
self.autocompletewindow.destroy()
|
||||
self.autocompletewindow = None
|
||||
104
Darwin/lib/python3.5/idlelib/AutoExpand.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
'''Complete the current word before the cursor with words in the editor.
|
||||
|
||||
Each menu selection or shortcut key selection replaces the word with a
|
||||
different word with the same prefix. The search for matches begins
|
||||
before the target and moves toward the top of the editor. It then starts
|
||||
after the cursor and moves down. It then returns to the original word and
|
||||
the cycle starts again.
|
||||
|
||||
Changing the current text line or leaving the cursor in a different
|
||||
place before requesting the next selection causes AutoExpand to reset
|
||||
its state.
|
||||
|
||||
This is an extension file and there is only one instance of AutoExpand.
|
||||
'''
|
||||
import string
|
||||
import re
|
||||
|
||||
###$ event <<expand-word>>
|
||||
###$ win <Alt-slash>
|
||||
###$ unix <Alt-slash>
|
||||
|
||||
class AutoExpand:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
('E_xpand Word', '<<expand-word>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
wordchars = string.ascii_letters + string.digits + "_"
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.text = editwin.text
|
||||
self.state = None
|
||||
|
||||
def expand_word_event(self, event):
|
||||
"Replace the current word with the next expansion."
|
||||
curinsert = self.text.index("insert")
|
||||
curline = self.text.get("insert linestart", "insert lineend")
|
||||
if not self.state:
|
||||
words = self.getwords()
|
||||
index = 0
|
||||
else:
|
||||
words, index, insert, line = self.state
|
||||
if insert != curinsert or line != curline:
|
||||
words = self.getwords()
|
||||
index = 0
|
||||
if not words:
|
||||
self.text.bell()
|
||||
return "break"
|
||||
word = self.getprevword()
|
||||
self.text.delete("insert - %d chars" % len(word), "insert")
|
||||
newword = words[index]
|
||||
index = (index + 1) % len(words)
|
||||
if index == 0:
|
||||
self.text.bell() # Warn we cycled around
|
||||
self.text.insert("insert", newword)
|
||||
curinsert = self.text.index("insert")
|
||||
curline = self.text.get("insert linestart", "insert lineend")
|
||||
self.state = words, index, curinsert, curline
|
||||
return "break"
|
||||
|
||||
def getwords(self):
|
||||
"Return a list of words that match the prefix before the cursor."
|
||||
word = self.getprevword()
|
||||
if not word:
|
||||
return []
|
||||
before = self.text.get("1.0", "insert wordstart")
|
||||
wbefore = re.findall(r"\b" + word + r"\w+\b", before)
|
||||
del before
|
||||
after = self.text.get("insert wordend", "end")
|
||||
wafter = re.findall(r"\b" + word + r"\w+\b", after)
|
||||
del after
|
||||
if not wbefore and not wafter:
|
||||
return []
|
||||
words = []
|
||||
dict = {}
|
||||
# search backwards through words before
|
||||
wbefore.reverse()
|
||||
for w in wbefore:
|
||||
if dict.get(w):
|
||||
continue
|
||||
words.append(w)
|
||||
dict[w] = w
|
||||
# search onwards through words after
|
||||
for w in wafter:
|
||||
if dict.get(w):
|
||||
continue
|
||||
words.append(w)
|
||||
dict[w] = w
|
||||
words.append(word)
|
||||
return words
|
||||
|
||||
def getprevword(self):
|
||||
"Return the word prefix before the cursor."
|
||||
line = self.text.get("insert linestart", "insert")
|
||||
i = len(line)
|
||||
while i > 0 and line[i-1] in self.wordchars:
|
||||
i = i-1
|
||||
return line[i:]
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2)
|
||||
95
Darwin/lib/python3.5/idlelib/Bindings.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
"""Define the menu contents, hotkeys, and event bindings.
|
||||
|
||||
There is additional configuration information in the EditorWindow class (and
|
||||
subclasses): the menus are created there based on the menu_specs (class)
|
||||
variable, and menus not created are silently skipped in the code here. This
|
||||
makes it possible, for example, to define a Debug menu which is only present in
|
||||
the PythonShell window, and a Format menu which is only present in the Editor
|
||||
windows.
|
||||
|
||||
"""
|
||||
from importlib.util import find_spec
|
||||
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
|
||||
# after it is determined that an OS X Aqua Tk is in use,
|
||||
# which cannot be done until after Tk() is first called.
|
||||
# Do not alter the 'file', 'options', or 'help' cascades here
|
||||
# without altering overrideRootMenu() as well.
|
||||
# TODO: Make this more robust
|
||||
|
||||
menudefs = [
|
||||
# underscore prefixes character to underscore
|
||||
('file', [
|
||||
('_New File', '<<open-new-window>>'),
|
||||
('_Open...', '<<open-window-from-file>>'),
|
||||
('Open _Module...', '<<open-module>>'),
|
||||
('Class _Browser', '<<open-class-browser>>'),
|
||||
('_Path Browser', '<<open-path-browser>>'),
|
||||
None,
|
||||
('_Save', '<<save-window>>'),
|
||||
('Save _As...', '<<save-window-as-file>>'),
|
||||
('Save Cop_y As...', '<<save-copy-of-window-as-file>>'),
|
||||
None,
|
||||
('Prin_t Window', '<<print-window>>'),
|
||||
None,
|
||||
('_Close', '<<close-window>>'),
|
||||
('E_xit', '<<close-all-windows>>'),
|
||||
]),
|
||||
('edit', [
|
||||
('_Undo', '<<undo>>'),
|
||||
('_Redo', '<<redo>>'),
|
||||
None,
|
||||
('Cu_t', '<<cut>>'),
|
||||
('_Copy', '<<copy>>'),
|
||||
('_Paste', '<<paste>>'),
|
||||
('Select _All', '<<select-all>>'),
|
||||
None,
|
||||
('_Find...', '<<find>>'),
|
||||
('Find A_gain', '<<find-again>>'),
|
||||
('Find _Selection', '<<find-selection>>'),
|
||||
('Find in Files...', '<<find-in-files>>'),
|
||||
('R_eplace...', '<<replace>>'),
|
||||
('Go to _Line', '<<goto-line>>'),
|
||||
]),
|
||||
('format', [
|
||||
('_Indent Region', '<<indent-region>>'),
|
||||
('_Dedent Region', '<<dedent-region>>'),
|
||||
('Comment _Out Region', '<<comment-region>>'),
|
||||
('U_ncomment Region', '<<uncomment-region>>'),
|
||||
('Tabify Region', '<<tabify-region>>'),
|
||||
('Untabify Region', '<<untabify-region>>'),
|
||||
('Toggle Tabs', '<<toggle-tabs>>'),
|
||||
('New Indent Width', '<<change-indentwidth>>'),
|
||||
]),
|
||||
('run', [
|
||||
('Python Shell', '<<open-python-shell>>'),
|
||||
]),
|
||||
('shell', [
|
||||
('_View Last Restart', '<<view-restart>>'),
|
||||
('_Restart Shell', '<<restart-shell>>'),
|
||||
]),
|
||||
('debug', [
|
||||
('_Go to File/Line', '<<goto-file-line>>'),
|
||||
('!_Debugger', '<<toggle-debugger>>'),
|
||||
('_Stack Viewer', '<<open-stack-viewer>>'),
|
||||
('!_Auto-open Stack Viewer', '<<toggle-jit-stack-viewer>>'),
|
||||
]),
|
||||
('options', [
|
||||
('Configure _IDLE', '<<open-config-dialog>>'),
|
||||
('Configure _Extensions', '<<open-config-extensions-dialog>>'),
|
||||
None,
|
||||
]),
|
||||
('help', [
|
||||
('_About IDLE', '<<about-idle>>'),
|
||||
None,
|
||||
('_IDLE Help', '<<help>>'),
|
||||
('Python _Docs', '<<python-docs>>'),
|
||||
]),
|
||||
]
|
||||
|
||||
if find_spec('turtledemo'):
|
||||
menudefs[-1][1].append(('Turtle Demo', '<<open-turtle-demo>>'))
|
||||
|
||||
default_keydefs = idleConf.GetCurrentKeySet()
|
||||
37
Darwin/lib/python3.5/idlelib/CREDITS.txt
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
Guido van Rossum, as well as being the creator of the Python language, is the
|
||||
original creator of IDLE. Other contributors prior to Version 0.8 include
|
||||
Mark Hammond, Jeremy Hylton, Tim Peters, and Moshe Zadka.
|
||||
|
||||
IDLE's recent development was carried out in the SF IDLEfork project. The
|
||||
objective was to develop a version of IDLE which had an execution environment
|
||||
which could be initialized prior to each run of user code.
|
||||
|
||||
The IDLEfork project was initiated by David Scherer, with some help from Peter
|
||||
Schneider-Kamp and Nicholas Riley. David wrote the first version of the RPC
|
||||
code and designed a fast turn-around environment for VPython. Guido developed
|
||||
the RPC code and Remote Debugger currently integrated in IDLE. Bruce Sherwood
|
||||
contributed considerable time testing and suggesting improvements.
|
||||
|
||||
Besides David and Guido, the main developers who were active on IDLEfork
|
||||
are Stephen M. Gava, who implemented the configuration GUI, the new
|
||||
configuration system, and the About dialog, and Kurt B. Kaiser, who completed
|
||||
the integration of the RPC and remote debugger, implemented the threaded
|
||||
subprocess, and made a number of usability enhancements.
|
||||
|
||||
Other contributors include Raymond Hettinger, Tony Lownds (Mac integration),
|
||||
Neal Norwitz (code check and clean-up), Ronald Oussoren (Mac integration),
|
||||
Noam Raphael (Code Context, Call Tips, many other patches), and Chui Tey (RPC
|
||||
integration, debugger integration and persistent breakpoints).
|
||||
|
||||
Scott David Daniels, Tal Einat, Hernan Foffani, Christos Georgiou,
|
||||
Jim Jewett, Martin v. Löwis, Jason Orendorff, Guilherme Polo, Josh Robb,
|
||||
Nigel Rowe, Bruce Sherwood, Jeff Shute, and Weeble have submitted useful
|
||||
patches. Thanks, guys!
|
||||
|
||||
For additional details refer to NEWS.txt and Changelog.
|
||||
|
||||
Please contact the IDLE maintainer (kbk@shore.net) to have yourself included
|
||||
here if you are one of those we missed!
|
||||
|
||||
|
||||
|
||||
160
Darwin/lib/python3.5/idlelib/CallTipWindow.py
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
"""A CallTip window class for Tkinter/IDLE.
|
||||
|
||||
After ToolTip.py, which uses ideas gleaned from PySol
|
||||
Used by the CallTips IDLE extension.
|
||||
"""
|
||||
from tkinter import Toplevel, Label, LEFT, SOLID, TclError
|
||||
|
||||
HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
|
||||
HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
|
||||
CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
|
||||
CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
|
||||
CHECKHIDE_TIME = 100 # miliseconds
|
||||
|
||||
MARK_RIGHT = "calltipwindowregion_right"
|
||||
|
||||
class CallTip:
|
||||
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.tipwindow = self.label = None
|
||||
self.parenline = self.parencol = None
|
||||
self.lastline = None
|
||||
self.hideid = self.checkhideid = None
|
||||
self.checkhide_after_id = None
|
||||
|
||||
def position_window(self):
|
||||
"""Check if needs to reposition the window, and if so - do it."""
|
||||
curline = int(self.widget.index("insert").split('.')[0])
|
||||
if curline == self.lastline:
|
||||
return
|
||||
self.lastline = curline
|
||||
self.widget.see("insert")
|
||||
if curline == self.parenline:
|
||||
box = self.widget.bbox("%d.%d" % (self.parenline,
|
||||
self.parencol))
|
||||
else:
|
||||
box = self.widget.bbox("%d.0" % curline)
|
||||
if not box:
|
||||
box = list(self.widget.bbox("insert"))
|
||||
# align to left of window
|
||||
box[0] = 0
|
||||
box[2] = 0
|
||||
x = box[0] + self.widget.winfo_rootx() + 2
|
||||
y = box[1] + box[3] + self.widget.winfo_rooty()
|
||||
self.tipwindow.wm_geometry("+%d+%d" % (x, y))
|
||||
|
||||
def showtip(self, text, parenleft, parenright):
|
||||
"""Show the calltip, bind events which will close it and reposition it.
|
||||
"""
|
||||
# Only called in CallTips, where lines are truncated
|
||||
self.text = text
|
||||
if self.tipwindow or not self.text:
|
||||
return
|
||||
|
||||
self.widget.mark_set(MARK_RIGHT, parenright)
|
||||
self.parenline, self.parencol = map(
|
||||
int, self.widget.index(parenleft).split("."))
|
||||
|
||||
self.tipwindow = tw = Toplevel(self.widget)
|
||||
self.position_window()
|
||||
# remove border on calltip window
|
||||
tw.wm_overrideredirect(1)
|
||||
try:
|
||||
# This command is only needed and available on Tk >= 8.4.0 for OSX
|
||||
# Without it, call tips intrude on the typing process by grabbing
|
||||
# the focus.
|
||||
tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
|
||||
"help", "noActivates")
|
||||
except TclError:
|
||||
pass
|
||||
self.label = Label(tw, text=self.text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1,
|
||||
font = self.widget['font'])
|
||||
self.label.pack()
|
||||
|
||||
self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,
|
||||
self.checkhide_event)
|
||||
for seq in CHECKHIDE_SEQUENCES:
|
||||
self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
|
||||
self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
|
||||
self.hide_event)
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
|
||||
def checkhide_event(self, event=None):
|
||||
if not self.tipwindow:
|
||||
# If the event was triggered by the same event that unbinded
|
||||
# this function, the function will be called nevertheless,
|
||||
# so do nothing in this case.
|
||||
return
|
||||
curline, curcol = map(int, self.widget.index("insert").split('.'))
|
||||
if curline < self.parenline or \
|
||||
(curline == self.parenline and curcol <= self.parencol) or \
|
||||
self.widget.compare("insert", ">", MARK_RIGHT):
|
||||
self.hidetip()
|
||||
else:
|
||||
self.position_window()
|
||||
if self.checkhide_after_id is not None:
|
||||
self.widget.after_cancel(self.checkhide_after_id)
|
||||
self.checkhide_after_id = \
|
||||
self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
|
||||
|
||||
def hide_event(self, event):
|
||||
if not self.tipwindow:
|
||||
# See the explanation in checkhide_event.
|
||||
return
|
||||
self.hidetip()
|
||||
|
||||
def hidetip(self):
|
||||
if not self.tipwindow:
|
||||
return
|
||||
|
||||
for seq in CHECKHIDE_SEQUENCES:
|
||||
self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)
|
||||
self.checkhideid = None
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
|
||||
self.hideid = None
|
||||
|
||||
self.label.destroy()
|
||||
self.label = None
|
||||
self.tipwindow.destroy()
|
||||
self.tipwindow = None
|
||||
|
||||
self.widget.mark_unset(MARK_RIGHT)
|
||||
self.parenline = self.parencol = self.lastline = None
|
||||
|
||||
def is_active(self):
|
||||
return bool(self.tipwindow)
|
||||
|
||||
|
||||
def _calltip_window(parent): # htest #
|
||||
from tkinter import Toplevel, Text, LEFT, BOTH
|
||||
|
||||
top = Toplevel(parent)
|
||||
top.title("Test calltips")
|
||||
top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
|
||||
parent.winfo_rooty() + 150))
|
||||
text = Text(top)
|
||||
text.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
text.insert("insert", "string.split")
|
||||
top.update()
|
||||
calltip = CallTip(text)
|
||||
|
||||
def calltip_show(event):
|
||||
calltip.showtip("(s=Hello world)", "insert", "end")
|
||||
def calltip_hide(event):
|
||||
calltip.hidetip()
|
||||
text.event_add("<<calltip-show>>", "(")
|
||||
text.event_add("<<calltip-hide>>", ")")
|
||||
text.bind("<<calltip-show>>", calltip_show)
|
||||
text.bind("<<calltip-hide>>", calltip_hide)
|
||||
text.focus_set()
|
||||
|
||||
if __name__=='__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_calltip_window)
|
||||
175
Darwin/lib/python3.5/idlelib/CallTips.py
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
"""CallTips.py - An IDLE Extension to Jog Your Memory
|
||||
|
||||
Call Tips are floating windows which display function, class, and method
|
||||
parameter and docstring information when you type an opening parenthesis, and
|
||||
which disappear when you type a closing parenthesis.
|
||||
|
||||
"""
|
||||
import __main__
|
||||
import inspect
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
import types
|
||||
|
||||
from idlelib import CallTipWindow
|
||||
from idlelib.HyperParser import HyperParser
|
||||
|
||||
class CallTips:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
("Show call tip", "<<force-open-calltip>>"),
|
||||
])
|
||||
]
|
||||
|
||||
def __init__(self, editwin=None):
|
||||
if editwin is None: # subprocess and test
|
||||
self.editwin = None
|
||||
else:
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.active_calltip = None
|
||||
self._calltip_window = self._make_tk_calltip_window
|
||||
|
||||
def close(self):
|
||||
self._calltip_window = None
|
||||
|
||||
def _make_tk_calltip_window(self):
|
||||
# See __init__ for usage
|
||||
return CallTipWindow.CallTip(self.text)
|
||||
|
||||
def _remove_calltip_window(self, event=None):
|
||||
if self.active_calltip:
|
||||
self.active_calltip.hidetip()
|
||||
self.active_calltip = None
|
||||
|
||||
def force_open_calltip_event(self, event):
|
||||
"The user selected the menu entry or hotkey, open the tip."
|
||||
self.open_calltip(True)
|
||||
|
||||
def try_open_calltip_event(self, event):
|
||||
"""Happens when it would be nice to open a CallTip, but not really
|
||||
necessary, for example after an opening bracket, so function calls
|
||||
won't be made.
|
||||
"""
|
||||
self.open_calltip(False)
|
||||
|
||||
def refresh_calltip_event(self, event):
|
||||
if self.active_calltip and self.active_calltip.is_active():
|
||||
self.open_calltip(False)
|
||||
|
||||
def open_calltip(self, evalfuncs):
|
||||
self._remove_calltip_window()
|
||||
|
||||
hp = HyperParser(self.editwin, "insert")
|
||||
sur_paren = hp.get_surrounding_brackets('(')
|
||||
if not sur_paren:
|
||||
return
|
||||
hp.set_index(sur_paren[0])
|
||||
expression = hp.get_expression()
|
||||
if not expression:
|
||||
return
|
||||
if not evalfuncs and (expression.find('(') != -1):
|
||||
return
|
||||
argspec = self.fetch_tip(expression)
|
||||
if not argspec:
|
||||
return
|
||||
self.active_calltip = self._calltip_window()
|
||||
self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
|
||||
|
||||
def fetch_tip(self, expression):
|
||||
"""Return the argument list and docstring of a function or class.
|
||||
|
||||
If there is a Python subprocess, get the calltip there. Otherwise,
|
||||
either this fetch_tip() is running in the subprocess or it was
|
||||
called in an IDLE running without the subprocess.
|
||||
|
||||
The subprocess environment is that of the most recently run script. If
|
||||
two unrelated modules are being edited some calltips in the current
|
||||
module may be inoperative if the module was not the last to run.
|
||||
|
||||
To find methods, fetch_tip must be fed a fully qualified name.
|
||||
|
||||
"""
|
||||
try:
|
||||
rpcclt = self.editwin.flist.pyshell.interp.rpcclt
|
||||
except AttributeError:
|
||||
rpcclt = None
|
||||
if rpcclt:
|
||||
return rpcclt.remotecall("exec", "get_the_calltip",
|
||||
(expression,), {})
|
||||
else:
|
||||
return get_argspec(get_entity(expression))
|
||||
|
||||
def get_entity(expression):
|
||||
"""Return the object corresponding to expression evaluated
|
||||
in a namespace spanning sys.modules and __main.dict__.
|
||||
"""
|
||||
if expression:
|
||||
namespace = sys.modules.copy()
|
||||
namespace.update(__main__.__dict__)
|
||||
try:
|
||||
return eval(expression, namespace)
|
||||
except BaseException:
|
||||
# An uncaught exception closes idle, and eval can raise any
|
||||
# exception, especially if user classes are involved.
|
||||
return None
|
||||
|
||||
# The following are used in get_argspec and some in tests
|
||||
_MAX_COLS = 85
|
||||
_MAX_LINES = 5 # enough for bytes
|
||||
_INDENT = ' '*4 # for wrapped signatures
|
||||
_first_param = re.compile('(?<=\()\w*\,?\s*')
|
||||
_default_callable_argspec = "See source or doc"
|
||||
|
||||
|
||||
def get_argspec(ob):
|
||||
'''Return a string describing the signature of a callable object, or ''.
|
||||
|
||||
For Python-coded functions and methods, the first line is introspected.
|
||||
Delete 'self' parameter for classes (.__init__) and bound methods.
|
||||
The next lines are the first lines of the doc string up to the first
|
||||
empty line or _MAX_LINES. For builtins, this typically includes
|
||||
the arguments in addition to the return value.
|
||||
'''
|
||||
argspec = ""
|
||||
try:
|
||||
ob_call = ob.__call__
|
||||
except BaseException:
|
||||
return argspec
|
||||
if isinstance(ob, type):
|
||||
fob = ob.__init__
|
||||
elif isinstance(ob_call, types.MethodType):
|
||||
fob = ob_call
|
||||
else:
|
||||
fob = ob
|
||||
if isinstance(fob, (types.FunctionType, types.MethodType)):
|
||||
argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
|
||||
if (isinstance(ob, (type, types.MethodType)) or
|
||||
isinstance(ob_call, types.MethodType)):
|
||||
argspec = _first_param.sub("", argspec)
|
||||
|
||||
lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
|
||||
if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
|
||||
|
||||
if isinstance(ob_call, types.MethodType):
|
||||
doc = ob_call.__doc__
|
||||
else:
|
||||
doc = getattr(ob, "__doc__", "")
|
||||
if doc:
|
||||
for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
break
|
||||
if len(line) > _MAX_COLS:
|
||||
line = line[: _MAX_COLS - 3] + '...'
|
||||
lines.append(line)
|
||||
argspec = '\n'.join(lines)
|
||||
if not argspec:
|
||||
argspec = _default_callable_argspec
|
||||
return argspec
|
||||
|
||||
if __name__ == '__main__':
|
||||
from unittest import main
|
||||
main('idlelib.idle_test.test_calltips', verbosity=2)
|
||||
1591
Darwin/lib/python3.5/idlelib/ChangeLog
Normal file
236
Darwin/lib/python3.5/idlelib/ClassBrowser.py
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
"""Class browser.
|
||||
|
||||
XXX TO DO:
|
||||
|
||||
- reparse when source changed (maybe just a button would be OK?)
|
||||
(or recheck on window popup)
|
||||
- add popup menu with more options (e.g. doc strings, base classes, imports)
|
||||
- show function argument list? (have to do pattern matching on source)
|
||||
- should the classes and methods lists also be in the module's menu bar?
|
||||
- add base classes to class browser tree
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pyclbr
|
||||
|
||||
from idlelib import PyShell
|
||||
from idlelib.WindowList import ListedToplevel
|
||||
from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
file_open = None # Method...Item and Class...Item use this.
|
||||
# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
|
||||
|
||||
class ClassBrowser:
|
||||
|
||||
def __init__(self, flist, name, path, _htest=False):
|
||||
# XXX This API should change, if the file doesn't end in ".py"
|
||||
# XXX the code here is bogus!
|
||||
"""
|
||||
_htest - bool, change box when location running htest.
|
||||
"""
|
||||
global file_open
|
||||
if not _htest:
|
||||
file_open = PyShell.flist.open
|
||||
self.name = name
|
||||
self.file = os.path.join(path[0], self.name + ".py")
|
||||
self._htest = _htest
|
||||
self.init(flist)
|
||||
|
||||
def close(self, event=None):
|
||||
self.top.destroy()
|
||||
self.node.destroy()
|
||||
|
||||
def init(self, flist):
|
||||
self.flist = flist
|
||||
# reset pyclbr
|
||||
pyclbr._modules.clear()
|
||||
# create top
|
||||
self.top = top = ListedToplevel(flist.root)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.bind("<Escape>", self.close)
|
||||
if self._htest: # place dialog below parent if running htest
|
||||
top.geometry("+%d+%d" %
|
||||
(flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
|
||||
self.settitle()
|
||||
top.focus_set()
|
||||
# create scrolled canvas
|
||||
theme = idleConf.GetOption('main','Theme','name')
|
||||
background = idleConf.GetHighlight(theme, 'normal')['background']
|
||||
sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = self.rootnode()
|
||||
self.node = node = TreeNode(sc.canvas, None, item)
|
||||
node.update()
|
||||
node.expand()
|
||||
|
||||
def settitle(self):
|
||||
self.top.wm_title("Class Browser - " + self.name)
|
||||
self.top.wm_iconname("Class Browser")
|
||||
|
||||
def rootnode(self):
|
||||
return ModuleBrowserTreeItem(self.file)
|
||||
|
||||
class ModuleBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return os.path.basename(self.file)
|
||||
|
||||
def GetIconName(self):
|
||||
return "python"
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for name in self.listclasses():
|
||||
item = ClassBrowserTreeItem(name, self.classes, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if os.path.normcase(self.file[-3:]) != ".py":
|
||||
return
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
PyShell.flist.open(self.file)
|
||||
|
||||
def IsExpandable(self):
|
||||
return os.path.normcase(self.file[-3:]) == ".py"
|
||||
|
||||
def listclasses(self):
|
||||
dir, file = os.path.split(self.file)
|
||||
name, ext = os.path.splitext(file)
|
||||
if os.path.normcase(ext) != ".py":
|
||||
return []
|
||||
try:
|
||||
dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
|
||||
except ImportError:
|
||||
return []
|
||||
items = []
|
||||
self.classes = {}
|
||||
for key, cl in dict.items():
|
||||
if cl.module == name:
|
||||
s = key
|
||||
if hasattr(cl, 'super') and cl.super:
|
||||
supers = []
|
||||
for sup in cl.super:
|
||||
if type(sup) is type(''):
|
||||
sname = sup
|
||||
else:
|
||||
sname = sup.name
|
||||
if sup.module != cl.module:
|
||||
sname = "%s.%s" % (sup.module, sname)
|
||||
supers.append(sname)
|
||||
s = s + "(%s)" % ", ".join(supers)
|
||||
items.append((cl.lineno, s))
|
||||
self.classes[s] = cl
|
||||
items.sort()
|
||||
list = []
|
||||
for item, s in items:
|
||||
list.append(s)
|
||||
return list
|
||||
|
||||
class ClassBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, classes, file):
|
||||
self.name = name
|
||||
self.classes = classes
|
||||
self.file = file
|
||||
try:
|
||||
self.cl = self.classes[self.name]
|
||||
except (IndexError, KeyError):
|
||||
self.cl = None
|
||||
self.isfunction = isinstance(self.cl, pyclbr.Function)
|
||||
|
||||
def GetText(self):
|
||||
if self.isfunction:
|
||||
return "def " + self.name + "(...)"
|
||||
else:
|
||||
return "class " + self.name
|
||||
|
||||
def GetIconName(self):
|
||||
if self.isfunction:
|
||||
return "python"
|
||||
else:
|
||||
return "folder"
|
||||
|
||||
def IsExpandable(self):
|
||||
if self.cl:
|
||||
try:
|
||||
return not not self.cl.methods
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def GetSubList(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
sublist = []
|
||||
for name in self.listmethods():
|
||||
item = MethodBrowserTreeItem(name, self.cl, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = file_open(self.file)
|
||||
if hasattr(self.cl, 'lineno'):
|
||||
lineno = self.cl.lineno
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def listmethods(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
items = []
|
||||
for name, lineno in self.cl.methods.items():
|
||||
items.append((lineno, name))
|
||||
items.sort()
|
||||
list = []
|
||||
for item, name in items:
|
||||
list.append(name)
|
||||
return list
|
||||
|
||||
class MethodBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, cl, file):
|
||||
self.name = name
|
||||
self.cl = cl
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return "def " + self.name + "(...)"
|
||||
|
||||
def GetIconName(self):
|
||||
return "python" # XXX
|
||||
|
||||
def IsExpandable(self):
|
||||
return 0
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = file_open(self.file)
|
||||
edit.gotoline(self.cl.methods[self.name])
|
||||
|
||||
def _class_browser(parent): #Wrapper for htest
|
||||
try:
|
||||
file = __file__
|
||||
except NameError:
|
||||
file = sys.argv[0]
|
||||
if sys.argv[1:]:
|
||||
file = sys.argv[1]
|
||||
else:
|
||||
file = sys.argv[0]
|
||||
dir, file = os.path.split(file)
|
||||
name = os.path.splitext(file)[0]
|
||||
flist = PyShell.PyShellFileList(parent)
|
||||
global file_open
|
||||
file_open = flist.open
|
||||
ClassBrowser(flist, name, [dir], _htest=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_class_browser)
|
||||
176
Darwin/lib/python3.5/idlelib/CodeContext.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
"""CodeContext - Extension to display the block context above the edit window
|
||||
|
||||
Once code has scrolled off the top of a window, it can be difficult to
|
||||
determine which block you are in. This extension implements a pane at the top
|
||||
of each IDLE edit window which provides block structure hints. These hints are
|
||||
the lines which contain the block opening keywords, e.g. 'if', for the
|
||||
enclosing block. The number of hint lines is determined by the numlines
|
||||
variable in the CodeContext section of config-extensions.def. Lines which do
|
||||
not open blocks are not shown in the context hints pane.
|
||||
|
||||
"""
|
||||
import tkinter
|
||||
from tkinter.constants import TOP, LEFT, X, W, SUNKEN
|
||||
import re
|
||||
from sys import maxsize as INFINITY
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
|
||||
"if", "try", "while", "with"}
|
||||
UPDATEINTERVAL = 100 # millisec
|
||||
FONTUPDATEINTERVAL = 1000 # millisec
|
||||
|
||||
getspacesfirstword =\
|
||||
lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
|
||||
|
||||
class CodeContext:
|
||||
menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
|
||||
context_depth = idleConf.GetOption("extensions", "CodeContext",
|
||||
"numlines", type="int", default=3)
|
||||
bgcolor = idleConf.GetOption("extensions", "CodeContext",
|
||||
"bgcolor", type="str", default="LightGray")
|
||||
fgcolor = idleConf.GetOption("extensions", "CodeContext",
|
||||
"fgcolor", type="str", default="Black")
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.textfont = self.text["font"]
|
||||
self.label = None
|
||||
# self.info is a list of (line number, indent level, line text, block
|
||||
# keyword) tuples providing the block structure associated with
|
||||
# self.topvisible (the linenumber of the line displayed at the top of
|
||||
# the edit window). self.info[0] is initialized as a 'dummy' line which
|
||||
# starts the toplevel 'block' of the module.
|
||||
self.info = [(0, -1, "", False)]
|
||||
self.topvisible = 1
|
||||
visible = idleConf.GetOption("extensions", "CodeContext",
|
||||
"visible", type="bool", default=False)
|
||||
if visible:
|
||||
self.toggle_code_context_event()
|
||||
self.editwin.setvar('<<toggle-code-context>>', True)
|
||||
# Start two update cycles, one for context lines, one for font changes.
|
||||
self.text.after(UPDATEINTERVAL, self.timer_event)
|
||||
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
|
||||
|
||||
def toggle_code_context_event(self, event=None):
|
||||
if not self.label:
|
||||
# Calculate the border width and horizontal padding required to
|
||||
# align the context with the text in the main Text widget.
|
||||
#
|
||||
# All values are passed through getint(), since some
|
||||
# values may be pixel objects, which can't simply be added to ints.
|
||||
widgets = self.editwin.text, self.editwin.text_frame
|
||||
# Calculate the required vertical padding
|
||||
padx = 0
|
||||
for widget in widgets:
|
||||
padx += widget.tk.getint(widget.pack_info()['padx'])
|
||||
padx += widget.tk.getint(widget.cget('padx'))
|
||||
# Calculate the required border width
|
||||
border = 0
|
||||
for widget in widgets:
|
||||
border += widget.tk.getint(widget.cget('border'))
|
||||
self.label = tkinter.Label(self.editwin.top,
|
||||
text="\n" * (self.context_depth - 1),
|
||||
anchor=W, justify=LEFT,
|
||||
font=self.textfont,
|
||||
bg=self.bgcolor, fg=self.fgcolor,
|
||||
width=1, #don't request more than we get
|
||||
padx=padx, border=border,
|
||||
relief=SUNKEN)
|
||||
# Pack the label widget before and above the text_frame widget,
|
||||
# thus ensuring that it will appear directly above text_frame
|
||||
self.label.pack(side=TOP, fill=X, expand=False,
|
||||
before=self.editwin.text_frame)
|
||||
else:
|
||||
self.label.destroy()
|
||||
self.label = None
|
||||
idleConf.SetOption("extensions", "CodeContext", "visible",
|
||||
str(self.label is not None))
|
||||
idleConf.SaveUserCfgFiles()
|
||||
|
||||
def get_line_info(self, linenum):
|
||||
"""Get the line indent value, text, and any block start keyword
|
||||
|
||||
If the line does not start a block, the keyword value is False.
|
||||
The indentation of empty lines (or comment lines) is INFINITY.
|
||||
|
||||
"""
|
||||
text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
|
||||
spaces, firstword = getspacesfirstword(text)
|
||||
opener = firstword in BLOCKOPENERS and firstword
|
||||
if len(text) == len(spaces) or text[len(spaces)] == '#':
|
||||
indent = INFINITY
|
||||
else:
|
||||
indent = len(spaces)
|
||||
return indent, text, opener
|
||||
|
||||
def get_context(self, new_topvisible, stopline=1, stopindent=0):
|
||||
"""Get context lines, starting at new_topvisible and working backwards.
|
||||
|
||||
Stop when stopline or stopindent is reached. Return a tuple of context
|
||||
data and the indent level at the top of the region inspected.
|
||||
|
||||
"""
|
||||
assert stopline > 0
|
||||
lines = []
|
||||
# The indentation level we are currently in:
|
||||
lastindent = INFINITY
|
||||
# For a line to be interesting, it must begin with a block opening
|
||||
# keyword, and have less indentation than lastindent.
|
||||
for linenum in range(new_topvisible, stopline-1, -1):
|
||||
indent, text, opener = self.get_line_info(linenum)
|
||||
if indent < lastindent:
|
||||
lastindent = indent
|
||||
if opener in ("else", "elif"):
|
||||
# We also show the if statement
|
||||
lastindent += 1
|
||||
if opener and linenum < new_topvisible and indent >= stopindent:
|
||||
lines.append((linenum, indent, text, opener))
|
||||
if lastindent <= stopindent:
|
||||
break
|
||||
lines.reverse()
|
||||
return lines, lastindent
|
||||
|
||||
def update_code_context(self):
|
||||
"""Update context information and lines visible in the context pane.
|
||||
|
||||
"""
|
||||
new_topvisible = int(self.text.index("@0,0").split('.')[0])
|
||||
if self.topvisible == new_topvisible: # haven't scrolled
|
||||
return
|
||||
if self.topvisible < new_topvisible: # scroll down
|
||||
lines, lastindent = self.get_context(new_topvisible,
|
||||
self.topvisible)
|
||||
# retain only context info applicable to the region
|
||||
# between topvisible and new_topvisible:
|
||||
while self.info[-1][1] >= lastindent:
|
||||
del self.info[-1]
|
||||
elif self.topvisible > new_topvisible: # scroll up
|
||||
stopindent = self.info[-1][1] + 1
|
||||
# retain only context info associated
|
||||
# with lines above new_topvisible:
|
||||
while self.info[-1][0] >= new_topvisible:
|
||||
stopindent = self.info[-1][1]
|
||||
del self.info[-1]
|
||||
lines, lastindent = self.get_context(new_topvisible,
|
||||
self.info[-1][0]+1,
|
||||
stopindent)
|
||||
self.info.extend(lines)
|
||||
self.topvisible = new_topvisible
|
||||
# empty lines in context pane:
|
||||
context_strings = [""] * max(0, self.context_depth - len(self.info))
|
||||
# followed by the context hint lines:
|
||||
context_strings += [x[2] for x in self.info[-self.context_depth:]]
|
||||
self.label["text"] = '\n'.join(context_strings)
|
||||
|
||||
def timer_event(self):
|
||||
if self.label:
|
||||
self.update_code_context()
|
||||
self.text.after(UPDATEINTERVAL, self.timer_event)
|
||||
|
||||
def font_timer_event(self):
|
||||
newtextfont = self.text["font"]
|
||||
if self.label and newtextfont != self.textfont:
|
||||
self.textfont = newtextfont
|
||||
self.label["font"] = self.textfont
|
||||
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
|
||||
256
Darwin/lib/python3.5/idlelib/ColorDelegator.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
import time
|
||||
import re
|
||||
import keyword
|
||||
import builtins
|
||||
from idlelib.Delegator import Delegator
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
DEBUG = False
|
||||
|
||||
def any(name, alternates):
|
||||
"Return a named group pattern matching list of alternates."
|
||||
return "(?P<%s>" % name + "|".join(alternates) + ")"
|
||||
|
||||
def make_pat():
|
||||
kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
|
||||
builtinlist = [str(name) for name in dir(builtins)
|
||||
if not name.startswith('_') and \
|
||||
name not in keyword.kwlist]
|
||||
# self.file = open("file") :
|
||||
# 1st 'file' colorized normal, 2nd as builtin, 3rd as string
|
||||
builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
|
||||
comment = any("COMMENT", [r"#[^\n]*"])
|
||||
stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?"
|
||||
sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
|
||||
dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
|
||||
sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
|
||||
dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
|
||||
string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
|
||||
return kw + "|" + builtin + "|" + comment + "|" + string +\
|
||||
"|" + any("SYNC", [r"\n"])
|
||||
|
||||
prog = re.compile(make_pat(), re.S)
|
||||
idprog = re.compile(r"\s+(\w+)", re.S)
|
||||
|
||||
class ColorDelegator(Delegator):
|
||||
|
||||
def __init__(self):
|
||||
Delegator.__init__(self)
|
||||
self.prog = prog
|
||||
self.idprog = idprog
|
||||
self.LoadTagDefs()
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
if self.delegate is not None:
|
||||
self.unbind("<<toggle-auto-coloring>>")
|
||||
Delegator.setdelegate(self, delegate)
|
||||
if delegate is not None:
|
||||
self.config_colors()
|
||||
self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
|
||||
self.notify_range("1.0", "end")
|
||||
else:
|
||||
# No delegate - stop any colorizing
|
||||
self.stop_colorizing = True
|
||||
self.allow_colorizing = False
|
||||
|
||||
def config_colors(self):
|
||||
for tag, cnf in self.tagdefs.items():
|
||||
if cnf:
|
||||
self.tag_configure(tag, **cnf)
|
||||
self.tag_raise('sel')
|
||||
|
||||
def LoadTagDefs(self):
|
||||
theme = idleConf.GetOption('main','Theme','name')
|
||||
self.tagdefs = {
|
||||
"COMMENT": idleConf.GetHighlight(theme, "comment"),
|
||||
"KEYWORD": idleConf.GetHighlight(theme, "keyword"),
|
||||
"BUILTIN": idleConf.GetHighlight(theme, "builtin"),
|
||||
"STRING": idleConf.GetHighlight(theme, "string"),
|
||||
"DEFINITION": idleConf.GetHighlight(theme, "definition"),
|
||||
"SYNC": {'background':None,'foreground':None},
|
||||
"TODO": {'background':None,'foreground':None},
|
||||
"ERROR": idleConf.GetHighlight(theme, "error"),
|
||||
# The following is used by ReplaceDialog:
|
||||
"hit": idleConf.GetHighlight(theme, "hit"),
|
||||
}
|
||||
|
||||
if DEBUG: print('tagdefs',self.tagdefs)
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
index = self.index(index)
|
||||
self.delegate.insert(index, chars, tags)
|
||||
self.notify_range(index, index + "+%dc" % len(chars))
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
index1 = self.index(index1)
|
||||
self.delegate.delete(index1, index2)
|
||||
self.notify_range(index1)
|
||||
|
||||
after_id = None
|
||||
allow_colorizing = True
|
||||
colorizing = False
|
||||
|
||||
def notify_range(self, index1, index2=None):
|
||||
self.tag_add("TODO", index1, index2)
|
||||
if self.after_id:
|
||||
if DEBUG: print("colorizing already scheduled")
|
||||
return
|
||||
if self.colorizing:
|
||||
self.stop_colorizing = True
|
||||
if DEBUG: print("stop colorizing")
|
||||
if self.allow_colorizing:
|
||||
if DEBUG: print("schedule colorizing")
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
|
||||
close_when_done = None # Window to be closed when done colorizing
|
||||
|
||||
def close(self, close_when_done=None):
|
||||
if self.after_id:
|
||||
after_id = self.after_id
|
||||
self.after_id = None
|
||||
if DEBUG: print("cancel scheduled recolorizer")
|
||||
self.after_cancel(after_id)
|
||||
self.allow_colorizing = False
|
||||
self.stop_colorizing = True
|
||||
if close_when_done:
|
||||
if not self.colorizing:
|
||||
close_when_done.destroy()
|
||||
else:
|
||||
self.close_when_done = close_when_done
|
||||
|
||||
def toggle_colorize_event(self, event):
|
||||
if self.after_id:
|
||||
after_id = self.after_id
|
||||
self.after_id = None
|
||||
if DEBUG: print("cancel scheduled recolorizer")
|
||||
self.after_cancel(after_id)
|
||||
if self.allow_colorizing and self.colorizing:
|
||||
if DEBUG: print("stop colorizing")
|
||||
self.stop_colorizing = True
|
||||
self.allow_colorizing = not self.allow_colorizing
|
||||
if self.allow_colorizing and not self.colorizing:
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
if DEBUG:
|
||||
print("auto colorizing turned",\
|
||||
self.allow_colorizing and "on" or "off")
|
||||
return "break"
|
||||
|
||||
def recolorize(self):
|
||||
self.after_id = None
|
||||
if not self.delegate:
|
||||
if DEBUG: print("no delegate")
|
||||
return
|
||||
if not self.allow_colorizing:
|
||||
if DEBUG: print("auto colorizing is off")
|
||||
return
|
||||
if self.colorizing:
|
||||
if DEBUG: print("already colorizing")
|
||||
return
|
||||
try:
|
||||
self.stop_colorizing = False
|
||||
self.colorizing = True
|
||||
if DEBUG: print("colorizing...")
|
||||
t0 = time.perf_counter()
|
||||
self.recolorize_main()
|
||||
t1 = time.perf_counter()
|
||||
if DEBUG: print("%.3f seconds" % (t1-t0))
|
||||
finally:
|
||||
self.colorizing = False
|
||||
if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
|
||||
if DEBUG: print("reschedule colorizing")
|
||||
self.after_id = self.after(1, self.recolorize)
|
||||
if self.close_when_done:
|
||||
top = self.close_when_done
|
||||
self.close_when_done = None
|
||||
top.destroy()
|
||||
|
||||
def recolorize_main(self):
|
||||
next = "1.0"
|
||||
while True:
|
||||
item = self.tag_nextrange("TODO", next)
|
||||
if not item:
|
||||
break
|
||||
head, tail = item
|
||||
self.tag_remove("SYNC", head, tail)
|
||||
item = self.tag_prevrange("SYNC", head)
|
||||
if item:
|
||||
head = item[1]
|
||||
else:
|
||||
head = "1.0"
|
||||
|
||||
chars = ""
|
||||
next = head
|
||||
lines_to_get = 1
|
||||
ok = False
|
||||
while not ok:
|
||||
mark = next
|
||||
next = self.index(mark + "+%d lines linestart" %
|
||||
lines_to_get)
|
||||
lines_to_get = min(lines_to_get * 2, 100)
|
||||
ok = "SYNC" in self.tag_names(next + "-1c")
|
||||
line = self.get(mark, next)
|
||||
##print head, "get", mark, next, "->", repr(line)
|
||||
if not line:
|
||||
return
|
||||
for tag in self.tagdefs:
|
||||
self.tag_remove(tag, mark, next)
|
||||
chars = chars + line
|
||||
m = self.prog.search(chars)
|
||||
while m:
|
||||
for key, value in m.groupdict().items():
|
||||
if value:
|
||||
a, b = m.span(key)
|
||||
self.tag_add(key,
|
||||
head + "+%dc" % a,
|
||||
head + "+%dc" % b)
|
||||
if value in ("def", "class"):
|
||||
m1 = self.idprog.match(chars, b)
|
||||
if m1:
|
||||
a, b = m1.span(1)
|
||||
self.tag_add("DEFINITION",
|
||||
head + "+%dc" % a,
|
||||
head + "+%dc" % b)
|
||||
m = self.prog.search(chars, m.end())
|
||||
if "SYNC" in self.tag_names(next + "-1c"):
|
||||
head = next
|
||||
chars = ""
|
||||
else:
|
||||
ok = False
|
||||
if not ok:
|
||||
# We're in an inconsistent state, and the call to
|
||||
# update may tell us to stop. It may also change
|
||||
# the correct value for "next" (since this is a
|
||||
# line.col string, not a true mark). So leave a
|
||||
# crumb telling the next invocation to resume here
|
||||
# in case update tells us to leave.
|
||||
self.tag_add("TODO", next)
|
||||
self.update()
|
||||
if self.stop_colorizing:
|
||||
if DEBUG: print("colorizing stopped")
|
||||
return
|
||||
|
||||
def removecolors(self):
|
||||
for tag in self.tagdefs:
|
||||
self.tag_remove(tag, "1.0", "end")
|
||||
|
||||
def _color_delegator(parent): # htest #
|
||||
from tkinter import Toplevel, Text
|
||||
from idlelib.Percolator import Percolator
|
||||
|
||||
top = Toplevel(parent)
|
||||
top.title("Test ColorDelegator")
|
||||
top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
|
||||
parent.winfo_rooty() + 150))
|
||||
source = "if somename: x = 'abc' # comment\nprint\n"
|
||||
text = Text(top, background="white")
|
||||
text.pack(expand=1, fill="both")
|
||||
text.insert("insert", source)
|
||||
text.focus_set()
|
||||
|
||||
p = Percolator(text)
|
||||
d = ColorDelegator()
|
||||
p.insertfilter(d)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_color_delegator)
|
||||
490
Darwin/lib/python3.5/idlelib/Debugger.py
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
import os
|
||||
import bdb
|
||||
from tkinter import *
|
||||
from idlelib.WindowList import ListedToplevel
|
||||
from idlelib.ScrolledList import ScrolledList
|
||||
from idlelib import macosxSupport
|
||||
|
||||
|
||||
class Idb(bdb.Bdb):
|
||||
|
||||
def __init__(self, gui):
|
||||
self.gui = gui
|
||||
bdb.Bdb.__init__(self)
|
||||
|
||||
def user_line(self, frame):
|
||||
if self.in_rpc_code(frame):
|
||||
self.set_step()
|
||||
return
|
||||
message = self.__frame2message(frame)
|
||||
self.gui.interaction(message, frame)
|
||||
|
||||
def user_exception(self, frame, info):
|
||||
if self.in_rpc_code(frame):
|
||||
self.set_step()
|
||||
return
|
||||
message = self.__frame2message(frame)
|
||||
self.gui.interaction(message, frame, info)
|
||||
|
||||
def in_rpc_code(self, frame):
|
||||
if frame.f_code.co_filename.count('rpc.py'):
|
||||
return True
|
||||
else:
|
||||
prev_frame = frame.f_back
|
||||
if prev_frame.f_code.co_filename.count('Debugger.py'):
|
||||
# (that test will catch both Debugger.py and RemoteDebugger.py)
|
||||
return False
|
||||
return self.in_rpc_code(prev_frame)
|
||||
|
||||
def __frame2message(self, frame):
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
basename = os.path.basename(filename)
|
||||
message = "%s:%s" % (basename, lineno)
|
||||
if code.co_name != "?":
|
||||
message = "%s: %s()" % (message, code.co_name)
|
||||
return message
|
||||
|
||||
|
||||
class Debugger:
|
||||
|
||||
vstack = vsource = vlocals = vglobals = None
|
||||
|
||||
def __init__(self, pyshell, idb=None):
|
||||
if idb is None:
|
||||
idb = Idb(self)
|
||||
self.pyshell = pyshell
|
||||
self.idb = idb
|
||||
self.frame = None
|
||||
self.make_gui()
|
||||
self.interacting = 0
|
||||
|
||||
def run(self, *args):
|
||||
try:
|
||||
self.interacting = 1
|
||||
return self.idb.run(*args)
|
||||
finally:
|
||||
self.interacting = 0
|
||||
|
||||
def close(self, event=None):
|
||||
if self.interacting:
|
||||
self.top.bell()
|
||||
return
|
||||
if self.stackviewer:
|
||||
self.stackviewer.close(); self.stackviewer = None
|
||||
# Clean up pyshell if user clicked debugger control close widget.
|
||||
# (Causes a harmless extra cycle through close_debugger() if user
|
||||
# toggled debugger from pyshell Debug menu)
|
||||
self.pyshell.close_debugger()
|
||||
# Now close the debugger control window....
|
||||
self.top.destroy()
|
||||
|
||||
def make_gui(self):
|
||||
pyshell = self.pyshell
|
||||
self.flist = pyshell.flist
|
||||
self.root = root = pyshell.root
|
||||
self.top = top = ListedToplevel(root)
|
||||
self.top.wm_title("Debug Control")
|
||||
self.top.wm_iconname("Debug")
|
||||
top.wm_protocol("WM_DELETE_WINDOW", self.close)
|
||||
self.top.bind("<Escape>", self.close)
|
||||
#
|
||||
self.bframe = bframe = Frame(top)
|
||||
self.bframe.pack(anchor="w")
|
||||
self.buttons = bl = []
|
||||
#
|
||||
self.bcont = b = Button(bframe, text="Go", command=self.cont)
|
||||
bl.append(b)
|
||||
self.bstep = b = Button(bframe, text="Step", command=self.step)
|
||||
bl.append(b)
|
||||
self.bnext = b = Button(bframe, text="Over", command=self.next)
|
||||
bl.append(b)
|
||||
self.bret = b = Button(bframe, text="Out", command=self.ret)
|
||||
bl.append(b)
|
||||
self.bret = b = Button(bframe, text="Quit", command=self.quit)
|
||||
bl.append(b)
|
||||
#
|
||||
for b in bl:
|
||||
b.configure(state="disabled")
|
||||
b.pack(side="left")
|
||||
#
|
||||
self.cframe = cframe = Frame(bframe)
|
||||
self.cframe.pack(side="left")
|
||||
#
|
||||
if not self.vstack:
|
||||
self.__class__.vstack = BooleanVar(top)
|
||||
self.vstack.set(1)
|
||||
self.bstack = Checkbutton(cframe,
|
||||
text="Stack", command=self.show_stack, variable=self.vstack)
|
||||
self.bstack.grid(row=0, column=0)
|
||||
if not self.vsource:
|
||||
self.__class__.vsource = BooleanVar(top)
|
||||
self.bsource = Checkbutton(cframe,
|
||||
text="Source", command=self.show_source, variable=self.vsource)
|
||||
self.bsource.grid(row=0, column=1)
|
||||
if not self.vlocals:
|
||||
self.__class__.vlocals = BooleanVar(top)
|
||||
self.vlocals.set(1)
|
||||
self.blocals = Checkbutton(cframe,
|
||||
text="Locals", command=self.show_locals, variable=self.vlocals)
|
||||
self.blocals.grid(row=1, column=0)
|
||||
if not self.vglobals:
|
||||
self.__class__.vglobals = BooleanVar(top)
|
||||
self.bglobals = Checkbutton(cframe,
|
||||
text="Globals", command=self.show_globals, variable=self.vglobals)
|
||||
self.bglobals.grid(row=1, column=1)
|
||||
#
|
||||
self.status = Label(top, anchor="w")
|
||||
self.status.pack(anchor="w")
|
||||
self.error = Label(top, anchor="w")
|
||||
self.error.pack(anchor="w", fill="x")
|
||||
self.errorbg = self.error.cget("background")
|
||||
#
|
||||
self.fstack = Frame(top, height=1)
|
||||
self.fstack.pack(expand=1, fill="both")
|
||||
self.flocals = Frame(top)
|
||||
self.flocals.pack(expand=1, fill="both")
|
||||
self.fglobals = Frame(top, height=1)
|
||||
self.fglobals.pack(expand=1, fill="both")
|
||||
#
|
||||
if self.vstack.get():
|
||||
self.show_stack()
|
||||
if self.vlocals.get():
|
||||
self.show_locals()
|
||||
if self.vglobals.get():
|
||||
self.show_globals()
|
||||
|
||||
def interaction(self, message, frame, info=None):
|
||||
self.frame = frame
|
||||
self.status.configure(text=message)
|
||||
#
|
||||
if info:
|
||||
type, value, tb = info
|
||||
try:
|
||||
m1 = type.__name__
|
||||
except AttributeError:
|
||||
m1 = "%s" % str(type)
|
||||
if value is not None:
|
||||
try:
|
||||
m1 = "%s: %s" % (m1, str(value))
|
||||
except:
|
||||
pass
|
||||
bg = "yellow"
|
||||
else:
|
||||
m1 = ""
|
||||
tb = None
|
||||
bg = self.errorbg
|
||||
self.error.configure(text=m1, background=bg)
|
||||
#
|
||||
sv = self.stackviewer
|
||||
if sv:
|
||||
stack, i = self.idb.get_stack(self.frame, tb)
|
||||
sv.load_stack(stack, i)
|
||||
#
|
||||
self.show_variables(1)
|
||||
#
|
||||
if self.vsource.get():
|
||||
self.sync_source_line()
|
||||
#
|
||||
for b in self.buttons:
|
||||
b.configure(state="normal")
|
||||
#
|
||||
self.top.wakeup()
|
||||
self.root.mainloop()
|
||||
#
|
||||
for b in self.buttons:
|
||||
b.configure(state="disabled")
|
||||
self.status.configure(text="")
|
||||
self.error.configure(text="", background=self.errorbg)
|
||||
self.frame = None
|
||||
|
||||
def sync_source_line(self):
|
||||
frame = self.frame
|
||||
if not frame:
|
||||
return
|
||||
filename, lineno = self.__frame2fileline(frame)
|
||||
if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
|
||||
self.flist.gotofileline(filename, lineno)
|
||||
|
||||
def __frame2fileline(self, frame):
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
return filename, lineno
|
||||
|
||||
def cont(self):
|
||||
self.idb.set_continue()
|
||||
self.root.quit()
|
||||
|
||||
def step(self):
|
||||
self.idb.set_step()
|
||||
self.root.quit()
|
||||
|
||||
def next(self):
|
||||
self.idb.set_next(self.frame)
|
||||
self.root.quit()
|
||||
|
||||
def ret(self):
|
||||
self.idb.set_return(self.frame)
|
||||
self.root.quit()
|
||||
|
||||
def quit(self):
|
||||
self.idb.set_quit()
|
||||
self.root.quit()
|
||||
|
||||
stackviewer = None
|
||||
|
||||
def show_stack(self):
|
||||
if not self.stackviewer and self.vstack.get():
|
||||
self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
|
||||
if self.frame:
|
||||
stack, i = self.idb.get_stack(self.frame, None)
|
||||
sv.load_stack(stack, i)
|
||||
else:
|
||||
sv = self.stackviewer
|
||||
if sv and not self.vstack.get():
|
||||
self.stackviewer = None
|
||||
sv.close()
|
||||
self.fstack['height'] = 1
|
||||
|
||||
def show_source(self):
|
||||
if self.vsource.get():
|
||||
self.sync_source_line()
|
||||
|
||||
def show_frame(self, stackitem):
|
||||
self.frame = stackitem[0] # lineno is stackitem[1]
|
||||
self.show_variables()
|
||||
|
||||
localsviewer = None
|
||||
globalsviewer = None
|
||||
|
||||
def show_locals(self):
|
||||
lv = self.localsviewer
|
||||
if self.vlocals.get():
|
||||
if not lv:
|
||||
self.localsviewer = NamespaceViewer(self.flocals, "Locals")
|
||||
else:
|
||||
if lv:
|
||||
self.localsviewer = None
|
||||
lv.close()
|
||||
self.flocals['height'] = 1
|
||||
self.show_variables()
|
||||
|
||||
def show_globals(self):
|
||||
gv = self.globalsviewer
|
||||
if self.vglobals.get():
|
||||
if not gv:
|
||||
self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
|
||||
else:
|
||||
if gv:
|
||||
self.globalsviewer = None
|
||||
gv.close()
|
||||
self.fglobals['height'] = 1
|
||||
self.show_variables()
|
||||
|
||||
def show_variables(self, force=0):
|
||||
lv = self.localsviewer
|
||||
gv = self.globalsviewer
|
||||
frame = self.frame
|
||||
if not frame:
|
||||
ldict = gdict = None
|
||||
else:
|
||||
ldict = frame.f_locals
|
||||
gdict = frame.f_globals
|
||||
if lv and gv and ldict is gdict:
|
||||
ldict = None
|
||||
if lv:
|
||||
lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
|
||||
if gv:
|
||||
gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
|
||||
|
||||
def set_breakpoint_here(self, filename, lineno):
|
||||
self.idb.set_break(filename, lineno)
|
||||
|
||||
def clear_breakpoint_here(self, filename, lineno):
|
||||
self.idb.clear_break(filename, lineno)
|
||||
|
||||
def clear_file_breaks(self, filename):
|
||||
self.idb.clear_all_file_breaks(filename)
|
||||
|
||||
def load_breakpoints(self):
|
||||
"Load PyShellEditorWindow breakpoints into subprocess debugger"
|
||||
for editwin in self.pyshell.flist.inversedict:
|
||||
filename = editwin.io.filename
|
||||
try:
|
||||
for lineno in editwin.breakpoints:
|
||||
self.set_breakpoint_here(filename, lineno)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
class StackViewer(ScrolledList):
|
||||
|
||||
def __init__(self, master, flist, gui):
|
||||
if macosxSupport.isAquaTk():
|
||||
# At least on with the stock AquaTk version on OSX 10.4 you'll
|
||||
# get an shaking GUI that eventually kills IDLE if the width
|
||||
# argument is specified.
|
||||
ScrolledList.__init__(self, master)
|
||||
else:
|
||||
ScrolledList.__init__(self, master, width=80)
|
||||
self.flist = flist
|
||||
self.gui = gui
|
||||
self.stack = []
|
||||
|
||||
def load_stack(self, stack, index=None):
|
||||
self.stack = stack
|
||||
self.clear()
|
||||
for i in range(len(stack)):
|
||||
frame, lineno = stack[i]
|
||||
try:
|
||||
modname = frame.f_globals["__name__"]
|
||||
except:
|
||||
modname = "?"
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
funcname = code.co_name
|
||||
import linecache
|
||||
sourceline = linecache.getline(filename, lineno)
|
||||
sourceline = sourceline.strip()
|
||||
if funcname in ("?", "", None):
|
||||
item = "%s, line %d: %s" % (modname, lineno, sourceline)
|
||||
else:
|
||||
item = "%s.%s(), line %d: %s" % (modname, funcname,
|
||||
lineno, sourceline)
|
||||
if i == index:
|
||||
item = "> " + item
|
||||
self.append(item)
|
||||
if index is not None:
|
||||
self.select(index)
|
||||
|
||||
def popup_event(self, event):
|
||||
"override base method"
|
||||
if self.stack:
|
||||
return ScrolledList.popup_event(self, event)
|
||||
|
||||
def fill_menu(self):
|
||||
"override base method"
|
||||
menu = self.menu
|
||||
menu.add_command(label="Go to source line",
|
||||
command=self.goto_source_line)
|
||||
menu.add_command(label="Show stack frame",
|
||||
command=self.show_stack_frame)
|
||||
|
||||
def on_select(self, index):
|
||||
"override base method"
|
||||
if 0 <= index < len(self.stack):
|
||||
self.gui.show_frame(self.stack[index])
|
||||
|
||||
def on_double(self, index):
|
||||
"override base method"
|
||||
self.show_source(index)
|
||||
|
||||
def goto_source_line(self):
|
||||
index = self.listbox.index("active")
|
||||
self.show_source(index)
|
||||
|
||||
def show_stack_frame(self):
|
||||
index = self.listbox.index("active")
|
||||
if 0 <= index < len(self.stack):
|
||||
self.gui.show_frame(self.stack[index])
|
||||
|
||||
def show_source(self, index):
|
||||
if not (0 <= index < len(self.stack)):
|
||||
return
|
||||
frame, lineno = self.stack[index]
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
if os.path.isfile(filename):
|
||||
edit = self.flist.open(filename)
|
||||
if edit:
|
||||
edit.gotoline(lineno)
|
||||
|
||||
|
||||
class NamespaceViewer:
|
||||
|
||||
def __init__(self, master, title, dict=None):
|
||||
width = 0
|
||||
height = 40
|
||||
if dict:
|
||||
height = 20*len(dict) # XXX 20 == observed height of Entry widget
|
||||
self.master = master
|
||||
self.title = title
|
||||
import reprlib
|
||||
self.repr = reprlib.Repr()
|
||||
self.repr.maxstring = 60
|
||||
self.repr.maxother = 60
|
||||
self.frame = frame = Frame(master)
|
||||
self.frame.pack(expand=1, fill="both")
|
||||
self.label = Label(frame, text=title, borderwidth=2, relief="groove")
|
||||
self.label.pack(fill="x")
|
||||
self.vbar = vbar = Scrollbar(frame, name="vbar")
|
||||
vbar.pack(side="right", fill="y")
|
||||
self.canvas = canvas = Canvas(frame,
|
||||
height=min(300, max(40, height)),
|
||||
scrollregion=(0, 0, width, height))
|
||||
canvas.pack(side="left", fill="both", expand=1)
|
||||
vbar["command"] = canvas.yview
|
||||
canvas["yscrollcommand"] = vbar.set
|
||||
self.subframe = subframe = Frame(canvas)
|
||||
self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
|
||||
self.load_dict(dict)
|
||||
|
||||
dict = -1
|
||||
|
||||
def load_dict(self, dict, force=0, rpc_client=None):
|
||||
if dict is self.dict and not force:
|
||||
return
|
||||
subframe = self.subframe
|
||||
frame = self.frame
|
||||
for c in list(subframe.children.values()):
|
||||
c.destroy()
|
||||
self.dict = None
|
||||
if not dict:
|
||||
l = Label(subframe, text="None")
|
||||
l.grid(row=0, column=0)
|
||||
else:
|
||||
#names = sorted(dict)
|
||||
###
|
||||
# Because of (temporary) limitations on the dict_keys type (not yet
|
||||
# public or pickleable), have the subprocess to send a list of
|
||||
# keys, not a dict_keys object. sorted() will take a dict_keys
|
||||
# (no subprocess) or a list.
|
||||
#
|
||||
# There is also an obscure bug in sorted(dict) where the
|
||||
# interpreter gets into a loop requesting non-existing dict[0],
|
||||
# dict[1], dict[2], etc from the RemoteDebugger.DictProxy.
|
||||
###
|
||||
keys_list = dict.keys()
|
||||
names = sorted(keys_list)
|
||||
###
|
||||
row = 0
|
||||
for name in names:
|
||||
value = dict[name]
|
||||
svalue = self.repr.repr(value) # repr(value)
|
||||
# Strip extra quotes caused by calling repr on the (already)
|
||||
# repr'd value sent across the RPC interface:
|
||||
if rpc_client:
|
||||
svalue = svalue[1:-1]
|
||||
l = Label(subframe, text=name)
|
||||
l.grid(row=row, column=0, sticky="nw")
|
||||
l = Entry(subframe, width=0, borderwidth=0)
|
||||
l.insert(0, svalue)
|
||||
l.grid(row=row, column=1, sticky="nw")
|
||||
row = row+1
|
||||
self.dict = dict
|
||||
# XXX Could we use a <Configure> callback for the following?
|
||||
subframe.update_idletasks() # Alas!
|
||||
width = subframe.winfo_reqwidth()
|
||||
height = subframe.winfo_reqheight()
|
||||
canvas = self.canvas
|
||||
self.canvas["scrollregion"] = (0, 0, width, height)
|
||||
if height > 300:
|
||||
canvas["height"] = 300
|
||||
frame.pack(expand=1)
|
||||
else:
|
||||
canvas["height"] = height
|
||||
frame.pack(expand=0)
|
||||
|
||||
def close(self):
|
||||
self.frame.destroy()
|
||||
25
Darwin/lib/python3.5/idlelib/Delegator.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
class Delegator:
|
||||
|
||||
# The cache is only used to be able to change delegates!
|
||||
|
||||
def __init__(self, delegate=None):
|
||||
self.delegate = delegate
|
||||
self.__cache = set()
|
||||
|
||||
def __getattr__(self, name):
|
||||
attr = getattr(self.delegate, name) # May raise AttributeError
|
||||
setattr(self, name, attr)
|
||||
self.__cache.add(name)
|
||||
return attr
|
||||
|
||||
def resetcache(self):
|
||||
for key in self.__cache:
|
||||
try:
|
||||
delattr(self, key)
|
||||
except AttributeError:
|
||||
pass
|
||||
self.__cache.clear()
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
self.resetcache()
|
||||
self.delegate = delegate
|
||||
1719
Darwin/lib/python3.5/idlelib/EditorWindow.py
Normal file
129
Darwin/lib/python3.5/idlelib/FileList.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import os
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
|
||||
|
||||
class FileList:
|
||||
|
||||
# N.B. this import overridden in PyShellFileList.
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.dict = {}
|
||||
self.inversedict = {}
|
||||
self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables)
|
||||
|
||||
def open(self, filename, action=None):
|
||||
assert filename
|
||||
filename = self.canonize(filename)
|
||||
if os.path.isdir(filename):
|
||||
# This can happen when bad filename is passed on command line:
|
||||
tkMessageBox.showerror(
|
||||
"File Error",
|
||||
"%r is a directory." % (filename,),
|
||||
master=self.root)
|
||||
return None
|
||||
key = os.path.normcase(filename)
|
||||
if key in self.dict:
|
||||
edit = self.dict[key]
|
||||
edit.top.wakeup()
|
||||
return edit
|
||||
if action:
|
||||
# Don't create window, perform 'action', e.g. open in same window
|
||||
return action(filename)
|
||||
else:
|
||||
edit = self.EditorWindow(self, filename, key)
|
||||
if edit.good_load:
|
||||
return edit
|
||||
else:
|
||||
edit._close()
|
||||
return None
|
||||
|
||||
def gotofileline(self, filename, lineno=None):
|
||||
edit = self.open(filename)
|
||||
if edit is not None and lineno is not None:
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def new(self, filename=None):
|
||||
return self.EditorWindow(self, filename)
|
||||
|
||||
def close_all_callback(self, *args, **kwds):
|
||||
for edit in list(self.inversedict):
|
||||
reply = edit.close()
|
||||
if reply == "cancel":
|
||||
break
|
||||
return "break"
|
||||
|
||||
def unregister_maybe_terminate(self, edit):
|
||||
try:
|
||||
key = self.inversedict[edit]
|
||||
except KeyError:
|
||||
print("Don't know this EditorWindow object. (close)")
|
||||
return
|
||||
if key:
|
||||
del self.dict[key]
|
||||
del self.inversedict[edit]
|
||||
if not self.inversedict:
|
||||
self.root.quit()
|
||||
|
||||
def filename_changed_edit(self, edit):
|
||||
edit.saved_change_hook()
|
||||
try:
|
||||
key = self.inversedict[edit]
|
||||
except KeyError:
|
||||
print("Don't know this EditorWindow object. (rename)")
|
||||
return
|
||||
filename = edit.io.filename
|
||||
if not filename:
|
||||
if key:
|
||||
del self.dict[key]
|
||||
self.inversedict[edit] = None
|
||||
return
|
||||
filename = self.canonize(filename)
|
||||
newkey = os.path.normcase(filename)
|
||||
if newkey == key:
|
||||
return
|
||||
if newkey in self.dict:
|
||||
conflict = self.dict[newkey]
|
||||
self.inversedict[conflict] = None
|
||||
tkMessageBox.showerror(
|
||||
"Name Conflict",
|
||||
"You now have multiple edit windows open for %r" % (filename,),
|
||||
master=self.root)
|
||||
self.dict[newkey] = edit
|
||||
self.inversedict[edit] = newkey
|
||||
if key:
|
||||
try:
|
||||
del self.dict[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def canonize(self, filename):
|
||||
if not os.path.isabs(filename):
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
filename = os.path.join(pwd, filename)
|
||||
return os.path.normpath(filename)
|
||||
|
||||
|
||||
def _test():
|
||||
from idlelib.EditorWindow import fixwordbreaks
|
||||
import sys
|
||||
root = Tk()
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
flist = FileList(root)
|
||||
if sys.argv[1:]:
|
||||
for filename in sys.argv[1:]:
|
||||
flist.open(filename)
|
||||
else:
|
||||
flist.new()
|
||||
if flist.inversedict:
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
_test()
|
||||
195
Darwin/lib/python3.5/idlelib/FormatParagraph.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
"""Extension to format a paragraph or selection to a max width.
|
||||
|
||||
Does basic, standard text formatting, and also understands Python
|
||||
comment blocks. Thus, for editing Python source code, this
|
||||
extension is really only suitable for reformatting these comment
|
||||
blocks or triple-quoted strings.
|
||||
|
||||
Known problems with comment reformatting:
|
||||
* If there is a selection marked, and the first line of the
|
||||
selection is not complete, the block will probably not be detected
|
||||
as comments, and will have the normal "text formatting" rules
|
||||
applied.
|
||||
* If a comment block has leading whitespace that mixes tabs and
|
||||
spaces, they will not be considered part of the same block.
|
||||
* Fancy comments, like this bulleted list, aren't handled :-)
|
||||
"""
|
||||
|
||||
import re
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
class FormatParagraph:
|
||||
|
||||
menudefs = [
|
||||
('format', [ # /s/edit/format dscherer@cmu.edu
|
||||
('Format Paragraph', '<<format-paragraph>>'),
|
||||
])
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def close(self):
|
||||
self.editwin = None
|
||||
|
||||
def format_paragraph_event(self, event, limit=None):
|
||||
"""Formats paragraph to a max width specified in idleConf.
|
||||
|
||||
If text is selected, format_paragraph_event will start breaking lines
|
||||
at the max width, starting from the beginning selection.
|
||||
|
||||
If no text is selected, format_paragraph_event uses the current
|
||||
cursor location to determine the paragraph (lines of text surrounded
|
||||
by blank lines) and formats it.
|
||||
|
||||
The length limit parameter is for testing with a known value.
|
||||
"""
|
||||
if limit is None:
|
||||
# The default length limit is that defined by pep8
|
||||
limit = idleConf.GetOption(
|
||||
'extensions', 'FormatParagraph', 'max-width',
|
||||
type='int', default=72)
|
||||
text = self.editwin.text
|
||||
first, last = self.editwin.get_selection_indices()
|
||||
if first and last:
|
||||
data = text.get(first, last)
|
||||
comment_header = get_comment_header(data)
|
||||
else:
|
||||
first, last, comment_header, data = \
|
||||
find_paragraph(text, text.index("insert"))
|
||||
if comment_header:
|
||||
newdata = reformat_comment(data, limit, comment_header)
|
||||
else:
|
||||
newdata = reformat_paragraph(data, limit)
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
|
||||
if newdata != data:
|
||||
text.mark_set("insert", first)
|
||||
text.undo_block_start()
|
||||
text.delete(first, last)
|
||||
text.insert(first, newdata)
|
||||
text.undo_block_stop()
|
||||
else:
|
||||
text.mark_set("insert", last)
|
||||
text.see("insert")
|
||||
return "break"
|
||||
|
||||
def find_paragraph(text, mark):
|
||||
"""Returns the start/stop indices enclosing the paragraph that mark is in.
|
||||
|
||||
Also returns the comment format string, if any, and paragraph of text
|
||||
between the start/stop indices.
|
||||
"""
|
||||
lineno, col = map(int, mark.split("."))
|
||||
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
||||
|
||||
# Look for start of next paragraph if the index passed in is a blank line
|
||||
while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
|
||||
lineno = lineno + 1
|
||||
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
||||
first_lineno = lineno
|
||||
comment_header = get_comment_header(line)
|
||||
comment_header_len = len(comment_header)
|
||||
|
||||
# Once start line found, search for end of paragraph (a blank line)
|
||||
while get_comment_header(line)==comment_header and \
|
||||
not is_all_white(line[comment_header_len:]):
|
||||
lineno = lineno + 1
|
||||
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
||||
last = "%d.0" % lineno
|
||||
|
||||
# Search back to beginning of paragraph (first blank line before)
|
||||
lineno = first_lineno - 1
|
||||
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
||||
while lineno > 0 and \
|
||||
get_comment_header(line)==comment_header and \
|
||||
not is_all_white(line[comment_header_len:]):
|
||||
lineno = lineno - 1
|
||||
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
||||
first = "%d.0" % (lineno+1)
|
||||
|
||||
return first, last, comment_header, text.get(first, last)
|
||||
|
||||
# This should perhaps be replaced with textwrap.wrap
|
||||
def reformat_paragraph(data, limit):
|
||||
"""Return data reformatted to specified width (limit)."""
|
||||
lines = data.split("\n")
|
||||
i = 0
|
||||
n = len(lines)
|
||||
while i < n and is_all_white(lines[i]):
|
||||
i = i+1
|
||||
if i >= n:
|
||||
return data
|
||||
indent1 = get_indent(lines[i])
|
||||
if i+1 < n and not is_all_white(lines[i+1]):
|
||||
indent2 = get_indent(lines[i+1])
|
||||
else:
|
||||
indent2 = indent1
|
||||
new = lines[:i]
|
||||
partial = indent1
|
||||
while i < n and not is_all_white(lines[i]):
|
||||
# XXX Should take double space after period (etc.) into account
|
||||
words = re.split("(\s+)", lines[i])
|
||||
for j in range(0, len(words), 2):
|
||||
word = words[j]
|
||||
if not word:
|
||||
continue # Can happen when line ends in whitespace
|
||||
if len((partial + word).expandtabs()) > limit and \
|
||||
partial != indent1:
|
||||
new.append(partial.rstrip())
|
||||
partial = indent2
|
||||
partial = partial + word + " "
|
||||
if j+1 < len(words) and words[j+1] != " ":
|
||||
partial = partial + " "
|
||||
i = i+1
|
||||
new.append(partial.rstrip())
|
||||
# XXX Should reformat remaining paragraphs as well
|
||||
new.extend(lines[i:])
|
||||
return "\n".join(new)
|
||||
|
||||
def reformat_comment(data, limit, comment_header):
|
||||
"""Return data reformatted to specified width with comment header."""
|
||||
|
||||
# Remove header from the comment lines
|
||||
lc = len(comment_header)
|
||||
data = "\n".join(line[lc:] for line in data.split("\n"))
|
||||
# Reformat to maxformatwidth chars or a 20 char width,
|
||||
# whichever is greater.
|
||||
format_width = max(limit - len(comment_header), 20)
|
||||
newdata = reformat_paragraph(data, format_width)
|
||||
# re-split and re-insert the comment header.
|
||||
newdata = newdata.split("\n")
|
||||
# If the block ends in a \n, we dont want the comment prefix
|
||||
# inserted after it. (Im not sure it makes sense to reformat a
|
||||
# comment block that is not made of complete lines, but whatever!)
|
||||
# Can't think of a clean solution, so we hack away
|
||||
block_suffix = ""
|
||||
if not newdata[-1]:
|
||||
block_suffix = "\n"
|
||||
newdata = newdata[:-1]
|
||||
return '\n'.join(comment_header+line for line in newdata) + block_suffix
|
||||
|
||||
def is_all_white(line):
|
||||
"""Return True if line is empty or all whitespace."""
|
||||
|
||||
return re.match(r"^\s*$", line) is not None
|
||||
|
||||
def get_indent(line):
|
||||
"""Return the initial space or tab indent of line."""
|
||||
return re.match(r"^([ \t]*)", line).group()
|
||||
|
||||
def get_comment_header(line):
|
||||
"""Return string with leading whitespace and '#' from line or ''.
|
||||
|
||||
A null return indicates that the line is not a comment line. A non-
|
||||
null return, such as ' #', will be used to find the other lines of
|
||||
a comment block with the same indent.
|
||||
"""
|
||||
m = re.match(r"^([ \t]*#*)", line)
|
||||
if m is None: return ""
|
||||
return m.group(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_formatparagraph',
|
||||
verbosity=2, exit=False)
|
||||
158
Darwin/lib/python3.5/idlelib/GrepDialog.py
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import os
|
||||
import fnmatch
|
||||
import re # for htest
|
||||
import sys
|
||||
from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog
|
||||
from tkinter import Tk, Text, Button, SEL, END # for htest
|
||||
from idlelib import SearchEngine
|
||||
from idlelib.SearchDialogBase import SearchDialogBase
|
||||
# Importing OutputWindow fails due to import loop
|
||||
# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
|
||||
|
||||
def grep(text, io=None, flist=None):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_grepdialog"):
|
||||
engine._grepdialog = GrepDialog(root, engine, flist)
|
||||
dialog = engine._grepdialog
|
||||
searchphrase = text.get("sel.first", "sel.last")
|
||||
dialog.open(text, searchphrase, io)
|
||||
|
||||
class GrepDialog(SearchDialogBase):
|
||||
|
||||
title = "Find in Files Dialog"
|
||||
icon = "Grep"
|
||||
needwrapbutton = 0
|
||||
|
||||
def __init__(self, root, engine, flist):
|
||||
SearchDialogBase.__init__(self, root, engine)
|
||||
self.flist = flist
|
||||
self.globvar = StringVar(root)
|
||||
self.recvar = BooleanVar(root)
|
||||
|
||||
def open(self, text, searchphrase, io=None):
|
||||
SearchDialogBase.open(self, text, searchphrase)
|
||||
if io:
|
||||
path = io.filename or ""
|
||||
else:
|
||||
path = ""
|
||||
dir, base = os.path.split(path)
|
||||
head, tail = os.path.splitext(base)
|
||||
if not tail:
|
||||
tail = ".py"
|
||||
self.globvar.set(os.path.join(dir, "*" + tail))
|
||||
|
||||
def create_entries(self):
|
||||
SearchDialogBase.create_entries(self)
|
||||
self.globent = self.make_entry("In files:", self.globvar)[0]
|
||||
|
||||
def create_other_buttons(self):
|
||||
f = self.make_frame()[0]
|
||||
|
||||
btn = Checkbutton(f, anchor="w",
|
||||
variable=self.recvar,
|
||||
text="Recurse down subdirectories")
|
||||
btn.pack(side="top", fill="both")
|
||||
btn.select()
|
||||
|
||||
def create_command_buttons(self):
|
||||
SearchDialogBase.create_command_buttons(self)
|
||||
self.make_button("Search Files", self.default_command, 1)
|
||||
|
||||
def default_command(self, event=None):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return
|
||||
path = self.globvar.get()
|
||||
if not path:
|
||||
self.top.bell()
|
||||
return
|
||||
from idlelib.OutputWindow import OutputWindow # leave here!
|
||||
save = sys.stdout
|
||||
try:
|
||||
sys.stdout = OutputWindow(self.flist)
|
||||
self.grep_it(prog, path)
|
||||
finally:
|
||||
sys.stdout = save
|
||||
|
||||
def grep_it(self, prog, path):
|
||||
dir, base = os.path.split(path)
|
||||
list = self.findfiles(dir, base, self.recvar.get())
|
||||
list.sort()
|
||||
self.close()
|
||||
pat = self.engine.getpat()
|
||||
print("Searching %r in %s ..." % (pat, path))
|
||||
hits = 0
|
||||
try:
|
||||
for fn in list:
|
||||
try:
|
||||
with open(fn, errors='replace') as f:
|
||||
for lineno, line in enumerate(f, 1):
|
||||
if line[-1:] == '\n':
|
||||
line = line[:-1]
|
||||
if prog.search(line):
|
||||
sys.stdout.write("%s: %s: %s\n" %
|
||||
(fn, lineno, line))
|
||||
hits += 1
|
||||
except OSError as msg:
|
||||
print(msg)
|
||||
print(("Hits found: %s\n"
|
||||
"(Hint: right-click to open locations.)"
|
||||
% hits) if hits else "No hits.")
|
||||
except AttributeError:
|
||||
# Tk window has been closed, OutputWindow.text = None,
|
||||
# so in OW.write, OW.text.insert fails.
|
||||
pass
|
||||
|
||||
def findfiles(self, dir, base, rec):
|
||||
try:
|
||||
names = os.listdir(dir or os.curdir)
|
||||
except OSError as msg:
|
||||
print(msg)
|
||||
return []
|
||||
list = []
|
||||
subdirs = []
|
||||
for name in names:
|
||||
fn = os.path.join(dir, name)
|
||||
if os.path.isdir(fn):
|
||||
subdirs.append(fn)
|
||||
else:
|
||||
if fnmatch.fnmatch(name, base):
|
||||
list.append(fn)
|
||||
if rec:
|
||||
for subdir in subdirs:
|
||||
list.extend(self.findfiles(subdir, base, rec))
|
||||
return list
|
||||
|
||||
def close(self, event=None):
|
||||
if self.top:
|
||||
self.top.grab_release()
|
||||
self.top.withdraw()
|
||||
|
||||
|
||||
def _grep_dialog(parent): # htest #
|
||||
from idlelib.PyShell import PyShellFileList
|
||||
root = Tk()
|
||||
root.title("Test GrepDialog")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
|
||||
flist = PyShellFileList(root)
|
||||
text = Text(root, height=5)
|
||||
text.pack()
|
||||
|
||||
def show_grep_dialog():
|
||||
text.tag_add(SEL, "1.0", END)
|
||||
grep(text, flist=flist)
|
||||
text.tag_remove(SEL, "1.0", END)
|
||||
|
||||
button = Button(root, text="Show GrepDialog", command=show_grep_dialog)
|
||||
button.pack()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
|
||||
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_grep_dialog)
|
||||
296
Darwin/lib/python3.5/idlelib/HISTORY.txt
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
IDLE History
|
||||
============
|
||||
|
||||
This file contains the release messages for previous IDLE releases.
|
||||
As you read on you go back to the dark ages of IDLE's history.
|
||||
|
||||
|
||||
What's New in IDLEfork 0.8.1?
|
||||
=============================
|
||||
|
||||
*Release date: 22-Jul-2001*
|
||||
|
||||
- New tarball released as a result of the 'revitalisation' of the IDLEfork
|
||||
project.
|
||||
|
||||
- This release requires python 2.1 or better. Compatibility with earlier
|
||||
versions of python (especially ancient ones like 1.5x) is no longer a
|
||||
priority in IDLEfork development.
|
||||
|
||||
- This release is based on a merging of the earlier IDLE fork work with current
|
||||
cvs IDLE (post IDLE version 0.8), with some minor additional coding by Kurt
|
||||
B. Kaiser and Stephen M. Gava.
|
||||
|
||||
- This release is basically functional but also contains some known breakages,
|
||||
particularly with running things from the shell window. Also the debugger is
|
||||
not working, but I believe this was the case with the previous IDLE fork
|
||||
release (0.7.1) as well.
|
||||
|
||||
- This release is being made now to mark the point at which IDLEfork is
|
||||
launching into a new stage of development.
|
||||
|
||||
- IDLEfork CVS will now be branched to enable further development and
|
||||
exploration of the two "execution in a remote process" patches submitted by
|
||||
David Scherer (David's is currently in IDLEfork) and GvR, while stabilisation
|
||||
and development of less heavyweight improvements (like user customisation)
|
||||
can continue on the trunk.
|
||||
|
||||
|
||||
What's New in IDLEfork 0.7.1?
|
||||
==============================
|
||||
|
||||
*Release date: 15-Aug-2000*
|
||||
|
||||
- First project tarball released.
|
||||
|
||||
- This was the first release of IDLE fork, which at this stage was a
|
||||
combination of IDLE 0.5 and the VPython idle fork, with additional changes
|
||||
coded by David Scherer, Peter Schneider-Kamp and Nicholas Riley.
|
||||
|
||||
|
||||
|
||||
IDLEfork 0.7.1 - 29 May 2000
|
||||
-----------------------------
|
||||
|
||||
David Scherer <dscherer@cmu.edu>
|
||||
|
||||
- This is a modification of the CVS version of IDLE 0.5, updated as of
|
||||
2000-03-09. It is alpha software and might be unstable. If it breaks, you
|
||||
get to keep both pieces.
|
||||
|
||||
- If you have problems or suggestions, you should either contact me or post to
|
||||
the list at http://www.python.org/mailman/listinfo/idle-dev (making it clear
|
||||
that you are using this modified version of IDLE).
|
||||
|
||||
- Changes:
|
||||
|
||||
- The ExecBinding module, a replacement for ScriptBinding, executes programs
|
||||
in a separate process, piping standard I/O through an RPC mechanism to an
|
||||
OnDemandOutputWindow in IDLE. It supports executing unnamed programs
|
||||
(through a temporary file). It does not yet support debugging.
|
||||
|
||||
- When running programs with ExecBinding, tracebacks will be clipped to
|
||||
exclude system modules. If, however, a system module calls back into the
|
||||
user program, that part of the traceback will be shown.
|
||||
|
||||
- The OnDemandOutputWindow class has been improved. In particular, it now
|
||||
supports a readline() function used to implement user input, and a
|
||||
scroll_clear() operation which is used to hide the output of a previous run
|
||||
by scrolling it out of the window.
|
||||
|
||||
- Startup behavior has been changed. By default IDLE starts up with just a
|
||||
blank editor window, rather than an interactive window. Opening a file in
|
||||
such a blank window replaces the (nonexistent) contents of that window
|
||||
instead of creating another window. Because of the need to have a
|
||||
well-known port for the ExecBinding protocol, only one copy of IDLE can be
|
||||
running. Additional invocations use the RPC mechanism to report their
|
||||
command line arguments to the copy already running.
|
||||
|
||||
- The menus have been reorganized. In particular, the excessively large
|
||||
'edit' menu has been split up into 'edit', 'format', and 'run'.
|
||||
|
||||
- 'Python Documentation' now works on Windows, if the win32api module is
|
||||
present.
|
||||
|
||||
- A few key bindings have been changed: F1 now loads Python Documentation
|
||||
instead of the IDLE help; shift-TAB is now a synonym for unindent.
|
||||
|
||||
- New modules:
|
||||
|
||||
ExecBinding.py Executes program through loader
|
||||
loader.py Bootstraps user program
|
||||
protocol.py RPC protocol
|
||||
Remote.py User-process interpreter
|
||||
spawn.py OS-specific code to start programs
|
||||
|
||||
- Files modified:
|
||||
|
||||
autoindent.py ( bindings tweaked )
|
||||
bindings.py ( menus reorganized )
|
||||
config.txt ( execbinding enabled )
|
||||
editorwindow.py ( new menus, fixed 'Python Documentation' )
|
||||
filelist.py ( hook for "open in same window" )
|
||||
formatparagraph.py ( bindings tweaked )
|
||||
idle.bat ( removed absolute pathname )
|
||||
idle.pyw ( weird bug due to import with same name? )
|
||||
iobinding.py ( open in same window, EOL convention )
|
||||
keydefs.py ( bindings tweaked )
|
||||
outputwindow.py ( readline, scroll_clear, etc )
|
||||
pyshell.py ( changed startup behavior )
|
||||
readme.txt ( <Recursion on file with id=1234567> )
|
||||
|
||||
|
||||
|
||||
IDLE 0.5 - February 2000 - Release Notes
|
||||
----------------------------------------
|
||||
|
||||
This is an early release of IDLE, my own attempt at a Tkinter-based
|
||||
IDE for Python.
|
||||
|
||||
(For a more detailed change log, see the file ChangeLog.)
|
||||
|
||||
FEATURES
|
||||
|
||||
IDLE has the following features:
|
||||
|
||||
- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk)
|
||||
|
||||
- cross-platform: works on Windows and Unix (on the Mac, there are
|
||||
currently problems with Tcl/Tk)
|
||||
|
||||
- multi-window text editor with multiple undo, Python colorizing
|
||||
and many other features, e.g. smart indent and call tips
|
||||
|
||||
- Python shell window (a.k.a. interactive interpreter)
|
||||
|
||||
- debugger (not complete, but you can set breakpoints, view and step)
|
||||
|
||||
USAGE
|
||||
|
||||
The main program is in the file "idle.py"; on Unix, you should be able
|
||||
to run it by typing "./idle.py" to your shell. On Windows, you can
|
||||
run it by double-clicking it; you can use idle.pyw to avoid popping up
|
||||
a DOS console. If you want to pass command line arguments on Windows,
|
||||
use the batch file idle.bat.
|
||||
|
||||
Command line arguments: files passed on the command line are executed,
|
||||
not opened for editing, unless you give the -e command line option.
|
||||
Try "./idle.py -h" to see other command line options.
|
||||
|
||||
IDLE requires Python 1.5.2, so it is currently only usable with a
|
||||
Python 1.5.2 distribution. (An older version of IDLE is distributed
|
||||
with Python 1.5.2; you can drop this version on top of it.)
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
IDLE is covered by the standard Python copyright notice
|
||||
(http://www.python.org/doc/Copyright.html).
|
||||
|
||||
|
||||
New in IDLE 0.5 (2/15/2000)
|
||||
---------------------------
|
||||
|
||||
Tons of stuff, much of it contributed by Tim Peters and Mark Hammond:
|
||||
|
||||
- Status bar, displaying current line/column (Moshe Zadka).
|
||||
|
||||
- Better stack viewer, using tree widget. (XXX Only used by Stack
|
||||
Viewer menu, not by the debugger.)
|
||||
|
||||
- Format paragraph now recognizes Python block comments and reformats
|
||||
them correctly (MH)
|
||||
|
||||
- New version of pyclbr.py parses top-level functions and understands
|
||||
much more of Python's syntax; this is reflected in the class and path
|
||||
browsers (TP)
|
||||
|
||||
- Much better auto-indent; knows how to indent the insides of
|
||||
multi-line statements (TP)
|
||||
|
||||
- Call tip window pops up when you type the name of a known function
|
||||
followed by an open parenthesis. Hit ESC or click elsewhere in the
|
||||
window to close the tip window (MH)
|
||||
|
||||
- Comment out region now inserts ## to make it stand out more (TP)
|
||||
|
||||
- New path and class browsers based on a tree widget that looks
|
||||
familiar to Windows users
|
||||
|
||||
- Reworked script running commands to be more intuitive: I/O now
|
||||
always goes to the *Python Shell* window, and raw_input() works
|
||||
correctly. You use F5 to import/reload a module: this adds the module
|
||||
name to the __main__ namespace. You use Control-F5 to run a script:
|
||||
this runs the script *in* the __main__ namespace. The latter also
|
||||
sets sys.argv[] to the script name
|
||||
|
||||
|
||||
New in IDLE 0.4 (4/7/99)
|
||||
------------------------
|
||||
|
||||
Most important change: a new menu entry "File -> Path browser", shows
|
||||
a 4-column hierarchical browser which lets you browse sys.path,
|
||||
directories, modules, and classes. Yes, it's a superset of the Class
|
||||
browser menu entry. There's also a new internal module,
|
||||
MultiScrolledLists.py, which provides the framework for this dialog.
|
||||
|
||||
|
||||
New in IDLE 0.3 (2/17/99)
|
||||
-------------------------
|
||||
|
||||
Most important changes:
|
||||
|
||||
- Enabled support for running a module, with or without the debugger.
|
||||
Output goes to a new window. Pressing F5 in a module is effectively a
|
||||
reload of that module; Control-F5 loads it under the debugger.
|
||||
|
||||
- Re-enable tearing off the Windows menu, and make a torn-off Windows
|
||||
menu update itself whenever a window is opened or closed.
|
||||
|
||||
- Menu items can now be have a checkbox (when the menu label starts
|
||||
with "!"); use this for the Debugger and "Auto-open stack viewer"
|
||||
(was: JIT stack viewer) menu items.
|
||||
|
||||
- Added a Quit button to the Debugger API.
|
||||
|
||||
- The current directory is explicitly inserted into sys.path.
|
||||
|
||||
- Fix the debugger (when using Python 1.5.2b2) to use canonical
|
||||
filenames for breakpoints, so these actually work. (There's still a
|
||||
lot of work to be done to the management of breakpoints in the
|
||||
debugger though.)
|
||||
|
||||
- Closing a window that is still colorizing now actually works.
|
||||
|
||||
- Allow dragging of the separator between the two list boxes in the
|
||||
class browser.
|
||||
|
||||
- Bind ESC to "close window" of the debugger, stack viewer and class
|
||||
browser. It removes the selection highlighting in regular text
|
||||
windows. (These are standard Windows conventions.)
|
||||
|
||||
|
||||
New in IDLE 0.2 (1/8/99)
|
||||
------------------------
|
||||
|
||||
Lots of changes; here are the highlights:
|
||||
|
||||
General:
|
||||
|
||||
- You can now write and configure your own IDLE extension modules; see
|
||||
extend.txt.
|
||||
|
||||
|
||||
File menu:
|
||||
|
||||
The command to open the Python shell window is now in the File menu.
|
||||
|
||||
|
||||
Edit menu:
|
||||
|
||||
New Find dialog with more options; replace dialog; find in files dialog.
|
||||
|
||||
Commands to tabify or untabify a region.
|
||||
|
||||
Command to format a paragraph.
|
||||
|
||||
|
||||
Debug menu:
|
||||
|
||||
JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer
|
||||
automaticall pops up when you get a traceback.
|
||||
|
||||
Windows menu:
|
||||
|
||||
Zoom height -- make the window full height.
|
||||
|
||||
|
||||
Help menu:
|
||||
|
||||
The help text now show up in a regular window so you can search and
|
||||
even edit it if you like.
|
||||
|
||||
|
||||
|
||||
IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98.
|
||||
|
||||
======================================================================
|
||||
313
Darwin/lib/python3.5/idlelib/HyperParser.py
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
"""Provide advanced parsing abilities for ParenMatch and other extensions.
|
||||
|
||||
HyperParser uses PyParser. PyParser mostly gives information on the
|
||||
proper indentation of code. HyperParser gives additional information on
|
||||
the structure of code.
|
||||
"""
|
||||
|
||||
import string
|
||||
from keyword import iskeyword
|
||||
from idlelib import PyParse
|
||||
|
||||
|
||||
# all ASCII chars that may be in an identifier
|
||||
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
|
||||
# all ASCII chars that may be the first char of an identifier
|
||||
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
|
||||
|
||||
# lookup table for whether 7-bit ASCII chars are valid in a Python identifier
|
||||
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
|
||||
# lookup table for whether 7-bit ASCII chars are valid as the first
|
||||
# char in a Python identifier
|
||||
_IS_ASCII_ID_FIRST_CHAR = \
|
||||
[(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]
|
||||
|
||||
|
||||
class HyperParser:
|
||||
def __init__(self, editwin, index):
|
||||
"To initialize, analyze the surroundings of the given index."
|
||||
|
||||
self.editwin = editwin
|
||||
self.text = text = editwin.text
|
||||
|
||||
parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth)
|
||||
|
||||
def index2line(index):
|
||||
return int(float(index))
|
||||
lno = index2line(text.index(index))
|
||||
|
||||
if not editwin.context_use_ps1:
|
||||
for context in editwin.num_context_lines:
|
||||
startat = max(lno - context, 1)
|
||||
startatindex = repr(startat) + ".0"
|
||||
stopatindex = "%d.end" % lno
|
||||
# We add the newline because PyParse requires a newline
|
||||
# at end. We add a space so that index won't be at end
|
||||
# of line, so that its status will be the same as the
|
||||
# char before it, if should.
|
||||
parser.set_str(text.get(startatindex, stopatindex)+' \n')
|
||||
bod = parser.find_good_parse_start(
|
||||
editwin._build_char_in_string_func(startatindex))
|
||||
if bod is not None or startat == 1:
|
||||
break
|
||||
parser.set_lo(bod or 0)
|
||||
else:
|
||||
r = text.tag_prevrange("console", index)
|
||||
if r:
|
||||
startatindex = r[1]
|
||||
else:
|
||||
startatindex = "1.0"
|
||||
stopatindex = "%d.end" % lno
|
||||
# We add the newline because PyParse requires it. We add a
|
||||
# space so that index won't be at end of line, so that its
|
||||
# status will be the same as the char before it, if should.
|
||||
parser.set_str(text.get(startatindex, stopatindex)+' \n')
|
||||
parser.set_lo(0)
|
||||
|
||||
# We want what the parser has, minus the last newline and space.
|
||||
self.rawtext = parser.str[:-2]
|
||||
# Parser.str apparently preserves the statement we are in, so
|
||||
# that stopatindex can be used to synchronize the string with
|
||||
# the text box indices.
|
||||
self.stopatindex = stopatindex
|
||||
self.bracketing = parser.get_last_stmt_bracketing()
|
||||
# find which pairs of bracketing are openers. These always
|
||||
# correspond to a character of rawtext.
|
||||
self.isopener = [i>0 and self.bracketing[i][1] >
|
||||
self.bracketing[i-1][1]
|
||||
for i in range(len(self.bracketing))]
|
||||
|
||||
self.set_index(index)
|
||||
|
||||
def set_index(self, index):
|
||||
"""Set the index to which the functions relate.
|
||||
|
||||
The index must be in the same statement.
|
||||
"""
|
||||
indexinrawtext = (len(self.rawtext) -
|
||||
len(self.text.get(index, self.stopatindex)))
|
||||
if indexinrawtext < 0:
|
||||
raise ValueError("Index %s precedes the analyzed statement"
|
||||
% index)
|
||||
self.indexinrawtext = indexinrawtext
|
||||
# find the rightmost bracket to which index belongs
|
||||
self.indexbracket = 0
|
||||
while (self.indexbracket < len(self.bracketing)-1 and
|
||||
self.bracketing[self.indexbracket+1][0] < self.indexinrawtext):
|
||||
self.indexbracket += 1
|
||||
if (self.indexbracket < len(self.bracketing)-1 and
|
||||
self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and
|
||||
not self.isopener[self.indexbracket+1]):
|
||||
self.indexbracket += 1
|
||||
|
||||
def is_in_string(self):
|
||||
"""Is the index given to the HyperParser in a string?"""
|
||||
# The bracket to which we belong should be an opener.
|
||||
# If it's an opener, it has to have a character.
|
||||
return (self.isopener[self.indexbracket] and
|
||||
self.rawtext[self.bracketing[self.indexbracket][0]]
|
||||
in ('"', "'"))
|
||||
|
||||
def is_in_code(self):
|
||||
"""Is the index given to the HyperParser in normal code?"""
|
||||
return (not self.isopener[self.indexbracket] or
|
||||
self.rawtext[self.bracketing[self.indexbracket][0]]
|
||||
not in ('#', '"', "'"))
|
||||
|
||||
def get_surrounding_brackets(self, openers='([{', mustclose=False):
|
||||
"""Return bracket indexes or None.
|
||||
|
||||
If the index given to the HyperParser is surrounded by a
|
||||
bracket defined in openers (or at least has one before it),
|
||||
return the indices of the opening bracket and the closing
|
||||
bracket (or the end of line, whichever comes first).
|
||||
|
||||
If it is not surrounded by brackets, or the end of line comes
|
||||
before the closing bracket and mustclose is True, returns None.
|
||||
"""
|
||||
|
||||
bracketinglevel = self.bracketing[self.indexbracket][1]
|
||||
before = self.indexbracket
|
||||
while (not self.isopener[before] or
|
||||
self.rawtext[self.bracketing[before][0]] not in openers or
|
||||
self.bracketing[before][1] > bracketinglevel):
|
||||
before -= 1
|
||||
if before < 0:
|
||||
return None
|
||||
bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
|
||||
after = self.indexbracket + 1
|
||||
while (after < len(self.bracketing) and
|
||||
self.bracketing[after][1] >= bracketinglevel):
|
||||
after += 1
|
||||
|
||||
beforeindex = self.text.index("%s-%dc" %
|
||||
(self.stopatindex, len(self.rawtext)-self.bracketing[before][0]))
|
||||
if (after >= len(self.bracketing) or
|
||||
self.bracketing[after][0] > len(self.rawtext)):
|
||||
if mustclose:
|
||||
return None
|
||||
afterindex = self.stopatindex
|
||||
else:
|
||||
# We are after a real char, so it is a ')' and we give the
|
||||
# index before it.
|
||||
afterindex = self.text.index(
|
||||
"%s-%dc" % (self.stopatindex,
|
||||
len(self.rawtext)-(self.bracketing[after][0]-1)))
|
||||
|
||||
return beforeindex, afterindex
|
||||
|
||||
# the set of built-in identifiers which are also keywords,
|
||||
# i.e. keyword.iskeyword() returns True for them
|
||||
_ID_KEYWORDS = frozenset({"True", "False", "None"})
|
||||
|
||||
@classmethod
|
||||
def _eat_identifier(cls, str, limit, pos):
|
||||
"""Given a string and pos, return the number of chars in the
|
||||
identifier which ends at pos, or 0 if there is no such one.
|
||||
|
||||
This ignores non-identifier eywords are not identifiers.
|
||||
"""
|
||||
is_ascii_id_char = _IS_ASCII_ID_CHAR
|
||||
|
||||
# Start at the end (pos) and work backwards.
|
||||
i = pos
|
||||
|
||||
# Go backwards as long as the characters are valid ASCII
|
||||
# identifier characters. This is an optimization, since it
|
||||
# is faster in the common case where most of the characters
|
||||
# are ASCII.
|
||||
while i > limit and (
|
||||
ord(str[i - 1]) < 128 and
|
||||
is_ascii_id_char[ord(str[i - 1])]
|
||||
):
|
||||
i -= 1
|
||||
|
||||
# If the above loop ended due to reaching a non-ASCII
|
||||
# character, continue going backwards using the most generic
|
||||
# test for whether a string contains only valid identifier
|
||||
# characters.
|
||||
if i > limit and ord(str[i - 1]) >= 128:
|
||||
while i - 4 >= limit and ('a' + str[i - 4:pos]).isidentifier():
|
||||
i -= 4
|
||||
if i - 2 >= limit and ('a' + str[i - 2:pos]).isidentifier():
|
||||
i -= 2
|
||||
if i - 1 >= limit and ('a' + str[i - 1:pos]).isidentifier():
|
||||
i -= 1
|
||||
|
||||
# The identifier candidate starts here. If it isn't a valid
|
||||
# identifier, don't eat anything. At this point that is only
|
||||
# possible if the first character isn't a valid first
|
||||
# character for an identifier.
|
||||
if not str[i:pos].isidentifier():
|
||||
return 0
|
||||
elif i < pos:
|
||||
# All characters in str[i:pos] are valid ASCII identifier
|
||||
# characters, so it is enough to check that the first is
|
||||
# valid as the first character of an identifier.
|
||||
if not _IS_ASCII_ID_FIRST_CHAR[ord(str[i])]:
|
||||
return 0
|
||||
|
||||
# All keywords are valid identifiers, but should not be
|
||||
# considered identifiers here, except for True, False and None.
|
||||
if i < pos and (
|
||||
iskeyword(str[i:pos]) and
|
||||
str[i:pos] not in cls._ID_KEYWORDS
|
||||
):
|
||||
return 0
|
||||
|
||||
return pos - i
|
||||
|
||||
# This string includes all chars that may be in a white space
|
||||
_whitespace_chars = " \t\n\\"
|
||||
|
||||
def get_expression(self):
|
||||
"""Return a string with the Python expression which ends at the
|
||||
given index, which is empty if there is no real one.
|
||||
"""
|
||||
if not self.is_in_code():
|
||||
raise ValueError("get_expression should only be called"
|
||||
"if index is inside a code.")
|
||||
|
||||
rawtext = self.rawtext
|
||||
bracketing = self.bracketing
|
||||
|
||||
brck_index = self.indexbracket
|
||||
brck_limit = bracketing[brck_index][0]
|
||||
pos = self.indexinrawtext
|
||||
|
||||
last_identifier_pos = pos
|
||||
postdot_phase = True
|
||||
|
||||
while 1:
|
||||
# Eat whitespaces, comments, and if postdot_phase is False - a dot
|
||||
while 1:
|
||||
if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars:
|
||||
# Eat a whitespace
|
||||
pos -= 1
|
||||
elif (not postdot_phase and
|
||||
pos > brck_limit and rawtext[pos-1] == '.'):
|
||||
# Eat a dot
|
||||
pos -= 1
|
||||
postdot_phase = True
|
||||
# The next line will fail if we are *inside* a comment,
|
||||
# but we shouldn't be.
|
||||
elif (pos == brck_limit and brck_index > 0 and
|
||||
rawtext[bracketing[brck_index-1][0]] == '#'):
|
||||
# Eat a comment
|
||||
brck_index -= 2
|
||||
brck_limit = bracketing[brck_index][0]
|
||||
pos = bracketing[brck_index+1][0]
|
||||
else:
|
||||
# If we didn't eat anything, quit.
|
||||
break
|
||||
|
||||
if not postdot_phase:
|
||||
# We didn't find a dot, so the expression end at the
|
||||
# last identifier pos.
|
||||
break
|
||||
|
||||
ret = self._eat_identifier(rawtext, brck_limit, pos)
|
||||
if ret:
|
||||
# There is an identifier to eat
|
||||
pos = pos - ret
|
||||
last_identifier_pos = pos
|
||||
# Now, to continue the search, we must find a dot.
|
||||
postdot_phase = False
|
||||
# (the loop continues now)
|
||||
|
||||
elif pos == brck_limit:
|
||||
# We are at a bracketing limit. If it is a closing
|
||||
# bracket, eat the bracket, otherwise, stop the search.
|
||||
level = bracketing[brck_index][1]
|
||||
while brck_index > 0 and bracketing[brck_index-1][1] > level:
|
||||
brck_index -= 1
|
||||
if bracketing[brck_index][0] == brck_limit:
|
||||
# We were not at the end of a closing bracket
|
||||
break
|
||||
pos = bracketing[brck_index][0]
|
||||
brck_index -= 1
|
||||
brck_limit = bracketing[brck_index][0]
|
||||
last_identifier_pos = pos
|
||||
if rawtext[pos] in "([":
|
||||
# [] and () may be used after an identifier, so we
|
||||
# continue. postdot_phase is True, so we don't allow a dot.
|
||||
pass
|
||||
else:
|
||||
# We can't continue after other types of brackets
|
||||
if rawtext[pos] in "'\"":
|
||||
# Scan a string prefix
|
||||
while pos > 0 and rawtext[pos - 1] in "rRbBuU":
|
||||
pos -= 1
|
||||
last_identifier_pos = pos
|
||||
break
|
||||
|
||||
else:
|
||||
# We've found an operator or something.
|
||||
break
|
||||
|
||||
return rawtext[last_identifier_pos:self.indexinrawtext]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2)
|
||||
554
Darwin/lib/python3.5/idlelib/IOBinding.py
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
import os
|
||||
import shlex
|
||||
import sys
|
||||
import codecs
|
||||
import tempfile
|
||||
import tkinter.filedialog as tkFileDialog
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
import re
|
||||
from tkinter import *
|
||||
from tkinter.simpledialog import askstring
|
||||
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
from codecs import BOM_UTF8
|
||||
|
||||
# Try setting the locale, so that we can find out
|
||||
# what encoding to use
|
||||
try:
|
||||
import locale
|
||||
locale.setlocale(locale.LC_CTYPE, "")
|
||||
except (ImportError, locale.Error):
|
||||
pass
|
||||
|
||||
# Encoding for file names
|
||||
filesystemencoding = sys.getfilesystemencoding() ### currently unused
|
||||
|
||||
locale_encoding = 'ascii'
|
||||
if sys.platform == 'win32':
|
||||
# On Windows, we could use "mbcs". However, to give the user
|
||||
# a portable encoding name, we need to find the code page
|
||||
try:
|
||||
locale_encoding = locale.getdefaultlocale()[1]
|
||||
codecs.lookup(locale_encoding)
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Different things can fail here: the locale module may not be
|
||||
# loaded, it may not offer nl_langinfo, or CODESET, or the
|
||||
# resulting codeset may be unknown to Python. We ignore all
|
||||
# these problems, falling back to ASCII
|
||||
locale_encoding = locale.nl_langinfo(locale.CODESET)
|
||||
if locale_encoding is None or locale_encoding is '':
|
||||
# situation occurs on Mac OS X
|
||||
locale_encoding = 'ascii'
|
||||
codecs.lookup(locale_encoding)
|
||||
except (NameError, AttributeError, LookupError):
|
||||
# Try getdefaultlocale: it parses environment variables,
|
||||
# which may give a clue. Unfortunately, getdefaultlocale has
|
||||
# bugs that can cause ValueError.
|
||||
try:
|
||||
locale_encoding = locale.getdefaultlocale()[1]
|
||||
if locale_encoding is None or locale_encoding is '':
|
||||
# situation occurs on Mac OS X
|
||||
locale_encoding = 'ascii'
|
||||
codecs.lookup(locale_encoding)
|
||||
except (ValueError, LookupError):
|
||||
pass
|
||||
|
||||
locale_encoding = locale_encoding.lower()
|
||||
|
||||
encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check!
|
||||
### 'encoding' is used below in encode(), check!
|
||||
|
||||
coding_re = re.compile(r'^[ \t\f]*#.*coding[:=][ \t]*([-\w.]+)', re.ASCII)
|
||||
blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)
|
||||
|
||||
def coding_spec(data):
|
||||
"""Return the encoding declaration according to PEP 263.
|
||||
|
||||
When checking encoded data, only the first two lines should be passed
|
||||
in to avoid a UnicodeDecodeError if the rest of the data is not unicode.
|
||||
The first two lines would contain the encoding specification.
|
||||
|
||||
Raise a LookupError if the encoding is declared but unknown.
|
||||
"""
|
||||
if isinstance(data, bytes):
|
||||
# This encoding might be wrong. However, the coding
|
||||
# spec must be ASCII-only, so any non-ASCII characters
|
||||
# around here will be ignored. Decoding to Latin-1 should
|
||||
# never fail (except for memory outage)
|
||||
lines = data.decode('iso-8859-1')
|
||||
else:
|
||||
lines = data
|
||||
# consider only the first two lines
|
||||
if '\n' in lines:
|
||||
lst = lines.split('\n', 2)[:2]
|
||||
elif '\r' in lines:
|
||||
lst = lines.split('\r', 2)[:2]
|
||||
else:
|
||||
lst = [lines]
|
||||
for line in lst:
|
||||
match = coding_re.match(line)
|
||||
if match is not None:
|
||||
break
|
||||
if not blank_re.match(line):
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
name = match.group(1)
|
||||
try:
|
||||
codecs.lookup(name)
|
||||
except LookupError:
|
||||
# The standard encoding error does not indicate the encoding
|
||||
raise LookupError("Unknown encoding: "+name)
|
||||
return name
|
||||
|
||||
|
||||
class IOBinding:
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
|
||||
self.__id_save = self.text.bind("<<save-window>>", self.save)
|
||||
self.__id_saveas = self.text.bind("<<save-window-as-file>>",
|
||||
self.save_as)
|
||||
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
|
||||
self.save_a_copy)
|
||||
self.fileencoding = None
|
||||
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
|
||||
|
||||
def close(self):
|
||||
# Undo command bindings
|
||||
self.text.unbind("<<open-window-from-file>>", self.__id_open)
|
||||
self.text.unbind("<<save-window>>", self.__id_save)
|
||||
self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
|
||||
self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
|
||||
self.text.unbind("<<print-window>>", self.__id_print)
|
||||
# Break cycles
|
||||
self.editwin = None
|
||||
self.text = None
|
||||
self.filename_change_hook = None
|
||||
|
||||
def get_saved(self):
|
||||
return self.editwin.get_saved()
|
||||
|
||||
def set_saved(self, flag):
|
||||
self.editwin.set_saved(flag)
|
||||
|
||||
def reset_undo(self):
|
||||
self.editwin.reset_undo()
|
||||
|
||||
filename_change_hook = None
|
||||
|
||||
def set_filename_change_hook(self, hook):
|
||||
self.filename_change_hook = hook
|
||||
|
||||
filename = None
|
||||
dirname = None
|
||||
|
||||
def set_filename(self, filename):
|
||||
if filename and os.path.isdir(filename):
|
||||
self.filename = None
|
||||
self.dirname = filename
|
||||
else:
|
||||
self.filename = filename
|
||||
self.dirname = None
|
||||
self.set_saved(1)
|
||||
if self.filename_change_hook:
|
||||
self.filename_change_hook()
|
||||
|
||||
def open(self, event=None, editFile=None):
|
||||
flist = self.editwin.flist
|
||||
# Save in case parent window is closed (ie, during askopenfile()).
|
||||
if flist:
|
||||
if not editFile:
|
||||
filename = self.askopenfile()
|
||||
else:
|
||||
filename=editFile
|
||||
if filename:
|
||||
# If editFile is valid and already open, flist.open will
|
||||
# shift focus to its existing window.
|
||||
# If the current window exists and is a fresh unnamed,
|
||||
# unmodified editor window (not an interpreter shell),
|
||||
# pass self.loadfile to flist.open so it will load the file
|
||||
# in the current window (if the file is not already open)
|
||||
# instead of a new window.
|
||||
if (self.editwin and
|
||||
not getattr(self.editwin, 'interp', None) and
|
||||
not self.filename and
|
||||
self.get_saved()):
|
||||
flist.open(filename, self.loadfile)
|
||||
else:
|
||||
flist.open(filename)
|
||||
else:
|
||||
if self.text:
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
# Code for use outside IDLE:
|
||||
if self.get_saved():
|
||||
reply = self.maybesave()
|
||||
if reply == "cancel":
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
if not editFile:
|
||||
filename = self.askopenfile()
|
||||
else:
|
||||
filename=editFile
|
||||
if filename:
|
||||
self.loadfile(filename)
|
||||
else:
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
|
||||
eol_re = re.compile(eol)
|
||||
eol_convention = os.linesep # default
|
||||
|
||||
def loadfile(self, filename):
|
||||
try:
|
||||
# open the file in binary mode so that we can handle
|
||||
# end-of-line convention ourselves.
|
||||
with open(filename, 'rb') as f:
|
||||
two_lines = f.readline() + f.readline()
|
||||
f.seek(0)
|
||||
bytes = f.read()
|
||||
except OSError as msg:
|
||||
tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
|
||||
return False
|
||||
chars, converted = self._decode(two_lines, bytes)
|
||||
if chars is None:
|
||||
tkMessageBox.showerror("Decoding Error",
|
||||
"File %s\nFailed to Decode" % filename,
|
||||
parent=self.text)
|
||||
return False
|
||||
# We now convert all end-of-lines to '\n's
|
||||
firsteol = self.eol_re.search(chars)
|
||||
if firsteol:
|
||||
self.eol_convention = firsteol.group(0)
|
||||
chars = self.eol_re.sub(r"\n", chars)
|
||||
self.text.delete("1.0", "end")
|
||||
self.set_filename(None)
|
||||
self.text.insert("1.0", chars)
|
||||
self.reset_undo()
|
||||
self.set_filename(filename)
|
||||
if converted:
|
||||
# We need to save the conversion results first
|
||||
# before being able to execute the code
|
||||
self.set_saved(False)
|
||||
self.text.mark_set("insert", "1.0")
|
||||
self.text.yview("insert")
|
||||
self.updaterecentfileslist(filename)
|
||||
return True
|
||||
|
||||
def _decode(self, two_lines, bytes):
|
||||
"Create a Unicode string."
|
||||
chars = None
|
||||
# Check presence of a UTF-8 signature first
|
||||
if bytes.startswith(BOM_UTF8):
|
||||
try:
|
||||
chars = bytes[3:].decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
# has UTF-8 signature, but fails to decode...
|
||||
return None, False
|
||||
else:
|
||||
# Indicates that this file originally had a BOM
|
||||
self.fileencoding = 'BOM'
|
||||
return chars, False
|
||||
# Next look for coding specification
|
||||
try:
|
||||
enc = coding_spec(two_lines)
|
||||
except LookupError as name:
|
||||
tkMessageBox.showerror(
|
||||
title="Error loading the file",
|
||||
message="The encoding '%s' is not known to this Python "\
|
||||
"installation. The file may not display correctly" % name,
|
||||
master = self.text)
|
||||
enc = None
|
||||
except UnicodeDecodeError:
|
||||
return None, False
|
||||
if enc:
|
||||
try:
|
||||
chars = str(bytes, enc)
|
||||
self.fileencoding = enc
|
||||
return chars, False
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
# Try ascii:
|
||||
try:
|
||||
chars = str(bytes, 'ascii')
|
||||
self.fileencoding = None
|
||||
return chars, False
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
# Try utf-8:
|
||||
try:
|
||||
chars = str(bytes, 'utf-8')
|
||||
self.fileencoding = 'utf-8'
|
||||
return chars, False
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
# Finally, try the locale's encoding. This is deprecated;
|
||||
# the user should declare a non-ASCII encoding
|
||||
try:
|
||||
# Wait for the editor window to appear
|
||||
self.editwin.text.update()
|
||||
enc = askstring(
|
||||
"Specify file encoding",
|
||||
"The file's encoding is invalid for Python 3.x.\n"
|
||||
"IDLE will convert it to UTF-8.\n"
|
||||
"What is the current encoding of the file?",
|
||||
initialvalue = locale_encoding,
|
||||
parent = self.editwin.text)
|
||||
|
||||
if enc:
|
||||
chars = str(bytes, enc)
|
||||
self.fileencoding = None
|
||||
return chars, True
|
||||
except (UnicodeDecodeError, LookupError):
|
||||
pass
|
||||
return None, False # None on failure
|
||||
|
||||
def maybesave(self):
|
||||
if self.get_saved():
|
||||
return "yes"
|
||||
message = "Do you want to save %s before closing?" % (
|
||||
self.filename or "this untitled document")
|
||||
confirm = tkMessageBox.askyesnocancel(
|
||||
title="Save On Close",
|
||||
message=message,
|
||||
default=tkMessageBox.YES,
|
||||
master=self.text)
|
||||
if confirm:
|
||||
reply = "yes"
|
||||
self.save(None)
|
||||
if not self.get_saved():
|
||||
reply = "cancel"
|
||||
elif confirm is None:
|
||||
reply = "cancel"
|
||||
else:
|
||||
reply = "no"
|
||||
self.text.focus_set()
|
||||
return reply
|
||||
|
||||
def save(self, event):
|
||||
if not self.filename:
|
||||
self.save_as(event)
|
||||
else:
|
||||
if self.writefile(self.filename):
|
||||
self.set_saved(True)
|
||||
try:
|
||||
self.editwin.store_file_breaks()
|
||||
except AttributeError: # may be a PyShell
|
||||
pass
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
|
||||
def save_as(self, event):
|
||||
filename = self.asksavefile()
|
||||
if filename:
|
||||
if self.writefile(filename):
|
||||
self.set_filename(filename)
|
||||
self.set_saved(1)
|
||||
try:
|
||||
self.editwin.store_file_breaks()
|
||||
except AttributeError:
|
||||
pass
|
||||
self.text.focus_set()
|
||||
self.updaterecentfileslist(filename)
|
||||
return "break"
|
||||
|
||||
def save_a_copy(self, event):
|
||||
filename = self.asksavefile()
|
||||
if filename:
|
||||
self.writefile(filename)
|
||||
self.text.focus_set()
|
||||
self.updaterecentfileslist(filename)
|
||||
return "break"
|
||||
|
||||
def writefile(self, filename):
|
||||
self.fixlastline()
|
||||
text = self.text.get("1.0", "end-1c")
|
||||
if self.eol_convention != "\n":
|
||||
text = text.replace("\n", self.eol_convention)
|
||||
chars = self.encode(text)
|
||||
try:
|
||||
with open(filename, "wb") as f:
|
||||
f.write(chars)
|
||||
return True
|
||||
except OSError as msg:
|
||||
tkMessageBox.showerror("I/O Error", str(msg),
|
||||
master=self.text)
|
||||
return False
|
||||
|
||||
def encode(self, chars):
|
||||
if isinstance(chars, bytes):
|
||||
# This is either plain ASCII, or Tk was returning mixed-encoding
|
||||
# text to us. Don't try to guess further.
|
||||
return chars
|
||||
# Preserve a BOM that might have been present on opening
|
||||
if self.fileencoding == 'BOM':
|
||||
return BOM_UTF8 + chars.encode("utf-8")
|
||||
# See whether there is anything non-ASCII in it.
|
||||
# If not, no need to figure out the encoding.
|
||||
try:
|
||||
return chars.encode('ascii')
|
||||
except UnicodeError:
|
||||
pass
|
||||
# Check if there is an encoding declared
|
||||
try:
|
||||
# a string, let coding_spec slice it to the first two lines
|
||||
enc = coding_spec(chars)
|
||||
failed = None
|
||||
except LookupError as msg:
|
||||
failed = msg
|
||||
enc = None
|
||||
else:
|
||||
if not enc:
|
||||
# PEP 3120: default source encoding is UTF-8
|
||||
enc = 'utf-8'
|
||||
if enc:
|
||||
try:
|
||||
return chars.encode(enc)
|
||||
except UnicodeError:
|
||||
failed = "Invalid encoding '%s'" % enc
|
||||
tkMessageBox.showerror(
|
||||
"I/O Error",
|
||||
"%s.\nSaving as UTF-8" % failed,
|
||||
master = self.text)
|
||||
# Fallback: save as UTF-8, with BOM - ignoring the incorrect
|
||||
# declared encoding
|
||||
return BOM_UTF8 + chars.encode("utf-8")
|
||||
|
||||
def fixlastline(self):
|
||||
c = self.text.get("end-2c")
|
||||
if c != '\n':
|
||||
self.text.insert("end-1c", "\n")
|
||||
|
||||
def print_window(self, event):
|
||||
confirm = tkMessageBox.askokcancel(
|
||||
title="Print",
|
||||
message="Print to Default Printer",
|
||||
default=tkMessageBox.OK,
|
||||
master=self.text)
|
||||
if not confirm:
|
||||
self.text.focus_set()
|
||||
return "break"
|
||||
tempfilename = None
|
||||
saved = self.get_saved()
|
||||
if saved:
|
||||
filename = self.filename
|
||||
# shell undo is reset after every prompt, looks saved, probably isn't
|
||||
if not saved or filename is None:
|
||||
(tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
|
||||
filename = tempfilename
|
||||
os.close(tfd)
|
||||
if not self.writefile(tempfilename):
|
||||
os.unlink(tempfilename)
|
||||
return "break"
|
||||
platform = os.name
|
||||
printPlatform = True
|
||||
if platform == 'posix': #posix platform
|
||||
command = idleConf.GetOption('main','General',
|
||||
'print-command-posix')
|
||||
command = command + " 2>&1"
|
||||
elif platform == 'nt': #win32 platform
|
||||
command = idleConf.GetOption('main','General','print-command-win')
|
||||
else: #no printing for this platform
|
||||
printPlatform = False
|
||||
if printPlatform: #we can try to print for this platform
|
||||
command = command % shlex.quote(filename)
|
||||
pipe = os.popen(command, "r")
|
||||
# things can get ugly on NT if there is no printer available.
|
||||
output = pipe.read().strip()
|
||||
status = pipe.close()
|
||||
if status:
|
||||
output = "Printing failed (exit status 0x%x)\n" % \
|
||||
status + output
|
||||
if output:
|
||||
output = "Printing command: %s\n" % repr(command) + output
|
||||
tkMessageBox.showerror("Print status", output, master=self.text)
|
||||
else: #no printing for this platform
|
||||
message = "Printing is not enabled for this platform: %s" % platform
|
||||
tkMessageBox.showinfo("Print status", message, master=self.text)
|
||||
if tempfilename:
|
||||
os.unlink(tempfilename)
|
||||
return "break"
|
||||
|
||||
opendialog = None
|
||||
savedialog = None
|
||||
|
||||
filetypes = [
|
||||
("Python files", "*.py *.pyw", "TEXT"),
|
||||
("Text files", "*.txt", "TEXT"),
|
||||
("All files", "*"),
|
||||
]
|
||||
|
||||
defaultextension = '.py' if sys.platform == 'darwin' else ''
|
||||
|
||||
def askopenfile(self):
|
||||
dir, base = self.defaultfilename("open")
|
||||
if not self.opendialog:
|
||||
self.opendialog = tkFileDialog.Open(master=self.text,
|
||||
filetypes=self.filetypes)
|
||||
filename = self.opendialog.show(initialdir=dir, initialfile=base)
|
||||
return filename
|
||||
|
||||
def defaultfilename(self, mode="open"):
|
||||
if self.filename:
|
||||
return os.path.split(self.filename)
|
||||
elif self.dirname:
|
||||
return self.dirname, ""
|
||||
else:
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except OSError:
|
||||
pwd = ""
|
||||
return pwd, ""
|
||||
|
||||
def asksavefile(self):
|
||||
dir, base = self.defaultfilename("save")
|
||||
if not self.savedialog:
|
||||
self.savedialog = tkFileDialog.SaveAs(
|
||||
master=self.text,
|
||||
filetypes=self.filetypes,
|
||||
defaultextension=self.defaultextension)
|
||||
filename = self.savedialog.show(initialdir=dir, initialfile=base)
|
||||
return filename
|
||||
|
||||
def updaterecentfileslist(self,filename):
|
||||
"Update recent file list on all editor windows"
|
||||
if self.editwin.flist:
|
||||
self.editwin.update_recent_files_list(filename)
|
||||
|
||||
def _io_binding(parent): # htest #
|
||||
root = Tk()
|
||||
root.title("Test IOBinding")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
class MyEditWin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.flist = None
|
||||
self.text.bind("<Control-o>", self.open)
|
||||
self.text.bind("<Control-s>", self.save)
|
||||
def get_saved(self): return 0
|
||||
def set_saved(self, flag): pass
|
||||
def reset_undo(self): pass
|
||||
def open(self, event):
|
||||
self.text.event_generate("<<open-window-from-file>>")
|
||||
def save(self, event):
|
||||
self.text.event_generate("<<save-window>>")
|
||||
|
||||
text = Text(root)
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
editwin = MyEditWin(text)
|
||||
IOBinding(editwin)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_io_binding)
|
||||
BIN
Darwin/lib/python3.5/idlelib/Icons/folder.gif
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle.icns
Normal file
BIN
Darwin/lib/python3.5/idlelib/Icons/idle.ico
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_16.gif
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_16.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_32.gif
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_32.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_48.gif
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/idle_48.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
Darwin/lib/python3.5/idlelib/Icons/minusnode.gif
Normal file
|
After Width: | Height: | Size: 96 B |
BIN
Darwin/lib/python3.5/idlelib/Icons/openfolder.gif
Normal file
|
After Width: | Height: | Size: 125 B |
BIN
Darwin/lib/python3.5/idlelib/Icons/plusnode.gif
Normal file
|
After Width: | Height: | Size: 79 B |
BIN
Darwin/lib/python3.5/idlelib/Icons/python.gif
Normal file
|
After Width: | Height: | Size: 585 B |
BIN
Darwin/lib/python3.5/idlelib/Icons/tk.gif
Normal file
|
After Width: | Height: | Size: 85 B |
104
Darwin/lib/python3.5/idlelib/IdleHistory.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
"Implement Idle Shell history mechanism with History class"
|
||||
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
class History:
|
||||
''' Implement Idle Shell history mechanism.
|
||||
|
||||
store - Store source statement (called from PyShell.resetoutput).
|
||||
fetch - Fetch stored statement matching prefix already entered.
|
||||
history_next - Bound to <<history-next>> event (default Alt-N).
|
||||
history_prev - Bound to <<history-prev>> event (default Alt-P).
|
||||
'''
|
||||
def __init__(self, text):
|
||||
'''Initialize data attributes and bind event methods.
|
||||
|
||||
.text - Idle wrapper of tk Text widget, with .bell().
|
||||
.history - source statements, possibly with multiple lines.
|
||||
.prefix - source already entered at prompt; filters history list.
|
||||
.pointer - index into history.
|
||||
.cyclic - wrap around history list (or not).
|
||||
'''
|
||||
self.text = text
|
||||
self.history = []
|
||||
self.prefix = None
|
||||
self.pointer = None
|
||||
self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
|
||||
text.bind("<<history-previous>>", self.history_prev)
|
||||
text.bind("<<history-next>>", self.history_next)
|
||||
|
||||
def history_next(self, event):
|
||||
"Fetch later statement; start with ealiest if cyclic."
|
||||
self.fetch(reverse=False)
|
||||
return "break"
|
||||
|
||||
def history_prev(self, event):
|
||||
"Fetch earlier statement; start with most recent."
|
||||
self.fetch(reverse=True)
|
||||
return "break"
|
||||
|
||||
def fetch(self, reverse):
|
||||
'''Fetch statememt and replace current line in text widget.
|
||||
|
||||
Set prefix and pointer as needed for successive fetches.
|
||||
Reset them to None, None when returning to the start line.
|
||||
Sound bell when return to start line or cannot leave a line
|
||||
because cyclic is False.
|
||||
'''
|
||||
nhist = len(self.history)
|
||||
pointer = self.pointer
|
||||
prefix = self.prefix
|
||||
if pointer is not None and prefix is not None:
|
||||
if self.text.compare("insert", "!=", "end-1c") or \
|
||||
self.text.get("iomark", "end-1c") != self.history[pointer]:
|
||||
pointer = prefix = None
|
||||
self.text.mark_set("insert", "end-1c") # != after cursor move
|
||||
if pointer is None or prefix is None:
|
||||
prefix = self.text.get("iomark", "end-1c")
|
||||
if reverse:
|
||||
pointer = nhist # will be decremented
|
||||
else:
|
||||
if self.cyclic:
|
||||
pointer = -1 # will be incremented
|
||||
else: # abort history_next
|
||||
self.text.bell()
|
||||
return
|
||||
nprefix = len(prefix)
|
||||
while 1:
|
||||
pointer += -1 if reverse else 1
|
||||
if pointer < 0 or pointer >= nhist:
|
||||
self.text.bell()
|
||||
if not self.cyclic and pointer < 0: # abort history_prev
|
||||
return
|
||||
else:
|
||||
if self.text.get("iomark", "end-1c") != prefix:
|
||||
self.text.delete("iomark", "end-1c")
|
||||
self.text.insert("iomark", prefix)
|
||||
pointer = prefix = None
|
||||
break
|
||||
item = self.history[pointer]
|
||||
if item[:nprefix] == prefix and len(item) > nprefix:
|
||||
self.text.delete("iomark", "end-1c")
|
||||
self.text.insert("iomark", item)
|
||||
break
|
||||
self.text.see("insert")
|
||||
self.text.tag_remove("sel", "1.0", "end")
|
||||
self.pointer = pointer
|
||||
self.prefix = prefix
|
||||
|
||||
def store(self, source):
|
||||
"Store Shell input statement into history list."
|
||||
source = source.strip()
|
||||
if len(source) > 2:
|
||||
# avoid duplicates
|
||||
try:
|
||||
self.history.remove(source)
|
||||
except ValueError:
|
||||
pass
|
||||
self.history.append(source)
|
||||
self.pointer = None
|
||||
self.prefix = None
|
||||
|
||||
if __name__ == "__main__":
|
||||
from unittest import main
|
||||
main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
|
||||
446
Darwin/lib/python3.5/idlelib/MultiCall.py
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
"""
|
||||
MultiCall - a class which inherits its methods from a Tkinter widget (Text, for
|
||||
example), but enables multiple calls of functions per virtual event - all
|
||||
matching events will be called, not only the most specific one. This is done
|
||||
by wrapping the event functions - event_add, event_delete and event_info.
|
||||
MultiCall recognizes only a subset of legal event sequences. Sequences which
|
||||
are not recognized are treated by the original Tk handling mechanism. A
|
||||
more-specific event will be called before a less-specific event.
|
||||
|
||||
The recognized sequences are complete one-event sequences (no emacs-style
|
||||
Ctrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
|
||||
Key/Button Press/Release events can have modifiers.
|
||||
The recognized modifiers are Shift, Control, Option and Command for Mac, and
|
||||
Control, Alt, Shift, Meta/M for other platforms.
|
||||
|
||||
For all events which were handled by MultiCall, a new member is added to the
|
||||
event instance passed to the binded functions - mc_type. This is one of the
|
||||
event type constants defined in this module (such as MC_KEYPRESS).
|
||||
For Key/Button events (which are handled by MultiCall and may receive
|
||||
modifiers), another member is added - mc_state. This member gives the state
|
||||
of the recognized modifiers, as a combination of the modifier constants
|
||||
also defined in this module (for example, MC_SHIFT).
|
||||
Using these members is absolutely portable.
|
||||
|
||||
The order by which events are called is defined by these rules:
|
||||
1. A more-specific event will be called before a less-specific event.
|
||||
2. A recently-binded event will be called before a previously-binded event,
|
||||
unless this conflicts with the first rule.
|
||||
Each function will be called at most once for each event.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import tkinter
|
||||
|
||||
# the event type constants, which define the meaning of mc_type
|
||||
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
|
||||
MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
|
||||
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
|
||||
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
|
||||
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
|
||||
# the modifier state constants, which define the meaning of mc_state
|
||||
MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
|
||||
MC_OPTION = 1<<6; MC_COMMAND = 1<<7
|
||||
|
||||
# define the list of modifiers, to be used in complex event types.
|
||||
if sys.platform == "darwin":
|
||||
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
|
||||
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
|
||||
else:
|
||||
_modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
|
||||
_modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
|
||||
|
||||
# a dictionary to map a modifier name into its number
|
||||
_modifier_names = dict([(name, number)
|
||||
for number in range(len(_modifiers))
|
||||
for name in _modifiers[number]])
|
||||
|
||||
# In 3.4, if no shell window is ever open, the underlying Tk widget is
|
||||
# destroyed before .__del__ methods here are called. The following
|
||||
# is used to selectively ignore shutdown exceptions to avoid
|
||||
# 'Exception ignored' messages. See http://bugs.python.org/issue20167
|
||||
APPLICATION_GONE = "application has been destroyed"
|
||||
|
||||
# A binder is a class which binds functions to one type of event. It has two
|
||||
# methods: bind and unbind, which get a function and a parsed sequence, as
|
||||
# returned by _parse_sequence(). There are two types of binders:
|
||||
# _SimpleBinder handles event types with no modifiers and no detail.
|
||||
# No Python functions are called when no events are binded.
|
||||
# _ComplexBinder handles event types with modifiers and a detail.
|
||||
# A Python function is called each time an event is generated.
|
||||
|
||||
class _SimpleBinder:
|
||||
def __init__(self, type, widget, widgetinst):
|
||||
self.type = type
|
||||
self.sequence = '<'+_types[type][0]+'>'
|
||||
self.widget = widget
|
||||
self.widgetinst = widgetinst
|
||||
self.bindedfuncs = []
|
||||
self.handlerid = None
|
||||
|
||||
def bind(self, triplet, func):
|
||||
if not self.handlerid:
|
||||
def handler(event, l = self.bindedfuncs, mc_type = self.type):
|
||||
event.mc_type = mc_type
|
||||
wascalled = {}
|
||||
for i in range(len(l)-1, -1, -1):
|
||||
func = l[i]
|
||||
if func not in wascalled:
|
||||
wascalled[func] = True
|
||||
r = func(event)
|
||||
if r:
|
||||
return r
|
||||
self.handlerid = self.widget.bind(self.widgetinst,
|
||||
self.sequence, handler)
|
||||
self.bindedfuncs.append(func)
|
||||
|
||||
def unbind(self, triplet, func):
|
||||
self.bindedfuncs.remove(func)
|
||||
if not self.bindedfuncs:
|
||||
self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
|
||||
self.handlerid = None
|
||||
|
||||
def __del__(self):
|
||||
if self.handlerid:
|
||||
try:
|
||||
self.widget.unbind(self.widgetinst, self.sequence,
|
||||
self.handlerid)
|
||||
except tkinter.TclError as e:
|
||||
if not APPLICATION_GONE in e.args[0]:
|
||||
raise
|
||||
|
||||
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
|
||||
# (if the least significent bit is on, _modifiers[0] is on, and so on).
|
||||
# _state_subsets gives for each combination of modifiers, or *state*,
|
||||
# a list of the states which are a subset of it. This list is ordered by the
|
||||
# number of modifiers is the state - the most specific state comes first.
|
||||
_states = range(1 << len(_modifiers))
|
||||
_state_names = [''.join(m[0]+'-'
|
||||
for i, m in enumerate(_modifiers)
|
||||
if (1 << i) & s)
|
||||
for s in _states]
|
||||
|
||||
def expand_substates(states):
|
||||
'''For each item of states return a list containing all combinations of
|
||||
that item with individual bits reset, sorted by the number of set bits.
|
||||
'''
|
||||
def nbits(n):
|
||||
"number of bits set in n base 2"
|
||||
nb = 0
|
||||
while n:
|
||||
n, rem = divmod(n, 2)
|
||||
nb += rem
|
||||
return nb
|
||||
statelist = []
|
||||
for state in states:
|
||||
substates = list(set(state & x for x in states))
|
||||
substates.sort(key=nbits, reverse=True)
|
||||
statelist.append(substates)
|
||||
return statelist
|
||||
|
||||
_state_subsets = expand_substates(_states)
|
||||
|
||||
# _state_codes gives for each state, the portable code to be passed as mc_state
|
||||
_state_codes = []
|
||||
for s in _states:
|
||||
r = 0
|
||||
for i in range(len(_modifiers)):
|
||||
if (1 << i) & s:
|
||||
r |= _modifier_masks[i]
|
||||
_state_codes.append(r)
|
||||
|
||||
class _ComplexBinder:
|
||||
# This class binds many functions, and only unbinds them when it is deleted.
|
||||
# self.handlerids is the list of seqs and ids of binded handler functions.
|
||||
# The binded functions sit in a dictionary of lists of lists, which maps
|
||||
# a detail (or None) and a state into a list of functions.
|
||||
# When a new detail is discovered, handlers for all the possible states
|
||||
# are binded.
|
||||
|
||||
def __create_handler(self, lists, mc_type, mc_state):
|
||||
def handler(event, lists = lists,
|
||||
mc_type = mc_type, mc_state = mc_state,
|
||||
ishandlerrunning = self.ishandlerrunning,
|
||||
doafterhandler = self.doafterhandler):
|
||||
ishandlerrunning[:] = [True]
|
||||
event.mc_type = mc_type
|
||||
event.mc_state = mc_state
|
||||
wascalled = {}
|
||||
r = None
|
||||
for l in lists:
|
||||
for i in range(len(l)-1, -1, -1):
|
||||
func = l[i]
|
||||
if func not in wascalled:
|
||||
wascalled[func] = True
|
||||
r = l[i](event)
|
||||
if r:
|
||||
break
|
||||
if r:
|
||||
break
|
||||
ishandlerrunning[:] = []
|
||||
# Call all functions in doafterhandler and remove them from list
|
||||
for f in doafterhandler:
|
||||
f()
|
||||
doafterhandler[:] = []
|
||||
if r:
|
||||
return r
|
||||
return handler
|
||||
|
||||
def __init__(self, type, widget, widgetinst):
|
||||
self.type = type
|
||||
self.typename = _types[type][0]
|
||||
self.widget = widget
|
||||
self.widgetinst = widgetinst
|
||||
self.bindedfuncs = {None: [[] for s in _states]}
|
||||
self.handlerids = []
|
||||
# we don't want to change the lists of functions while a handler is
|
||||
# running - it will mess up the loop and anyway, we usually want the
|
||||
# change to happen from the next event. So we have a list of functions
|
||||
# for the handler to run after it finishes calling the binded functions.
|
||||
# It calls them only once.
|
||||
# ishandlerrunning is a list. An empty one means no, otherwise - yes.
|
||||
# this is done so that it would be mutable.
|
||||
self.ishandlerrunning = []
|
||||
self.doafterhandler = []
|
||||
for s in _states:
|
||||
lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
|
||||
handler = self.__create_handler(lists, type, _state_codes[s])
|
||||
seq = '<'+_state_names[s]+self.typename+'>'
|
||||
self.handlerids.append((seq, self.widget.bind(self.widgetinst,
|
||||
seq, handler)))
|
||||
|
||||
def bind(self, triplet, func):
|
||||
if triplet[2] not in self.bindedfuncs:
|
||||
self.bindedfuncs[triplet[2]] = [[] for s in _states]
|
||||
for s in _states:
|
||||
lists = [ self.bindedfuncs[detail][i]
|
||||
for detail in (triplet[2], None)
|
||||
for i in _state_subsets[s] ]
|
||||
handler = self.__create_handler(lists, self.type,
|
||||
_state_codes[s])
|
||||
seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
|
||||
self.handlerids.append((seq, self.widget.bind(self.widgetinst,
|
||||
seq, handler)))
|
||||
doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
|
||||
if not self.ishandlerrunning:
|
||||
doit()
|
||||
else:
|
||||
self.doafterhandler.append(doit)
|
||||
|
||||
def unbind(self, triplet, func):
|
||||
doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
|
||||
if not self.ishandlerrunning:
|
||||
doit()
|
||||
else:
|
||||
self.doafterhandler.append(doit)
|
||||
|
||||
def __del__(self):
|
||||
for seq, id in self.handlerids:
|
||||
try:
|
||||
self.widget.unbind(self.widgetinst, seq, id)
|
||||
except tkinter.TclError as e:
|
||||
if not APPLICATION_GONE in e.args[0]:
|
||||
raise
|
||||
|
||||
# define the list of event types to be handled by MultiEvent. the order is
|
||||
# compatible with the definition of event type constants.
|
||||
_types = (
|
||||
("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
|
||||
("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
|
||||
("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
|
||||
("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
|
||||
("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
|
||||
("Visibility",),
|
||||
)
|
||||
|
||||
# which binder should be used for every event type?
|
||||
_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
|
||||
|
||||
# A dictionary to map a type name into its number
|
||||
_type_names = dict([(name, number)
|
||||
for number in range(len(_types))
|
||||
for name in _types[number]])
|
||||
|
||||
_keysym_re = re.compile(r"^\w+$")
|
||||
_button_re = re.compile(r"^[1-5]$")
|
||||
def _parse_sequence(sequence):
|
||||
"""Get a string which should describe an event sequence. If it is
|
||||
successfully parsed as one, return a tuple containing the state (as an int),
|
||||
the event type (as an index of _types), and the detail - None if none, or a
|
||||
string if there is one. If the parsing is unsuccessful, return None.
|
||||
"""
|
||||
if not sequence or sequence[0] != '<' or sequence[-1] != '>':
|
||||
return None
|
||||
words = sequence[1:-1].split('-')
|
||||
modifiers = 0
|
||||
while words and words[0] in _modifier_names:
|
||||
modifiers |= 1 << _modifier_names[words[0]]
|
||||
del words[0]
|
||||
if words and words[0] in _type_names:
|
||||
type = _type_names[words[0]]
|
||||
del words[0]
|
||||
else:
|
||||
return None
|
||||
if _binder_classes[type] is _SimpleBinder:
|
||||
if modifiers or words:
|
||||
return None
|
||||
else:
|
||||
detail = None
|
||||
else:
|
||||
# _ComplexBinder
|
||||
if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
|
||||
type_re = _keysym_re
|
||||
else:
|
||||
type_re = _button_re
|
||||
|
||||
if not words:
|
||||
detail = None
|
||||
elif len(words) == 1 and type_re.match(words[0]):
|
||||
detail = words[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
return modifiers, type, detail
|
||||
|
||||
def _triplet_to_sequence(triplet):
|
||||
if triplet[2]:
|
||||
return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
|
||||
triplet[2]+'>'
|
||||
else:
|
||||
return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
|
||||
|
||||
_multicall_dict = {}
|
||||
def MultiCallCreator(widget):
|
||||
"""Return a MultiCall class which inherits its methods from the
|
||||
given widget class (for example, Tkinter.Text). This is used
|
||||
instead of a templating mechanism.
|
||||
"""
|
||||
if widget in _multicall_dict:
|
||||
return _multicall_dict[widget]
|
||||
|
||||
class MultiCall (widget):
|
||||
assert issubclass(widget, tkinter.Misc)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
widget.__init__(self, *args, **kwargs)
|
||||
# a dictionary which maps a virtual event to a tuple with:
|
||||
# 0. the function binded
|
||||
# 1. a list of triplets - the sequences it is binded to
|
||||
self.__eventinfo = {}
|
||||
self.__binders = [_binder_classes[i](i, widget, self)
|
||||
for i in range(len(_types))]
|
||||
|
||||
def bind(self, sequence=None, func=None, add=None):
|
||||
#print("bind(%s, %s, %s)" % (sequence, func, add),
|
||||
# file=sys.__stderr__)
|
||||
if type(sequence) is str and len(sequence) > 2 and \
|
||||
sequence[:2] == "<<" and sequence[-2:] == ">>":
|
||||
if sequence in self.__eventinfo:
|
||||
ei = self.__eventinfo[sequence]
|
||||
if ei[0] is not None:
|
||||
for triplet in ei[1]:
|
||||
self.__binders[triplet[1]].unbind(triplet, ei[0])
|
||||
ei[0] = func
|
||||
if ei[0] is not None:
|
||||
for triplet in ei[1]:
|
||||
self.__binders[triplet[1]].bind(triplet, func)
|
||||
else:
|
||||
self.__eventinfo[sequence] = [func, []]
|
||||
return widget.bind(self, sequence, func, add)
|
||||
|
||||
def unbind(self, sequence, funcid=None):
|
||||
if type(sequence) is str and len(sequence) > 2 and \
|
||||
sequence[:2] == "<<" and sequence[-2:] == ">>" and \
|
||||
sequence in self.__eventinfo:
|
||||
func, triplets = self.__eventinfo[sequence]
|
||||
if func is not None:
|
||||
for triplet in triplets:
|
||||
self.__binders[triplet[1]].unbind(triplet, func)
|
||||
self.__eventinfo[sequence][0] = None
|
||||
return widget.unbind(self, sequence, funcid)
|
||||
|
||||
def event_add(self, virtual, *sequences):
|
||||
#print("event_add(%s, %s)" % (repr(virtual), repr(sequences)),
|
||||
# file=sys.__stderr__)
|
||||
if virtual not in self.__eventinfo:
|
||||
self.__eventinfo[virtual] = [None, []]
|
||||
|
||||
func, triplets = self.__eventinfo[virtual]
|
||||
for seq in sequences:
|
||||
triplet = _parse_sequence(seq)
|
||||
if triplet is None:
|
||||
#print("Tkinter event_add(%s)" % seq, file=sys.__stderr__)
|
||||
widget.event_add(self, virtual, seq)
|
||||
else:
|
||||
if func is not None:
|
||||
self.__binders[triplet[1]].bind(triplet, func)
|
||||
triplets.append(triplet)
|
||||
|
||||
def event_delete(self, virtual, *sequences):
|
||||
if virtual not in self.__eventinfo:
|
||||
return
|
||||
func, triplets = self.__eventinfo[virtual]
|
||||
for seq in sequences:
|
||||
triplet = _parse_sequence(seq)
|
||||
if triplet is None:
|
||||
#print("Tkinter event_delete: %s" % seq, file=sys.__stderr__)
|
||||
widget.event_delete(self, virtual, seq)
|
||||
else:
|
||||
if func is not None:
|
||||
self.__binders[triplet[1]].unbind(triplet, func)
|
||||
triplets.remove(triplet)
|
||||
|
||||
def event_info(self, virtual=None):
|
||||
if virtual is None or virtual not in self.__eventinfo:
|
||||
return widget.event_info(self, virtual)
|
||||
else:
|
||||
return tuple(map(_triplet_to_sequence,
|
||||
self.__eventinfo[virtual][1])) + \
|
||||
widget.event_info(self, virtual)
|
||||
|
||||
def __del__(self):
|
||||
for virtual in self.__eventinfo:
|
||||
func, triplets = self.__eventinfo[virtual]
|
||||
if func:
|
||||
for triplet in triplets:
|
||||
try:
|
||||
self.__binders[triplet[1]].unbind(triplet, func)
|
||||
except tkinter.TclError as e:
|
||||
if not APPLICATION_GONE in e.args[0]:
|
||||
raise
|
||||
|
||||
_multicall_dict[widget] = MultiCall
|
||||
return MultiCall
|
||||
|
||||
|
||||
def _multi_call(parent):
|
||||
root = tkinter.Tk()
|
||||
root.title("Test MultiCall")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
text = MultiCallCreator(tkinter.Text)(root)
|
||||
text.pack()
|
||||
def bindseq(seq, n=[0]):
|
||||
def handler(event):
|
||||
print(seq)
|
||||
text.bind("<<handler%d>>"%n[0], handler)
|
||||
text.event_add("<<handler%d>>"%n[0], seq)
|
||||
n[0] += 1
|
||||
bindseq("<Key>")
|
||||
bindseq("<Control-Key>")
|
||||
bindseq("<Alt-Key-a>")
|
||||
bindseq("<Control-Key-a>")
|
||||
bindseq("<Alt-Control-Key-a>")
|
||||
bindseq("<Key-b>")
|
||||
bindseq("<Control-Button-1>")
|
||||
bindseq("<Button-2>")
|
||||
bindseq("<Alt-Button-1>")
|
||||
bindseq("<FocusOut>")
|
||||
bindseq("<Enter>")
|
||||
bindseq("<Leave>")
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_multi_call)
|
||||
45
Darwin/lib/python3.5/idlelib/MultiStatusBar.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from tkinter import *
|
||||
|
||||
class MultiStatusBar(Frame):
|
||||
|
||||
def __init__(self, master=None, **kw):
|
||||
if master is None:
|
||||
master = Tk()
|
||||
Frame.__init__(self, master, **kw)
|
||||
self.labels = {}
|
||||
|
||||
def set_label(self, name, text='', side=LEFT):
|
||||
if name not in self.labels:
|
||||
label = Label(self, bd=1, relief=SUNKEN, anchor=W)
|
||||
label.pack(side=side)
|
||||
self.labels[name] = label
|
||||
else:
|
||||
label = self.labels[name]
|
||||
label.config(text=text)
|
||||
|
||||
def _multistatus_bar(parent):
|
||||
root = Tk()
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d" %(x, y + 150))
|
||||
root.title("Test multistatus bar")
|
||||
frame = Frame(root)
|
||||
text = Text(frame)
|
||||
text.pack()
|
||||
msb = MultiStatusBar(frame)
|
||||
msb.set_label("one", "hello")
|
||||
msb.set_label("two", "world")
|
||||
msb.pack(side=BOTTOM, fill=X)
|
||||
|
||||
def change():
|
||||
msb.set_label("one", "foo")
|
||||
msb.set_label("two", "bar")
|
||||
|
||||
button = Button(root, text="Update status", command=change)
|
||||
button.pack(side=BOTTOM)
|
||||
frame.pack()
|
||||
frame.mainloop()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_multistatus_bar)
|
||||
959
Darwin/lib/python3.5/idlelib/NEWS.txt
Normal file
|
|
@ -0,0 +1,959 @@
|
|||
What's New in IDLE 3.5.0?
|
||||
=========================
|
||||
*Release date: 2015-09-13* ??
|
||||
|
||||
- Issue #23184: remove unused names and imports in idlelib.
|
||||
Initial patch by Al Sweigart.
|
||||
|
||||
- Issue #20577: Configuration of the max line length for the FormatParagraph
|
||||
extension has been moved from the General tab of the Idle preferences dialog
|
||||
to the FormatParagraph tab of the Config Extensions dialog.
|
||||
Patch by Tal Einat.
|
||||
|
||||
- Issue #16893: Update Idle doc chapter to match current Idle and add new
|
||||
information.
|
||||
|
||||
- Issue #3068: Add Idle extension configuration dialog to Options menu.
|
||||
Changes are written to HOME/.idlerc/config-extensions.cfg.
|
||||
Original patch by Tal Einat.
|
||||
|
||||
- Issue #16233: A module browser (File : Class Browser, Alt+C) requires a
|
||||
editor window with a filename. When Class Browser is requested otherwise,
|
||||
from a shell, output window, or 'Untitled' editor, Idle no longer displays
|
||||
an error box. It now pops up an Open Module box (Alt+M). If a valid name
|
||||
is entered and a module is opened, a corresponding browser is also opened.
|
||||
|
||||
- Issue #4832: Save As to type Python files automatically adds .py to the
|
||||
name you enter (even if your system does not display it). Some systems
|
||||
automatically add .txt when type is Text files.
|
||||
|
||||
- Issue #21986: Code objects are not normally pickled by the pickle module.
|
||||
To match this, they are no longer pickled when running under Idle.
|
||||
|
||||
- Issue #23180: Rename IDLE "Windows" menu item to "Window".
|
||||
Patch by Al Sweigart.
|
||||
|
||||
- Issue #17390: Adjust Editor window title; remove 'Python',
|
||||
move version to end.
|
||||
|
||||
- Issue #14105: Idle debugger breakpoints no longer disappear
|
||||
when inseting or deleting lines.
|
||||
|
||||
- Issue #17172: Turtledemo can now be run from Idle.
|
||||
Currently, the entry is on the Help menu, but it may move to Run.
|
||||
Patch by Ramchandra Apt and Lita Cho.
|
||||
|
||||
- Issue #21765: Add support for non-ascii identifiers to HyperParser.
|
||||
|
||||
- Issue #21940: Add unittest for WidgetRedirector. Initial patch by Saimadhav
|
||||
Heblikar.
|
||||
|
||||
- Issue #18592: Add unittest for SearchDialogBase. Patch by Phil Webster.
|
||||
|
||||
- Issue #21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar.
|
||||
|
||||
- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav
|
||||
Heblikar.
|
||||
|
||||
- Issue #12387: Add missing upper(lower)case versions of default Windows key
|
||||
bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy.
|
||||
|
||||
- Issue #21695: Closing a Find-in-files output window while the search is
|
||||
still in progress no longer closes Idle.
|
||||
|
||||
- Issue #18910: Add unittest for textView. Patch by Phil Webster.
|
||||
|
||||
- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar.
|
||||
|
||||
- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster.
|
||||
|
||||
- Issue #21477: htest.py - Improve framework, complete set of tests.
|
||||
Patches by Saimadhav Heblikar
|
||||
|
||||
- Issue #18104: Add idlelib/idle_test/htest.py with a few sample tests to begin
|
||||
consolidating and improving human-validated tests of Idle. Change other files
|
||||
as needed to work with htest. Running the module as __main__ runs all tests.
|
||||
|
||||
- Issue #21139: Change default paragraph width to 72, the PEP 8 recommendation.
|
||||
|
||||
- Issue #21284: Paragraph reformat test passes after user changes reformat width.
|
||||
|
||||
- Issue #17654: Ensure IDLE menus are customized properly on OS X for
|
||||
non-framework builds and for all variants of Tk.
|
||||
|
||||
|
||||
What's New in IDLE 3.4.0?
|
||||
=========================
|
||||
*Release date: 2014-03-16*
|
||||
|
||||
- Issue #17390: Display Python version on Idle title bar.
|
||||
Initial patch by Edmond Burnett.
|
||||
|
||||
- Issue #5066: Update IDLE docs. Patch by Todd Rovito.
|
||||
|
||||
- Issue #17625: Close the replace dialog after it is used.
|
||||
|
||||
- Issue #16226: Fix IDLE Path Browser crash.
|
||||
(Patch by Roger Serwy)
|
||||
|
||||
- Issue #15853: Prevent IDLE crash on OS X when opening Preferences menu
|
||||
with certain versions of Tk 8.5. Initial patch by Kevin Walzer.
|
||||
|
||||
|
||||
What's New in IDLE 3.3.0?
|
||||
=========================
|
||||
*Release date: 2012-09-29*
|
||||
|
||||
- Issue #17625: Close the replace dialog after it is used.
|
||||
|
||||
- Issue #7163: Propagate return value of sys.stdout.write.
|
||||
|
||||
- Issue #15318: Prevent writing to sys.stdin.
|
||||
|
||||
- Issue #4832: Modify IDLE to save files with .py extension by
|
||||
default on Windows and OS X (Tk 8.5) as it already does with X11 Tk.
|
||||
|
||||
- Issue #13532, #15319: Check that arguments to sys.stdout.write are strings.
|
||||
|
||||
- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE.
|
||||
Erroneous tool tips have been corrected. Default added for callables.
|
||||
|
||||
- Issue10365: File open dialog now works instead of crashing even when
|
||||
parent window is closed while dialog is open.
|
||||
|
||||
- Issue 14876: use user-selected font for highlight configuration.
|
||||
|
||||
- Issue #14937: Perform auto-completion of filenames in strings even for
|
||||
non-ASCII filenames. Likewise for identifiers.
|
||||
|
||||
- Issue #8515: Set __file__ when run file in IDLE.
|
||||
Initial patch by Bruce Frederiksen.
|
||||
|
||||
- IDLE can be launched as `python -m idlelib`
|
||||
|
||||
- Issue #14409: IDLE now properly executes commands in the Shell window
|
||||
when it cannot read the normal config files on startup and
|
||||
has to use the built-in default key bindings.
|
||||
There was previously a bug in one of the defaults.
|
||||
|
||||
- Issue #3573: IDLE hangs when passing invalid command line args
|
||||
(directory(ies) instead of file(s)).
|
||||
|
||||
- Issue #14018: Update checks for unstable system Tcl/Tk versions on OS X
|
||||
to include versions shipped with OS X 10.7 and 10.8 in addition to 10.6.
|
||||
|
||||
|
||||
What's New in IDLE 3.2.1?
|
||||
=========================
|
||||
*Release date: 15-May-11*
|
||||
|
||||
- Issue #6378: Further adjust idle.bat to start associated Python
|
||||
|
||||
- Issue #11896: Save on Close failed despite selecting "Yes" in dialog.
|
||||
|
||||
- Issue #1028: Ctrl-space binding to show completions was causing IDLE to exit.
|
||||
Tk < 8.5 was sending invalid Unicode null; replaced with valid null.
|
||||
|
||||
- <Home> toggle failing on Tk 8.5, causing IDLE exits and strange selection
|
||||
behavior. Issue 4676. Improve selection extension behaviour.
|
||||
|
||||
- <Home> toggle non-functional when NumLock set on Windows. Issue 3851.
|
||||
|
||||
|
||||
What's New in IDLE 3.1b1?
|
||||
=========================
|
||||
*Release date: 06-May-09*
|
||||
|
||||
- Use of 'filter' in keybindingDialog.py was causing custom key assignment to
|
||||
fail. Patch 5707 amaury.forgeotdarc.
|
||||
|
||||
|
||||
What's New in IDLE 3.1a1?
|
||||
=========================
|
||||
*Release date: 07-Mar-09*
|
||||
|
||||
- Issue #4815: Offer conversion to UTF-8 if source files have
|
||||
no encoding declaration and are not encoded in UTF-8.
|
||||
|
||||
- Issue #4008: Fix problems with non-ASCII source files.
|
||||
|
||||
- Issue #4323: Always encode source as UTF-8 without asking
|
||||
the user (unless a different encoding is declared); remove
|
||||
user configuration of source encoding; all according to
|
||||
PEP 3120.
|
||||
|
||||
- Issue #2665: On Windows, an IDLE installation upgraded from an old version
|
||||
would not start if a custom theme was defined.
|
||||
|
||||
What's New in IDLE 2.7? (UNRELEASED, but merged into 3.1 releases above.)
|
||||
=======================
|
||||
*Release date: XX-XXX-2010*
|
||||
|
||||
- idle.py modified and simplified to better support developing experimental
|
||||
versions of IDLE which are not installed in the standard location.
|
||||
|
||||
- OutputWindow/PyShell right click menu "Go to file/line" wasn't working with
|
||||
file paths containing spaces. Bug 5559.
|
||||
|
||||
- Windows: Version string for the .chm help file changed, file not being
|
||||
accessed Patch 5783 Guilherme Polo
|
||||
|
||||
- Allow multiple IDLE GUI/subprocess pairs to exist simultaneously. Thanks to
|
||||
David Scherer for suggesting the use of an ephemeral port for the GUI.
|
||||
Patch 1529142 Weeble.
|
||||
|
||||
- Remove port spec from run.py and fix bug where subprocess fails to
|
||||
extract port from command line when warnings are present.
|
||||
|
||||
- Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr to handle
|
||||
mixed space/tab properly. Issue 5129, patch by Guilherme Polo.
|
||||
|
||||
- Issue #3549: On MacOS the preferences menu was not present
|
||||
|
||||
What's New in IDLE 3.0 final?
|
||||
=============================
|
||||
|
||||
*Release date: 03-Dec-2008*
|
||||
|
||||
- IDLE would print a "Unhandled server exception!" message when internal
|
||||
debugging is enabled.
|
||||
|
||||
- Issue #4455: IDLE failed to display the windows list when two windows have
|
||||
the same title.
|
||||
|
||||
- Issue #4383: When IDLE cannot make the connection to its subprocess, it would
|
||||
fail to properly display the error message.
|
||||
|
||||
|
||||
What's New in IDLE 3.0a3?
|
||||
=========================
|
||||
|
||||
*Release date: 29-Feb-2008*
|
||||
|
||||
- help() was not paging to the shell. Issue1650.
|
||||
|
||||
- CodeContext was not importing.
|
||||
|
||||
- Corrected two 3.0 compatibility errors reported by Mark Summerfield:
|
||||
http://mail.python.org/pipermail/python-3000/2007-December/011491.html
|
||||
|
||||
- Shell was not colorizing due to bug introduced at r57998, Bug 1586.
|
||||
|
||||
- Issue #1585: IDLE uses non-existent xrange() function.
|
||||
|
||||
|
||||
What's New in IDLE 3.0a2?
|
||||
=========================
|
||||
|
||||
*Release date: 06-Dec-2007*
|
||||
|
||||
- Windows EOL sequence not converted correctly, encoding error.
|
||||
Caused file save to fail. Bug 1130.
|
||||
|
||||
|
||||
What's New in IDLE 3.0a1?
|
||||
=========================
|
||||
|
||||
*Release date: 31-Aug-2007*
|
||||
|
||||
- IDLE converted to Python 3000 syntax.
|
||||
|
||||
- Strings became Unicode.
|
||||
|
||||
- CallTips module now uses the inspect module to produce the argspec.
|
||||
|
||||
- IDLE modules now use absolute import instead of implied relative import.
|
||||
|
||||
- atexit call replaces sys.exitfunc. The functionality of delete-exitfunc flag
|
||||
in config-main.cfg remains unchanged: if set, registered exit functions will
|
||||
be cleared before IDLE exits.
|
||||
|
||||
|
||||
What's New in IDLE 2.6 final?
|
||||
=============================
|
||||
|
||||
*Release date: 01-Oct-2008*, merged into 3.0 releases detailed above (3.0rc2)
|
||||
|
||||
- Issue #2665: On Windows, an IDLE installation upgraded from an old version
|
||||
would not start if a custom theme was defined.
|
||||
|
||||
- Home / Control-A toggles between left margin and end of leading white
|
||||
space. issue1196903, patch by Jeff Shute.
|
||||
|
||||
- Improved AutoCompleteWindow logic. issue2062, patch by Tal Einat.
|
||||
|
||||
- Autocompletion of filenames now support alternate separators, e.g. the
|
||||
'/' char on Windows. issue2061 Patch by Tal Einat.
|
||||
|
||||
- Configured selection highlighting colors were ignored; updating highlighting
|
||||
in the config dialog would cause non-Python files to be colored as if they
|
||||
were Python source; improve use of ColorDelagator. Patch 1334. Tal Einat.
|
||||
|
||||
- ScriptBinding event handlers weren't returning 'break'. Patch 2050, Tal Einat
|
||||
|
||||
- There was an error on exit if no sys.exitfunc was defined. Issue 1647.
|
||||
|
||||
- Could not open files in .idlerc directory if latter was hidden on Windows.
|
||||
Issue 1743, Issue 1862.
|
||||
|
||||
- Configure Dialog: improved layout for keybinding. Patch 1457 Tal Einat.
|
||||
|
||||
- tabpage.py updated: tabbedPages.py now supports multiple dynamic rows
|
||||
of tabs. Patch 1612746 Tal Einat.
|
||||
|
||||
- Add confirmation dialog before printing. Patch 1717170 Tal Einat.
|
||||
|
||||
- Show paste position if > 80 col. Patch 1659326 Tal Einat.
|
||||
|
||||
- Update cursor color without restarting. Patch 1725576 Tal Einat.
|
||||
|
||||
- Allow keyboard interrupt only when user code is executing in subprocess.
|
||||
Patch 1225 Tal Einat (reworked from IDLE-Spoon).
|
||||
|
||||
- configDialog cleanup. Patch 1730217 Tal Einat.
|
||||
|
||||
- textView cleanup. Patch 1718043 Tal Einat.
|
||||
|
||||
- Clean up EditorWindow close.
|
||||
|
||||
- Patch 1693258: Fix for duplicate "preferences" menu-OS X. Backport of r56204.
|
||||
|
||||
- OSX: Avoid crash for those versions of Tcl/Tk which don't have a console
|
||||
|
||||
- Bug in idlelib.MultiCall: Options dialog was crashing IDLE if there was an
|
||||
option in config-extensions w/o a value. Patch #1672481, Tal Einat
|
||||
|
||||
- Corrected some bugs in AutoComplete. Also, Page Up/Down in ACW implemented;
|
||||
mouse and cursor selection in ACWindow implemented; double Tab inserts
|
||||
current selection and closes ACW (similar to double-click and Return); scroll
|
||||
wheel now works in ACW. Added AutoComplete instructions to IDLE Help.
|
||||
|
||||
- AutoCompleteWindow moved below input line, will move above if there
|
||||
isn't enough space. Patch 1621265 Tal Einat
|
||||
|
||||
- Calltips now 'handle' tuples in the argument list (display '<tuple>' :)
|
||||
Suggested solution by Christos Georgiou, Bug 791968.
|
||||
|
||||
- Add 'raw' support to configHandler. Patch 1650174 Tal Einat.
|
||||
|
||||
- Avoid hang when encountering a duplicate in a completion list. Bug 1571112.
|
||||
|
||||
- Patch #1362975: Rework CodeContext indentation algorithm to
|
||||
avoid hard-coding pixel widths.
|
||||
|
||||
- Bug #813342: Start the IDLE subprocess with -Qnew if the parent
|
||||
is started with that option.
|
||||
|
||||
- Honor the "Cancel" action in the save dialog (Debian bug #299092)
|
||||
|
||||
- Some syntax errors were being caught by tokenize during the tabnanny
|
||||
check, resulting in obscure error messages. Do the syntax check
|
||||
first. Bug 1562716, 1562719
|
||||
|
||||
- IDLE's version number takes a big jump to match the version number of
|
||||
the Python release of which it's a part.
|
||||
|
||||
|
||||
What's New in IDLE 1.2?
|
||||
=======================
|
||||
|
||||
*Release date: 19-SEP-2006*
|
||||
|
||||
|
||||
What's New in IDLE 1.2c1?
|
||||
=========================
|
||||
|
||||
*Release date: 17-AUG-2006*
|
||||
|
||||
- File menu hotkeys: there were three 'p' assignments. Reassign the
|
||||
'Save Copy As' and 'Print' hotkeys to 'y' and 't'. Change the
|
||||
Shell hotkey from 's' to 'l'.
|
||||
|
||||
- IDLE honors new quit() and exit() commands from site.py Quitter() object.
|
||||
Patch 1540892, Jim Jewett
|
||||
|
||||
- The 'with' statement is now a Code Context block opener.
|
||||
Patch 1540851, Jim Jewett
|
||||
|
||||
- Retrieval of previous shell command was not always preserving indentation
|
||||
(since 1.2a1) Patch 1528468 Tal Einat.
|
||||
|
||||
- Changing tokenize (39046) to detect dedent broke tabnanny check (since 1.2a1)
|
||||
|
||||
- ToggleTab dialog was setting indent to 8 even if cancelled (since 1.2a1).
|
||||
|
||||
- When used w/o subprocess, all exceptions were preceded by an error
|
||||
message claiming they were IDLE internal errors (since 1.2a1).
|
||||
|
||||
What's New in IDLE 1.2b3?
|
||||
=========================
|
||||
|
||||
*Release date: 03-AUG-2006*
|
||||
|
||||
- Bug #1525817: Don't truncate short lines in IDLE's tool tips.
|
||||
|
||||
- Bug #1517990: IDLE keybindings on MacOS X now work correctly
|
||||
|
||||
- Bug #1517996: IDLE now longer shows the default Tk menu when a
|
||||
path browser, class browser or debugger is the frontmost window on MacOS X
|
||||
|
||||
- EditorWindow.test() was failing. Bug 1417598
|
||||
|
||||
- EditorWindow failed when used stand-alone if sys.ps1 not set.
|
||||
Bug 1010370 Dave Florek
|
||||
|
||||
- Tooltips failed on new-syle class __init__ args. Bug 1027566 Loren Guthrie
|
||||
|
||||
- Avoid occasional failure to detect closing paren properly.
|
||||
Patch 1407280 Tal Einat
|
||||
|
||||
- Rebinding Tab key was inserting 'tab' instead of 'Tab'. Bug 1179168.
|
||||
|
||||
- Colorizer now handles #<builtin> correctly, also unicode strings and
|
||||
'as' keyword in comment directly following import command. Closes 1325071.
|
||||
Patch 1479219 Tal Einat
|
||||
|
||||
What's New in IDLE 1.2b2?
|
||||
=========================
|
||||
|
||||
*Release date: 11-JUL-2006*
|
||||
|
||||
What's New in IDLE 1.2b1?
|
||||
=========================
|
||||
|
||||
*Release date: 20-JUN-2006*
|
||||
|
||||
What's New in IDLE 1.2a2?
|
||||
=========================
|
||||
|
||||
*Release date: 27-APR-2006*
|
||||
|
||||
What's New in IDLE 1.2a1?
|
||||
=========================
|
||||
|
||||
*Release date: 05-APR-2006*
|
||||
|
||||
- Patch #1162825: Support non-ASCII characters in IDLE window titles.
|
||||
|
||||
- Source file f.flush() after writing; trying to avoid lossage if user
|
||||
kills GUI.
|
||||
|
||||
- Options / Keys / Advanced dialog made functional. Also, allow binding
|
||||
of 'movement' keys.
|
||||
|
||||
- 'syntax' patch adds improved calltips and a new class attribute listbox.
|
||||
MultiCall module allows binding multiple actions to an event.
|
||||
Patch 906702 Noam Raphael
|
||||
|
||||
- Better indentation after first line of string continuation.
|
||||
IDLEfork Patch 681992, Noam Raphael
|
||||
|
||||
- Fixed CodeContext alignment problem, following suggestion from Tal Einat.
|
||||
|
||||
- Increased performance in CodeContext extension Patch 936169 Noam Raphael
|
||||
|
||||
- Mac line endings were incorrect when pasting code from some browsers
|
||||
when using X11 and the Fink distribution. Python Bug 1263656.
|
||||
|
||||
- <Enter> when cursor is on a previous command retrieves that command. Instead
|
||||
of replacing the input line, the previous command is now appended to the
|
||||
input line. Indentation is preserved, and undo is enabled.
|
||||
Patch 1196917 Jeff Shute
|
||||
|
||||
- Clarify "tab/space" Error Dialog and "Tab Width" Dialog associated with
|
||||
the Untabify command.
|
||||
|
||||
- Corrected "tab/space" Error Dialog to show correct menu for Untabify.
|
||||
Patch 1196980 Jeff Shute
|
||||
|
||||
- New files are colorized by default, and colorizing is removed when
|
||||
saving as non-Python files. Patch 1196895 Jeff Shute
|
||||
Closes Python Bugs 775012 and 800432, partial fix IDLEfork 763524
|
||||
|
||||
- Improve subprocess link error notification.
|
||||
|
||||
- run.py: use Queue's blocking feature instead of sleeping in the main
|
||||
loop. Patch # 1190163 Michiel de Hoon
|
||||
|
||||
- Add config-main option to make the 'history' feature non-cyclic.
|
||||
Default remains cyclic. Python Patch 914546 Noam Raphael.
|
||||
|
||||
- Removed ability to configure tabs indent from Options dialog. This 'feature'
|
||||
has never worked and no one has complained. It is still possible to set a
|
||||
default tabs (v. spaces) indent 'manually' via config-main.def (or to turn on
|
||||
tabs for the current EditorWindow via the Format menu) but IDLE will
|
||||
encourage indentation via spaces.
|
||||
|
||||
- Enable setting the indentation width using the Options dialog.
|
||||
Bug # 783877
|
||||
|
||||
- Add keybindings for del-word-left and del-word-right.
|
||||
|
||||
- Discourage using an indent width other than 8 when using tabs to indent
|
||||
Python code.
|
||||
|
||||
- Restore use of EditorWindow.set_indentation_params(), was dead code since
|
||||
Autoindent was merged into EditorWindow. This allows IDLE to conform to the
|
||||
indentation width of a loaded file. (But it still will not switch to tabs
|
||||
even if the file uses tabs.) Any change in indent width is local to that
|
||||
window.
|
||||
|
||||
- Add Tabnanny check before Run/F5, not just when Checking module.
|
||||
|
||||
- If an extension can't be loaded, print warning and skip it instead of
|
||||
erroring out.
|
||||
|
||||
- Improve error handling when .idlerc can't be created (warn and exit).
|
||||
|
||||
- The GUI was hanging if the shell window was closed while a raw_input()
|
||||
was pending. Restored the quit() of the readline() mainloop().
|
||||
http://mail.python.org/pipermail/idle-dev/2004-December/002307.html
|
||||
|
||||
- The remote procedure call module rpc.py can now access data attributes of
|
||||
remote registered objects. Changes to these attributes are local, however.
|
||||
|
||||
What's New in IDLE 1.1?
|
||||
=======================
|
||||
|
||||
*Release date: 30-NOV-2004*
|
||||
|
||||
- On OpenBSD, terminating IDLE with ctrl-c from the command line caused a
|
||||
stuck subprocess MainThread because only the SocketThread was exiting.
|
||||
|
||||
What's New in IDLE 1.1b3/rc1?
|
||||
=============================
|
||||
|
||||
*Release date: 18-NOV-2004*
|
||||
|
||||
- Saving a Keyset w/o making changes (by using the "Save as New Custom Key Set"
|
||||
button) caused IDLE to fail on restart (no new keyset was created in
|
||||
config-keys.cfg). Also true for Theme/highlights. Python Bug 1064535.
|
||||
|
||||
- A change to the linecache.py API caused IDLE to exit when an exception was
|
||||
raised while running without the subprocess (-n switch). Python Bug 1063840.
|
||||
|
||||
What's New in IDLE 1.1b2?
|
||||
=========================
|
||||
|
||||
*Release date: 03-NOV-2004*
|
||||
|
||||
- When paragraph reformat width was made configurable, a bug was
|
||||
introduced that caused reformatting of comment blocks to ignore how
|
||||
far the block was indented, effectively adding the indentation width
|
||||
to the reformat width. This has been repaired, and the reformat
|
||||
width is again a bound on the total width of reformatted lines.
|
||||
|
||||
What's New in IDLE 1.1b1?
|
||||
=========================
|
||||
|
||||
*Release date: 15-OCT-2004*
|
||||
|
||||
|
||||
What's New in IDLE 1.1a3?
|
||||
=========================
|
||||
|
||||
*Release date: 02-SEP-2004*
|
||||
|
||||
- Improve keyboard focus binding, especially in Windows menu. Improve
|
||||
window raising, especially in the Windows menu and in the debugger.
|
||||
IDLEfork 763524.
|
||||
|
||||
- If user passes a non-existent filename on the commandline, just
|
||||
open a new file, don't raise a dialog. IDLEfork 854928.
|
||||
|
||||
|
||||
What's New in IDLE 1.1a2?
|
||||
=========================
|
||||
|
||||
*Release date: 05-AUG-2004*
|
||||
|
||||
- EditorWindow.py was not finding the .chm help file on Windows. Typo
|
||||
at Rev 1.54. Python Bug 990954
|
||||
|
||||
- checking sys.platform for substring 'win' was breaking IDLE docs on Mac
|
||||
(darwin). Also, Mac Safari browser requires full file:// URIs. SF 900580.
|
||||
|
||||
|
||||
What's New in IDLE 1.1a1?
|
||||
=========================
|
||||
|
||||
*Release date: 08-JUL-2004*
|
||||
|
||||
- Redirect the warning stream to the shell during the ScriptBinding check of
|
||||
user code and format the warning similarly to an exception for both that
|
||||
check and for runtime warnings raised in the subprocess.
|
||||
|
||||
- CodeContext hint pane visibility state is now persistent across sessions.
|
||||
The pane no longer appears in the shell window. Added capability to limit
|
||||
extensions to shell window or editor windows. Noam Raphael addition
|
||||
to Patch 936169.
|
||||
|
||||
- Paragraph reformat width is now a configurable parameter in the
|
||||
Options GUI.
|
||||
|
||||
- New Extension: CodeContext. Provides block structuring hints for code
|
||||
which has scrolled above an edit window. Patch 936169 Noam Raphael.
|
||||
|
||||
- If nulls somehow got into the strings in recent-files.lst
|
||||
EditorWindow.update_recent_files_list() was failing. Python Bug 931336.
|
||||
|
||||
- If the normal background is changed via Configure/Highlighting, it will
|
||||
update immediately, thanks to the previously mentioned patch by Nigel Rowe.
|
||||
|
||||
- Add a highlight theme for builtin keywords. Python Patch 805830 Nigel Rowe
|
||||
This also fixed IDLEfork bug [ 693418 ] Normal text background color not
|
||||
refreshed and Python bug [897872 ] Unknown color name on HP-UX
|
||||
|
||||
- rpc.py:SocketIO - Large modules were generating large pickles when downloaded
|
||||
to the execution server. The return of the OK response from the subprocess
|
||||
initialization was interfering and causing the sending socket to be not
|
||||
ready. Add an IO ready test to fix this. Moved the polling IO ready test
|
||||
into pollpacket().
|
||||
|
||||
- Fix typo in rpc.py, s/b "pickle.PicklingError" not "pickle.UnpicklingError".
|
||||
|
||||
- Added a Tk error dialog to run.py inform the user if the subprocess can't
|
||||
connect to the user GUI process. Added a timeout to the GUI's listening
|
||||
socket. Added Tk error dialogs to PyShell.py to announce a failure to bind
|
||||
the port or connect to the subprocess. Clean up error handling during
|
||||
connection initiation phase. This is an update of Python Patch 778323.
|
||||
|
||||
- Print correct exception even if source file changed since shell was
|
||||
restarted. IDLEfork Patch 869012 Noam Raphael
|
||||
|
||||
- Keybindings with the Shift modifier now work correctly. So do bindings which
|
||||
use the Space key. Limit unmodified user keybindings to the function keys.
|
||||
Python Bug 775353, IDLEfork Bugs 755647, 761557
|
||||
|
||||
- After an exception, run.py was not setting the exception vector. Noam
|
||||
Raphael suggested correcting this so pdb's postmortem pm() would work.
|
||||
IDLEfork Patch 844675
|
||||
|
||||
- IDLE now does not fail to save the file anymore if the Tk buffer is not a
|
||||
Unicode string, yet eol_convention is. Python Bugs 774680, 788378
|
||||
|
||||
- IDLE didn't start correctly when Python was installed in "Program Files" on
|
||||
W2K and XP. Python Bugs 780451, 784183
|
||||
|
||||
- config-main.def documentation incorrectly referred to idle- instead of
|
||||
config- filenames. SF 782759 Also added note about .idlerc location.
|
||||
|
||||
|
||||
What's New in IDLE 1.0?
|
||||
=======================
|
||||
|
||||
*Release date: 29-Jul-2003*
|
||||
|
||||
- Added a banner to the shell discussing warnings possibly raised by personal
|
||||
firewall software. Added same comment to README.txt.
|
||||
|
||||
|
||||
What's New in IDLE 1.0 release candidate 2?
|
||||
===========================================
|
||||
|
||||
*Release date: 24-Jul-2003*
|
||||
|
||||
- Calltip error when docstring was None Python Bug 775541
|
||||
|
||||
|
||||
What's New in IDLE 1.0 release candidate 1?
|
||||
===========================================
|
||||
|
||||
*Release date: 18-Jul-2003*
|
||||
|
||||
- Updated extend.txt, help.txt, and config-extensions.def to correctly
|
||||
reflect the current status of the configuration system. Python Bug 768469
|
||||
|
||||
- Fixed: Call Tip Trimming May Loop Forever. Python Patch 769142 (Daniels)
|
||||
|
||||
- Replaced apply(f, args, kwds) with f(*args, **kwargs) to improve performance
|
||||
Python Patch 768187
|
||||
|
||||
- Break or continue statements outside a loop were causing IDLE crash
|
||||
Python Bug 767794
|
||||
|
||||
- Convert Unicode strings from readline to IOBinding.encoding. Also set
|
||||
sys.std{in|out|err}.encoding, for both the local and the subprocess case.
|
||||
SF IDLEfork patch 682347.
|
||||
|
||||
|
||||
What's New in IDLE 1.0b2?
|
||||
=========================
|
||||
|
||||
*Release date: 29-Jun-2003*
|
||||
|
||||
- Extend AboutDialog.ViewFile() to support file encodings. Make the CREDITS
|
||||
file Latin-1.
|
||||
|
||||
- Updated the About dialog to reflect re-integration into Python. Provide
|
||||
buttons to display Python's NEWS, License, and Credits, plus additional
|
||||
buttons for IDLE's README and NEWS.
|
||||
|
||||
- TextViewer() now has a third parameter which allows inserting text into the
|
||||
viewer instead of reading from a file.
|
||||
|
||||
- (Created the .../Lib/idlelib directory in the Python CVS, which is a clone of
|
||||
IDLEfork modified to install in the Python environment. The code in the
|
||||
interrupt module has been moved to thread.interrupt_main(). )
|
||||
|
||||
- Printing the Shell window was failing if it was not saved first SF 748975
|
||||
|
||||
- When using the Search in Files dialog, if the user had a selection
|
||||
highlighted in his Editor window, insert it into the dialog search field.
|
||||
|
||||
- The Python Shell entry was disappearing from the Windows menu.
|
||||
|
||||
- Update the Windows file list when a file name change occurs
|
||||
|
||||
- Change to File / Open Module: always pop up the dialog, using the current
|
||||
selection as the default value. This is easier to use habitually.
|
||||
|
||||
- Avoided a problem with starting the subprocess when 'localhost' doesn't
|
||||
resolve to the user's loopback interface. SF 747772
|
||||
|
||||
- Fixed an issue with highlighted errors never de-colorizing. SF 747677. Also
|
||||
improved notification of Tabnanny Token Error.
|
||||
|
||||
- File / New will by default save in the directory of the Edit window from
|
||||
which it was initiated. SF 748973 Guido van Rossum patch.
|
||||
|
||||
|
||||
What's New in IDLEfork 0.9b1?
|
||||
=============================
|
||||
|
||||
*Release date: 02-Jun-2003*
|
||||
|
||||
- The current working directory of the execution environment (and shell
|
||||
following completion of execution) is now that of the module being run.
|
||||
|
||||
- Added the delete-exitfunc option to config-main.def. (This option is not
|
||||
included in the Options dialog.) Setting this to True (the default) will
|
||||
cause IDLE to not run sys.exitfunc/atexit when the subprocess exits.
|
||||
|
||||
- IDLE now preserves the line ending codes when editing a file produced on
|
||||
a different platform. SF 661759, SF 538584
|
||||
|
||||
- Reduced default editor font size to 10 point and increased window height
|
||||
to provide a better initial impression on Windows.
|
||||
|
||||
- Options / Fonts/Tabs / Set Base Editor Font: List box was not highlighting
|
||||
the default font when first installed on Windows. SF 661676
|
||||
|
||||
- Added Autosave feature: when user runs code from edit window, if the file
|
||||
has been modified IDLE will silently save it if Autosave is enabled. The
|
||||
option is set in the Options dialog, and the default is to prompt the
|
||||
user to save the file. SF 661318 Bruce Sherwood patch.
|
||||
|
||||
- Improved the RESTART annotation in the shell window when the user restarts
|
||||
the shell while it is generating output. Also improved annotation when user
|
||||
repeatedly hammers the Ctrl-F6 restart.
|
||||
|
||||
- Allow IDLE to run when not installed and cwd is not the IDLE directory
|
||||
SF Patch 686254 "Run IDLEfork from any directory without set-up" - Raphael
|
||||
|
||||
- When a module is run from an EditorWindow: if its directory is not in
|
||||
sys.path, prepend it. This allows the module to import other modules in
|
||||
the same directory. Do the same for a script run from the command line.
|
||||
|
||||
- Correctly restart the subprocess if it is running user code and the user
|
||||
attempts to run some other module or restarts the shell. Do the same if
|
||||
the link is broken and it is possible to restart the subprocess and re-
|
||||
connect to the GUI. SF RFE 661321.
|
||||
|
||||
- Improved exception reporting when running commands or scripts from the
|
||||
command line.
|
||||
|
||||
- Added a -n command line switch to start IDLE without the subprocess.
|
||||
Removed the Shell menu when running in that mode. Updated help messages.
|
||||
|
||||
- Added a comment to the shell startup header to indicate when IDLE is not
|
||||
using the subprocess.
|
||||
|
||||
- Restore the ability to run without the subprocess. This can be important for
|
||||
some platforms or configurations. (Running without the subprocess allows the
|
||||
debugger to trace through parts of IDLE itself, which may or may not be
|
||||
desirable, depending on your point of view. In addition, the traditional
|
||||
reload/import tricks must be use if user source code is changed.) This is
|
||||
helpful for developing IDLE using IDLE, because one instance can be used to
|
||||
edit the code and a separate instance run to test changes. (Multiple
|
||||
concurrent IDLE instances with subprocesses is a future feature)
|
||||
|
||||
- Improve the error message a user gets when saving a file with non-ASCII
|
||||
characters and no source encoding is specified. Done by adding a dialog
|
||||
'EncodingMessage', which contains the line to add in a fixed-font entry
|
||||
widget, and which has a button to add that line to the file automatically.
|
||||
Also, add a configuration option 'EditorWindow/encoding', which has three
|
||||
possible values: none, utf-8, and locale. None is the default: IDLE will show
|
||||
this dialog when non-ASCII characters are encountered. utf-8 means that files
|
||||
with non-ASCII characters are saved as utf-8-with-bom. locale means that
|
||||
files are saved in the locale's encoding; the dialog is only displayed if the
|
||||
source contains characters outside the locale's charset. SF 710733 - Loewis
|
||||
|
||||
- Improved I/O response by tweaking the wait parameter in various
|
||||
calls to signal.signal().
|
||||
|
||||
- Implemented a threaded subprocess which allows interrupting a pass
|
||||
loop in user code using the 'interrupt' extension. User code runs
|
||||
in MainThread, while the RPCServer is handled by SockThread. This is
|
||||
necessary because Windows doesn't support signals.
|
||||
|
||||
- Implemented the 'interrupt' extension module, which allows a subthread
|
||||
to raise a KeyboardInterrupt in the main thread.
|
||||
|
||||
- Attempting to save the shell raised an error related to saving
|
||||
breakpoints, which are not implemented in the shell
|
||||
|
||||
- Provide a correct message when 'exit' or 'quit' are entered at the
|
||||
IDLE command prompt SF 695861
|
||||
|
||||
- Eliminate extra blank line in shell output caused by not flushing
|
||||
stdout when user code ends with an unterminated print. SF 695861
|
||||
|
||||
- Moved responsibility for exception formatting (i.e. pruning IDLE internal
|
||||
calls) out of rpc.py into the client and server.
|
||||
|
||||
- Exit IDLE cleanly even when doing subprocess I/O
|
||||
|
||||
- Handle subprocess interrupt with an RPC message.
|
||||
|
||||
- Restart the subprocess if it terminates itself. (VPython programs do that)
|
||||
|
||||
- Support subclassing of exceptions, including in the shell, by moving the
|
||||
exception formatting to the subprocess.
|
||||
|
||||
|
||||
|
||||
What's New in IDLEfork 0.9 Alpha 2?
|
||||
===================================
|
||||
|
||||
*Release date: 27-Jan-2003*
|
||||
|
||||
- Updated INSTALL.txt to claify use of the python2 rpm.
|
||||
|
||||
- Improved formatting in IDLE Help.
|
||||
|
||||
- Run menu: Replace "Run Script" with "Run Module".
|
||||
|
||||
- Code encountering an unhandled exception under the debugger now shows
|
||||
the correct traceback, with IDLE internal levels pruned out.
|
||||
|
||||
- If an exception occurs entirely in IDLE, don't prune the IDLE internal
|
||||
modules from the traceback displayed.
|
||||
|
||||
- Class Browser and Path Browser now use Alt-Key-2 for vertical zoom.
|
||||
|
||||
- IDLE icons will now install correctly even when setup.py is run from the
|
||||
build directory
|
||||
|
||||
- Class Browser now compatible with Python2.3 version of pyclbr.py
|
||||
|
||||
- Left cursor move in presence of selected text now moves from left end
|
||||
of the selection.
|
||||
|
||||
- Add Meta keybindings to "IDLE Classic Windows" to handle reversed
|
||||
Alt/Meta on some Linux distros.
|
||||
|
||||
- Change default: IDLE now starts with Python Shell.
|
||||
|
||||
- Removed the File Path from the Additional Help Sources scrolled list.
|
||||
|
||||
- Add capability to access Additional Help Sources on the web if the
|
||||
Help File Path begins with //http or www. (Otherwise local path is
|
||||
validated, as before.)
|
||||
|
||||
- Additional Help Sources were not being posted on the Help menu in the
|
||||
order entered. Implement sorting the list by [HelpFiles] 'option'
|
||||
number.
|
||||
|
||||
- Add Browse button to New Help Source dialog. Arrange to start in
|
||||
Python/Doc if platform is Windows, otherwise start in current directory.
|
||||
|
||||
- Put the Additional Help Sources directly on the Help menu instead of in
|
||||
an Extra Help cascade menu. Rearrange the Help menu so the Additional
|
||||
Help Sources come last. Update help.txt appropriately.
|
||||
|
||||
- Fix Tk root pop-ups in configSectionNameDialog.py and configDialog.py
|
||||
|
||||
- Uniform capitalization in General tab of ConfigDialog, update the doc string.
|
||||
|
||||
- Fix bug in ConfigDialog where SaveAllChangedConfig() was unexpectedly
|
||||
deleting Additional Help Sources from the user's config file.
|
||||
|
||||
- Make configHelpSourceEdit OK button the default and bind <Return>
|
||||
|
||||
- Fix Tk root pop-ups in configHelpSourceEdit: error dialogs not attached
|
||||
to parents.
|
||||
|
||||
- Use os.startfile() to open both Additional Help and Python Help on the
|
||||
Windows platform. The application associated with the file type will act as
|
||||
the viewer. Windows help files (.chm) are now supported via the
|
||||
Settings/General/Additional Help facility.
|
||||
|
||||
- If Python Help files are installed locally on Linux, use them instead of
|
||||
accessing python.org.
|
||||
|
||||
- Make the methods for finding the Python help docs more robust, and make
|
||||
them work in the installed configuration, also.
|
||||
|
||||
- On the Save Before Run dialog, make the OK button the default. One
|
||||
less mouse action!
|
||||
|
||||
- Add a method: EditorWindow.get_geometry() for future use in implementing
|
||||
window location persistence.
|
||||
|
||||
- Removed the "Help/Advice" menu entry. Thanks, David! We'll remember!
|
||||
|
||||
- Change the "Classic Windows" theme's paste key to be <ctrl-v>.
|
||||
|
||||
- Rearrange the Shell menu to put Stack Viewer entries adjacent.
|
||||
|
||||
- Add the ability to restart the subprocess interpreter from the shell window;
|
||||
add an associated menu entry "Shell/Restart" with binding Control-F6. Update
|
||||
IDLE help.
|
||||
|
||||
- Upon a restart, annotate the shell window with a "restart boundary". Add a
|
||||
shell window menu "Shell/View Restart" with binding F6 to jump to the most
|
||||
recent restart boundary.
|
||||
|
||||
- Add Shell menu to Python Shell; change "Settings" to "Options".
|
||||
|
||||
- Remove incorrect comment in setup.py: IDLEfork is now installed as a package.
|
||||
|
||||
- Add INSTALL.txt, HISTORY.txt, NEWS.txt to installed configuration.
|
||||
|
||||
- In installer text, fix reference to Visual Python, should be VPython.
|
||||
Properly credit David Scherer.
|
||||
|
||||
- Modified idle, idle.py, idle.pyw to improve exception handling.
|
||||
|
||||
|
||||
What's New in IDLEfork 0.9 Alpha 1?
|
||||
===================================
|
||||
|
||||
*Release date: 31-Dec-2002*
|
||||
|
||||
- First release of major new functionality. For further details refer to
|
||||
Idle-dev and/or the Sourceforge CVS.
|
||||
|
||||
- Adapted to the Mac platform.
|
||||
|
||||
- Overhauled the IDLE startup options and revised the idle -h help message,
|
||||
which provides details of command line usage.
|
||||
|
||||
- Multiple bug fixes and usability enhancements.
|
||||
|
||||
- Introduced the new RPC implementation, which includes a debugger. The output
|
||||
of user code is to the shell, and the shell may be used to inspect the
|
||||
environment after the run has finished. (In version 0.8.1 the shell
|
||||
environment was separate from the environment of the user code.)
|
||||
|
||||
- Introduced the configuration GUI and a new About dialog.
|
||||
|
||||
- Removed David Scherer's Remote Procedure Call code and replaced with Guido
|
||||
van Rossum's. GvR code has support for the IDLE debugger and uses the shell
|
||||
to inspect the environment of code Run from an Edit window. Files removed:
|
||||
ExecBinding.py, loader.py, protocol.py, Remote.py, spawn.py
|
||||
|
||||
--------------------------------------------------------------------
|
||||
Refer to HISTORY.txt for additional information on earlier releases.
|
||||
--------------------------------------------------------------------
|
||||
143
Darwin/lib/python3.5/idlelib/ObjectBrowser.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# XXX TO DO:
|
||||
# - popup menu
|
||||
# - support partial or total redisplay
|
||||
# - more doc strings
|
||||
# - tooltips
|
||||
|
||||
# object browser
|
||||
|
||||
# XXX TO DO:
|
||||
# - for classes/modules, add "open source" to object browser
|
||||
|
||||
import re
|
||||
|
||||
from idlelib.TreeWidget import TreeItem, TreeNode, ScrolledCanvas
|
||||
|
||||
from reprlib import Repr
|
||||
|
||||
myrepr = Repr()
|
||||
myrepr.maxstring = 100
|
||||
myrepr.maxother = 100
|
||||
|
||||
class ObjectTreeItem(TreeItem):
|
||||
def __init__(self, labeltext, object, setfunction=None):
|
||||
self.labeltext = labeltext
|
||||
self.object = object
|
||||
self.setfunction = setfunction
|
||||
def GetLabelText(self):
|
||||
return self.labeltext
|
||||
def GetText(self):
|
||||
return myrepr.repr(self.object)
|
||||
def GetIconName(self):
|
||||
if not self.IsExpandable():
|
||||
return "python"
|
||||
def IsEditable(self):
|
||||
return self.setfunction is not None
|
||||
def SetText(self, text):
|
||||
try:
|
||||
value = eval(text)
|
||||
self.setfunction(value)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
self.object = value
|
||||
def IsExpandable(self):
|
||||
return not not dir(self.object)
|
||||
def GetSubList(self):
|
||||
keys = dir(self.object)
|
||||
sublist = []
|
||||
for key in keys:
|
||||
try:
|
||||
value = getattr(self.object, key)
|
||||
except AttributeError:
|
||||
continue
|
||||
item = make_objecttreeitem(
|
||||
str(key) + " =",
|
||||
value,
|
||||
lambda value, key=key, object=self.object:
|
||||
setattr(object, key, value))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class ClassTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return True
|
||||
def GetSubList(self):
|
||||
sublist = ObjectTreeItem.GetSubList(self)
|
||||
if len(self.object.__bases__) == 1:
|
||||
item = make_objecttreeitem("__bases__[0] =",
|
||||
self.object.__bases__[0])
|
||||
else:
|
||||
item = make_objecttreeitem("__bases__ =", self.object.__bases__)
|
||||
sublist.insert(0, item)
|
||||
return sublist
|
||||
|
||||
class AtomicObjectTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return 0
|
||||
|
||||
class SequenceTreeItem(ObjectTreeItem):
|
||||
def IsExpandable(self):
|
||||
return len(self.object) > 0
|
||||
def keys(self):
|
||||
return range(len(self.object))
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for key in self.keys():
|
||||
try:
|
||||
value = self.object[key]
|
||||
except KeyError:
|
||||
continue
|
||||
def setfunction(value, key=key, object=self.object):
|
||||
object[key] = value
|
||||
item = make_objecttreeitem("%r:" % (key,), value, setfunction)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class DictTreeItem(SequenceTreeItem):
|
||||
def keys(self):
|
||||
keys = list(self.object.keys())
|
||||
try:
|
||||
keys.sort()
|
||||
except:
|
||||
pass
|
||||
return keys
|
||||
|
||||
dispatch = {
|
||||
int: AtomicObjectTreeItem,
|
||||
float: AtomicObjectTreeItem,
|
||||
str: AtomicObjectTreeItem,
|
||||
tuple: SequenceTreeItem,
|
||||
list: SequenceTreeItem,
|
||||
dict: DictTreeItem,
|
||||
type: ClassTreeItem,
|
||||
}
|
||||
|
||||
def make_objecttreeitem(labeltext, object, setfunction=None):
|
||||
t = type(object)
|
||||
if t in dispatch:
|
||||
c = dispatch[t]
|
||||
else:
|
||||
c = ObjectTreeItem
|
||||
return c(labeltext, object, setfunction)
|
||||
|
||||
|
||||
def _object_browser(parent):
|
||||
import sys
|
||||
from tkinter import Tk
|
||||
root = Tk()
|
||||
root.title("Test ObjectBrowser")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
root.configure(bd=0, bg="yellow")
|
||||
root.focus_set()
|
||||
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = make_objecttreeitem("sys", sys)
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.update()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_object_browser)
|
||||
144
Darwin/lib/python3.5/idlelib/OutputWindow.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
from tkinter import *
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
import re
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
from idlelib import IOBinding
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
def short_title(self):
|
||||
return "Output"
|
||||
|
||||
def maybesave(self):
|
||||
# Override base class method -- don't ask any questions
|
||||
if self.get_saved():
|
||||
return "yes"
|
||||
else:
|
||||
return "no"
|
||||
|
||||
# Act as output file
|
||||
|
||||
def write(self, s, tags=(), mark="insert"):
|
||||
if isinstance(s, (bytes, bytes)):
|
||||
s = s.decode(IOBinding.encoding, "replace")
|
||||
self.text.insert(mark, s, tags)
|
||||
self.text.see(mark)
|
||||
self.text.update()
|
||||
return len(s)
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def flush(self):
|
||||
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),
|
||||
]
|
||||
|
||||
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 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))
|
||||
line = self.text.get("insert linestart", "insert lineend")
|
||||
result = self._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)
|
||||
if not result:
|
||||
tkMessageBox.showerror(
|
||||
"No special line",
|
||||
"The line you point at doesn't look like "
|
||||
"a valid file name followed by a line number.",
|
||||
master=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
|
||||
|
||||
# These classes are currently not used but might come in handy
|
||||
|
||||
class OnDemandOutputWindow:
|
||||
|
||||
tagdefs = {
|
||||
# XXX Should use IdlePrefs.ColorPrefs
|
||||
"stdout": {"foreground": "blue"},
|
||||
"stderr": {"foreground": "#007700"},
|
||||
}
|
||||
|
||||
def __init__(self, flist):
|
||||
self.flist = flist
|
||||
self.owin = None
|
||||
|
||||
def write(self, s, tags, mark):
|
||||
if not self.owin:
|
||||
self.setup()
|
||||
self.owin.write(s, tags, mark)
|
||||
|
||||
def setup(self):
|
||||
self.owin = owin = OutputWindow(self.flist)
|
||||
text = owin.text
|
||||
for tag, cnf in self.tagdefs.items():
|
||||
if cnf:
|
||||
text.tag_configure(tag, **cnf)
|
||||
text.tag_raise('sel')
|
||||
self.write = self.owin.write
|
||||
178
Darwin/lib/python3.5/idlelib/ParenMatch.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
"""ParenMatch -- An IDLE extension for parenthesis matching.
|
||||
|
||||
When you hit a right paren, the cursor should move briefly to the left
|
||||
paren. Paren here is used generically; the matching applies to
|
||||
parentheses, square brackets, and curly braces.
|
||||
"""
|
||||
|
||||
from idlelib.HyperParser import HyperParser
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
_openers = {')':'(',']':'[','}':'{'}
|
||||
CHECK_DELAY = 100 # miliseconds
|
||||
|
||||
class ParenMatch:
|
||||
"""Highlight matching parentheses
|
||||
|
||||
There are three supported style of paren matching, based loosely
|
||||
on the Emacs options. The style is select based on the
|
||||
HILITE_STYLE attribute; it can be changed used the set_style
|
||||
method.
|
||||
|
||||
The supported styles are:
|
||||
|
||||
default -- When a right paren is typed, highlight the matching
|
||||
left paren for 1/2 sec.
|
||||
|
||||
expression -- When a right paren is typed, highlight the entire
|
||||
expression from the left paren to the right paren.
|
||||
|
||||
TODO:
|
||||
- extend IDLE with configuration dialog to change options
|
||||
- implement rest of Emacs highlight styles (see below)
|
||||
- print mismatch warning in IDLE status window
|
||||
|
||||
Note: In Emacs, there are several styles of highlight where the
|
||||
matching paren is highlighted whenever the cursor is immediately
|
||||
to the right of a right paren. I don't know how to do that in Tk,
|
||||
so I haven't bothered.
|
||||
"""
|
||||
menudefs = [
|
||||
('edit', [
|
||||
("Show surrounding parens", "<<flash-paren>>"),
|
||||
])
|
||||
]
|
||||
STYLE = idleConf.GetOption('extensions','ParenMatch','style',
|
||||
default='expression')
|
||||
FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
|
||||
type='int',default=500)
|
||||
HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
|
||||
BELL = idleConf.GetOption('extensions','ParenMatch','bell',
|
||||
type='bool',default=1)
|
||||
|
||||
RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
|
||||
# We want the restore event be called before the usual return and
|
||||
# backspace events.
|
||||
RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
|
||||
"<Key-Return>", "<Key-BackSpace>")
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
# Bind the check-restore event to the function restore_event,
|
||||
# so that we can then use activate_restore (which calls event_add)
|
||||
# and deactivate_restore (which calls event_delete).
|
||||
editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
|
||||
self.restore_event)
|
||||
self.counter = 0
|
||||
self.is_restore_active = 0
|
||||
self.set_style(self.STYLE)
|
||||
|
||||
def activate_restore(self):
|
||||
if not self.is_restore_active:
|
||||
for seq in self.RESTORE_SEQUENCES:
|
||||
self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.is_restore_active = True
|
||||
|
||||
def deactivate_restore(self):
|
||||
if self.is_restore_active:
|
||||
for seq in self.RESTORE_SEQUENCES:
|
||||
self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.is_restore_active = False
|
||||
|
||||
def set_style(self, style):
|
||||
self.STYLE = style
|
||||
if style == "default":
|
||||
self.create_tag = self.create_tag_default
|
||||
self.set_timeout = self.set_timeout_last
|
||||
elif style == "expression":
|
||||
self.create_tag = self.create_tag_expression
|
||||
self.set_timeout = self.set_timeout_none
|
||||
|
||||
def flash_paren_event(self, event):
|
||||
indices = (HyperParser(self.editwin, "insert")
|
||||
.get_surrounding_brackets())
|
||||
if indices is None:
|
||||
self.warn_mismatched()
|
||||
return
|
||||
self.activate_restore()
|
||||
self.create_tag(indices)
|
||||
self.set_timeout_last()
|
||||
|
||||
def paren_closed_event(self, event):
|
||||
# If it was a shortcut and not really a closing paren, quit.
|
||||
closer = self.text.get("insert-1c")
|
||||
if closer not in _openers:
|
||||
return
|
||||
hp = HyperParser(self.editwin, "insert-1c")
|
||||
if not hp.is_in_code():
|
||||
return
|
||||
indices = hp.get_surrounding_brackets(_openers[closer], True)
|
||||
if indices is None:
|
||||
self.warn_mismatched()
|
||||
return
|
||||
self.activate_restore()
|
||||
self.create_tag(indices)
|
||||
self.set_timeout()
|
||||
|
||||
def restore_event(self, event=None):
|
||||
self.text.tag_delete("paren")
|
||||
self.deactivate_restore()
|
||||
self.counter += 1 # disable the last timer, if there is one.
|
||||
|
||||
def handle_restore_timer(self, timer_count):
|
||||
if timer_count == self.counter:
|
||||
self.restore_event()
|
||||
|
||||
def warn_mismatched(self):
|
||||
if self.BELL:
|
||||
self.text.bell()
|
||||
|
||||
# any one of the create_tag_XXX methods can be used depending on
|
||||
# the style
|
||||
|
||||
def create_tag_default(self, indices):
|
||||
"""Highlight the single paren that matches"""
|
||||
self.text.tag_add("paren", indices[0])
|
||||
self.text.tag_config("paren", self.HILITE_CONFIG)
|
||||
|
||||
def create_tag_expression(self, indices):
|
||||
"""Highlight the entire expression"""
|
||||
if self.text.get(indices[1]) in (')', ']', '}'):
|
||||
rightindex = indices[1]+"+1c"
|
||||
else:
|
||||
rightindex = indices[1]
|
||||
self.text.tag_add("paren", indices[0], rightindex)
|
||||
self.text.tag_config("paren", self.HILITE_CONFIG)
|
||||
|
||||
# any one of the set_timeout_XXX methods can be used depending on
|
||||
# the style
|
||||
|
||||
def set_timeout_none(self):
|
||||
"""Highlight will remain until user input turns it off
|
||||
or the insert has moved"""
|
||||
# After CHECK_DELAY, call a function which disables the "paren" tag
|
||||
# if the event is for the most recent timer and the insert has changed,
|
||||
# or schedules another call for itself.
|
||||
self.counter += 1
|
||||
def callme(callme, self=self, c=self.counter,
|
||||
index=self.text.index("insert")):
|
||||
if index != self.text.index("insert"):
|
||||
self.handle_restore_timer(c)
|
||||
else:
|
||||
self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
|
||||
self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
|
||||
|
||||
def set_timeout_last(self):
|
||||
"""The last highlight created will be removed after .5 sec"""
|
||||
# associate a counter with an event; only disable the "paren"
|
||||
# tag if the event is for the most recent timer.
|
||||
self.counter += 1
|
||||
self.editwin.text_frame.after(
|
||||
self.FLASH_DELAY,
|
||||
lambda self=self, c=self.counter: self.handle_restore_timer(c))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)
|
||||
108
Darwin/lib/python3.5/idlelib/PathBrowser.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import os
|
||||
import sys
|
||||
import importlib.machinery
|
||||
|
||||
from idlelib.TreeWidget import TreeItem
|
||||
from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
|
||||
from idlelib.PyShell import PyShellFileList
|
||||
|
||||
|
||||
class PathBrowser(ClassBrowser):
|
||||
|
||||
def __init__(self, flist, _htest=False):
|
||||
"""
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
self._htest = _htest
|
||||
self.init(flist)
|
||||
|
||||
def settitle(self):
|
||||
"Set window titles."
|
||||
self.top.wm_title("Path Browser")
|
||||
self.top.wm_iconname("Path Browser")
|
||||
|
||||
def rootnode(self):
|
||||
return PathBrowserTreeItem()
|
||||
|
||||
class PathBrowserTreeItem(TreeItem):
|
||||
|
||||
def GetText(self):
|
||||
return "sys.path"
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for dir in sys.path:
|
||||
item = DirBrowserTreeItem(dir)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class DirBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, dir, packages=[]):
|
||||
self.dir = dir
|
||||
self.packages = packages
|
||||
|
||||
def GetText(self):
|
||||
if not self.packages:
|
||||
return self.dir
|
||||
else:
|
||||
return self.packages[-1] + ": package"
|
||||
|
||||
def GetSubList(self):
|
||||
try:
|
||||
names = os.listdir(self.dir or os.curdir)
|
||||
except OSError:
|
||||
return []
|
||||
packages = []
|
||||
for name in names:
|
||||
file = os.path.join(self.dir, name)
|
||||
if self.ispackagedir(file):
|
||||
nn = os.path.normcase(name)
|
||||
packages.append((nn, name, file))
|
||||
packages.sort()
|
||||
sublist = []
|
||||
for nn, name, file in packages:
|
||||
item = DirBrowserTreeItem(file, self.packages + [name])
|
||||
sublist.append(item)
|
||||
for nn, name in self.listmodules(names):
|
||||
item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def ispackagedir(self, file):
|
||||
" Return true for directories that are packages."
|
||||
if not os.path.isdir(file):
|
||||
return False
|
||||
init = os.path.join(file, "__init__.py")
|
||||
return os.path.exists(init)
|
||||
|
||||
def listmodules(self, allnames):
|
||||
modules = {}
|
||||
suffixes = importlib.machinery.EXTENSION_SUFFIXES[:]
|
||||
suffixes += importlib.machinery.SOURCE_SUFFIXES
|
||||
suffixes += importlib.machinery.BYTECODE_SUFFIXES
|
||||
sorted = []
|
||||
for suff in suffixes:
|
||||
i = -len(suff)
|
||||
for name in allnames[:]:
|
||||
normed_name = os.path.normcase(name)
|
||||
if normed_name[i:] == suff:
|
||||
mod_name = name[:i]
|
||||
if mod_name not in modules:
|
||||
modules[mod_name] = None
|
||||
sorted.append((normed_name, name))
|
||||
allnames.remove(name)
|
||||
sorted.sort()
|
||||
return sorted
|
||||
|
||||
def _path_browser(parent): # htest #
|
||||
flist = PyShellFileList(parent)
|
||||
PathBrowser(flist, _htest=True)
|
||||
parent.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from unittest import main
|
||||
main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False)
|
||||
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_path_browser)
|
||||
104
Darwin/lib/python3.5/idlelib/Percolator.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
from idlelib.WidgetRedirector import WidgetRedirector
|
||||
from idlelib.Delegator import Delegator
|
||||
|
||||
class Percolator:
|
||||
|
||||
def __init__(self, text):
|
||||
# XXX would be nice to inherit from Delegator
|
||||
self.text = text
|
||||
self.redir = WidgetRedirector(text)
|
||||
self.top = self.bottom = Delegator(text)
|
||||
self.bottom.insert = self.redir.register("insert", self.insert)
|
||||
self.bottom.delete = self.redir.register("delete", self.delete)
|
||||
self.filters = []
|
||||
|
||||
def close(self):
|
||||
while self.top is not self.bottom:
|
||||
self.removefilter(self.top)
|
||||
self.top = None
|
||||
self.bottom.setdelegate(None); self.bottom = None
|
||||
self.redir.close(); self.redir = None
|
||||
self.text = None
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
# Could go away if inheriting from Delegator
|
||||
self.top.insert(index, chars, tags)
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
# Could go away if inheriting from Delegator
|
||||
self.top.delete(index1, index2)
|
||||
|
||||
def insertfilter(self, filter):
|
||||
# Perhaps rename to pushfilter()?
|
||||
assert isinstance(filter, Delegator)
|
||||
assert filter.delegate is None
|
||||
filter.setdelegate(self.top)
|
||||
self.top = filter
|
||||
|
||||
def removefilter(self, filter):
|
||||
# XXX Perhaps should only support popfilter()?
|
||||
assert isinstance(filter, Delegator)
|
||||
assert filter.delegate is not None
|
||||
f = self.top
|
||||
if f is filter:
|
||||
self.top = filter.delegate
|
||||
filter.setdelegate(None)
|
||||
else:
|
||||
while f.delegate is not filter:
|
||||
assert f is not self.bottom
|
||||
f.resetcache()
|
||||
f = f.delegate
|
||||
f.setdelegate(filter.delegate)
|
||||
filter.setdelegate(None)
|
||||
|
||||
def _percolator(parent):
|
||||
import tkinter as tk
|
||||
import re
|
||||
class Tracer(Delegator):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
Delegator.__init__(self, None)
|
||||
def insert(self, *args):
|
||||
print(self.name, ": insert", args)
|
||||
self.delegate.insert(*args)
|
||||
def delete(self, *args):
|
||||
print(self.name, ": delete", args)
|
||||
self.delegate.delete(*args)
|
||||
root = tk.Tk()
|
||||
root.title("Test Percolator")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
text = tk.Text(root)
|
||||
p = Percolator(text)
|
||||
t1 = Tracer("t1")
|
||||
t2 = Tracer("t2")
|
||||
|
||||
def toggle1():
|
||||
if var1.get() == 0:
|
||||
var1.set(1)
|
||||
p.insertfilter(t1)
|
||||
elif var1.get() == 1:
|
||||
var1.set(0)
|
||||
p.removefilter(t1)
|
||||
|
||||
def toggle2():
|
||||
if var2.get() == 0:
|
||||
var2.set(1)
|
||||
p.insertfilter(t2)
|
||||
elif var2.get() == 1:
|
||||
var2.set(0)
|
||||
p.removefilter(t2)
|
||||
|
||||
text.pack()
|
||||
var1 = tk.IntVar()
|
||||
cb1 = tk.Checkbutton(root, text="Tracer1", command=toggle1, variable=var1)
|
||||
cb1.pack()
|
||||
var2 = tk.IntVar()
|
||||
cb2 = tk.Checkbutton(root, text="Tracer2", command=toggle2, variable=var2)
|
||||
cb2.pack()
|
||||
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_percolator)
|
||||
617
Darwin/lib/python3.5/idlelib/PyParse.py
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
import re
|
||||
import sys
|
||||
from collections import Mapping
|
||||
|
||||
# Reason last stmt is continued (or C_NONE if it's not).
|
||||
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
|
||||
C_STRING_NEXT_LINES, C_BRACKET) = range(5)
|
||||
|
||||
if 0: # for throwaway debugging output
|
||||
def dump(*stuff):
|
||||
sys.__stdout__.write(" ".join(map(str, stuff)) + "\n")
|
||||
|
||||
# Find what looks like the start of a popular stmt.
|
||||
|
||||
_synchre = re.compile(r"""
|
||||
^
|
||||
[ \t]*
|
||||
(?: while
|
||||
| else
|
||||
| def
|
||||
| return
|
||||
| assert
|
||||
| break
|
||||
| class
|
||||
| continue
|
||||
| elif
|
||||
| try
|
||||
| except
|
||||
| raise
|
||||
| import
|
||||
| yield
|
||||
)
|
||||
\b
|
||||
""", re.VERBOSE | re.MULTILINE).search
|
||||
|
||||
# Match blank line or non-indenting comment line.
|
||||
|
||||
_junkre = re.compile(r"""
|
||||
[ \t]*
|
||||
(?: \# \S .* )?
|
||||
\n
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Match any flavor of string; the terminating quote is optional
|
||||
# so that we're robust in the face of incomplete program text.
|
||||
|
||||
_match_stringre = re.compile(r"""
|
||||
\""" [^"\\]* (?:
|
||||
(?: \\. | "(?!"") )
|
||||
[^"\\]*
|
||||
)*
|
||||
(?: \""" )?
|
||||
|
||||
| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
|
||||
|
||||
| ''' [^'\\]* (?:
|
||||
(?: \\. | '(?!'') )
|
||||
[^'\\]*
|
||||
)*
|
||||
(?: ''' )?
|
||||
|
||||
| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
|
||||
""", re.VERBOSE | re.DOTALL).match
|
||||
|
||||
# Match a line that starts with something interesting;
|
||||
# used to find the first item of a bracket structure.
|
||||
|
||||
_itemre = re.compile(r"""
|
||||
[ \t]*
|
||||
[^\s#\\] # if we match, m.end()-1 is the interesting char
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Match start of stmts that should be followed by a dedent.
|
||||
|
||||
_closere = re.compile(r"""
|
||||
\s*
|
||||
(?: return
|
||||
| break
|
||||
| continue
|
||||
| raise
|
||||
| pass
|
||||
)
|
||||
\b
|
||||
""", re.VERBOSE).match
|
||||
|
||||
# Chew up non-special chars as quickly as possible. If match is
|
||||
# successful, m.end() less 1 is the index of the last boring char
|
||||
# matched. If match is unsuccessful, the string starts with an
|
||||
# interesting char.
|
||||
|
||||
_chew_ordinaryre = re.compile(r"""
|
||||
[^[\](){}#'"\\]+
|
||||
""", re.VERBOSE).match
|
||||
|
||||
|
||||
class StringTranslatePseudoMapping(Mapping):
|
||||
r"""Utility class to be used with str.translate()
|
||||
|
||||
This Mapping class wraps a given dict. When a value for a key is
|
||||
requested via __getitem__() or get(), the key is looked up in the
|
||||
given dict. If found there, the value from the dict is returned.
|
||||
Otherwise, the default value given upon initialization is returned.
|
||||
|
||||
This allows using str.translate() to make some replacements, and to
|
||||
replace all characters for which no replacement was specified with
|
||||
a given character instead of leaving them as-is.
|
||||
|
||||
For example, to replace everything except whitespace with 'x':
|
||||
|
||||
>>> whitespace_chars = ' \t\n\r'
|
||||
>>> preserve_dict = {ord(c): ord(c) for c in whitespace_chars}
|
||||
>>> mapping = StringTranslatePseudoMapping(preserve_dict, ord('x'))
|
||||
>>> text = "a + b\tc\nd"
|
||||
>>> text.translate(mapping)
|
||||
'x x x\tx\nx'
|
||||
"""
|
||||
def __init__(self, non_defaults, default_value):
|
||||
self._non_defaults = non_defaults
|
||||
self._default_value = default_value
|
||||
|
||||
def _get(key, _get=non_defaults.get, _default=default_value):
|
||||
return _get(key, _default)
|
||||
self._get = _get
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self._get(item)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._non_defaults)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._non_defaults)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._get(key)
|
||||
|
||||
|
||||
class Parser:
|
||||
|
||||
def __init__(self, indentwidth, tabwidth):
|
||||
self.indentwidth = indentwidth
|
||||
self.tabwidth = tabwidth
|
||||
|
||||
def set_str(self, s):
|
||||
assert len(s) == 0 or s[-1] == '\n'
|
||||
self.str = s
|
||||
self.study_level = 0
|
||||
|
||||
# Return index of a good place to begin parsing, as close to the
|
||||
# end of the string as possible. This will be the start of some
|
||||
# popular stmt like "if" or "def". Return None if none found:
|
||||
# the caller should pass more prior context then, if possible, or
|
||||
# if not (the entire program text up until the point of interest
|
||||
# has already been tried) pass 0 to set_lo.
|
||||
#
|
||||
# This will be reliable iff given a reliable is_char_in_string
|
||||
# function, meaning that when it says "no", it's absolutely
|
||||
# guaranteed that the char is not in a string.
|
||||
|
||||
def find_good_parse_start(self, is_char_in_string=None,
|
||||
_synchre=_synchre):
|
||||
str, pos = self.str, None
|
||||
|
||||
if not is_char_in_string:
|
||||
# no clue -- make the caller pass everything
|
||||
return None
|
||||
|
||||
# Peek back from the end for a good place to start,
|
||||
# but don't try too often; pos will be left None, or
|
||||
# bumped to a legitimate synch point.
|
||||
limit = len(str)
|
||||
for tries in range(5):
|
||||
i = str.rfind(":\n", 0, limit)
|
||||
if i < 0:
|
||||
break
|
||||
i = str.rfind('\n', 0, i) + 1 # start of colon line
|
||||
m = _synchre(str, i, limit)
|
||||
if m and not is_char_in_string(m.start()):
|
||||
pos = m.start()
|
||||
break
|
||||
limit = i
|
||||
if pos is None:
|
||||
# Nothing looks like a block-opener, or stuff does
|
||||
# but is_char_in_string keeps returning true; most likely
|
||||
# we're in or near a giant string, the colorizer hasn't
|
||||
# caught up enough to be helpful, or there simply *aren't*
|
||||
# any interesting stmts. In any of these cases we're
|
||||
# going to have to parse the whole thing to be sure, so
|
||||
# give it one last try from the start, but stop wasting
|
||||
# time here regardless of the outcome.
|
||||
m = _synchre(str)
|
||||
if m and not is_char_in_string(m.start()):
|
||||
pos = m.start()
|
||||
return pos
|
||||
|
||||
# Peeking back worked; look forward until _synchre no longer
|
||||
# matches.
|
||||
i = pos + 1
|
||||
while 1:
|
||||
m = _synchre(str, i)
|
||||
if m:
|
||||
s, i = m.span()
|
||||
if not is_char_in_string(s):
|
||||
pos = s
|
||||
else:
|
||||
break
|
||||
return pos
|
||||
|
||||
# Throw away the start of the string. Intended to be called with
|
||||
# find_good_parse_start's result.
|
||||
|
||||
def set_lo(self, lo):
|
||||
assert lo == 0 or self.str[lo-1] == '\n'
|
||||
if lo > 0:
|
||||
self.str = self.str[lo:]
|
||||
|
||||
# Build a translation table to map uninteresting chars to 'x', open
|
||||
# brackets to '(', close brackets to ')' while preserving quotes,
|
||||
# backslashes, newlines and hashes. This is to be passed to
|
||||
# str.translate() in _study1().
|
||||
_tran = {}
|
||||
_tran.update((ord(c), ord('(')) for c in "({[")
|
||||
_tran.update((ord(c), ord(')')) for c in ")}]")
|
||||
_tran.update((ord(c), ord(c)) for c in "\"'\\\n#")
|
||||
_tran = StringTranslatePseudoMapping(_tran, default_value=ord('x'))
|
||||
|
||||
# As quickly as humanly possible <wink>, find the line numbers (0-
|
||||
# based) of the non-continuation lines.
|
||||
# Creates self.{goodlines, continuation}.
|
||||
|
||||
def _study1(self):
|
||||
if self.study_level >= 1:
|
||||
return
|
||||
self.study_level = 1
|
||||
|
||||
# Map all uninteresting characters to "x", all open brackets
|
||||
# to "(", all close brackets to ")", then collapse runs of
|
||||
# uninteresting characters. This can cut the number of chars
|
||||
# by a factor of 10-40, and so greatly speed the following loop.
|
||||
str = self.str
|
||||
str = str.translate(self._tran)
|
||||
str = str.replace('xxxxxxxx', 'x')
|
||||
str = str.replace('xxxx', 'x')
|
||||
str = str.replace('xx', 'x')
|
||||
str = str.replace('xx', 'x')
|
||||
str = str.replace('\nx', '\n')
|
||||
# note that replacing x\n with \n would be incorrect, because
|
||||
# x may be preceded by a backslash
|
||||
|
||||
# March over the squashed version of the program, accumulating
|
||||
# the line numbers of non-continued stmts, and determining
|
||||
# whether & why the last stmt is a continuation.
|
||||
continuation = C_NONE
|
||||
level = lno = 0 # level is nesting level; lno is line number
|
||||
self.goodlines = goodlines = [0]
|
||||
push_good = goodlines.append
|
||||
i, n = 0, len(str)
|
||||
while i < n:
|
||||
ch = str[i]
|
||||
i = i+1
|
||||
|
||||
# cases are checked in decreasing order of frequency
|
||||
if ch == 'x':
|
||||
continue
|
||||
|
||||
if ch == '\n':
|
||||
lno = lno + 1
|
||||
if level == 0:
|
||||
push_good(lno)
|
||||
# else we're in an unclosed bracket structure
|
||||
continue
|
||||
|
||||
if ch == '(':
|
||||
level = level + 1
|
||||
continue
|
||||
|
||||
if ch == ')':
|
||||
if level:
|
||||
level = level - 1
|
||||
# else the program is invalid, but we can't complain
|
||||
continue
|
||||
|
||||
if ch == '"' or ch == "'":
|
||||
# consume the string
|
||||
quote = ch
|
||||
if str[i-1:i+2] == quote * 3:
|
||||
quote = quote * 3
|
||||
firstlno = lno
|
||||
w = len(quote) - 1
|
||||
i = i+w
|
||||
while i < n:
|
||||
ch = str[i]
|
||||
i = i+1
|
||||
|
||||
if ch == 'x':
|
||||
continue
|
||||
|
||||
if str[i-1:i+w] == quote:
|
||||
i = i+w
|
||||
break
|
||||
|
||||
if ch == '\n':
|
||||
lno = lno + 1
|
||||
if w == 0:
|
||||
# unterminated single-quoted string
|
||||
if level == 0:
|
||||
push_good(lno)
|
||||
break
|
||||
continue
|
||||
|
||||
if ch == '\\':
|
||||
assert i < n
|
||||
if str[i] == '\n':
|
||||
lno = lno + 1
|
||||
i = i+1
|
||||
continue
|
||||
|
||||
# else comment char or paren inside string
|
||||
|
||||
else:
|
||||
# didn't break out of the loop, so we're still
|
||||
# inside a string
|
||||
if (lno - 1) == firstlno:
|
||||
# before the previous \n in str, we were in the first
|
||||
# line of the string
|
||||
continuation = C_STRING_FIRST_LINE
|
||||
else:
|
||||
continuation = C_STRING_NEXT_LINES
|
||||
continue # with outer loop
|
||||
|
||||
if ch == '#':
|
||||
# consume the comment
|
||||
i = str.find('\n', i)
|
||||
assert i >= 0
|
||||
continue
|
||||
|
||||
assert ch == '\\'
|
||||
assert i < n
|
||||
if str[i] == '\n':
|
||||
lno = lno + 1
|
||||
if i+1 == n:
|
||||
continuation = C_BACKSLASH
|
||||
i = i+1
|
||||
|
||||
# The last stmt may be continued for all 3 reasons.
|
||||
# String continuation takes precedence over bracket
|
||||
# continuation, which beats backslash continuation.
|
||||
if (continuation != C_STRING_FIRST_LINE
|
||||
and continuation != C_STRING_NEXT_LINES and level > 0):
|
||||
continuation = C_BRACKET
|
||||
self.continuation = continuation
|
||||
|
||||
# Push the final line number as a sentinel value, regardless of
|
||||
# whether it's continued.
|
||||
assert (continuation == C_NONE) == (goodlines[-1] == lno)
|
||||
if goodlines[-1] != lno:
|
||||
push_good(lno)
|
||||
|
||||
def get_continuation_type(self):
|
||||
self._study1()
|
||||
return self.continuation
|
||||
|
||||
# study1 was sufficient to determine the continuation status,
|
||||
# but doing more requires looking at every character. study2
|
||||
# does this for the last interesting statement in the block.
|
||||
# Creates:
|
||||
# self.stmt_start, stmt_end
|
||||
# slice indices of last interesting stmt
|
||||
# self.stmt_bracketing
|
||||
# the bracketing structure of the last interesting stmt;
|
||||
# for example, for the statement "say(boo) or die", stmt_bracketing
|
||||
# will be [(0, 0), (3, 1), (8, 0)]. Strings and comments are
|
||||
# treated as brackets, for the matter.
|
||||
# self.lastch
|
||||
# last non-whitespace character before optional trailing
|
||||
# comment
|
||||
# self.lastopenbracketpos
|
||||
# if continuation is C_BRACKET, index of last open bracket
|
||||
|
||||
def _study2(self):
|
||||
if self.study_level >= 2:
|
||||
return
|
||||
self._study1()
|
||||
self.study_level = 2
|
||||
|
||||
# Set p and q to slice indices of last interesting stmt.
|
||||
str, goodlines = self.str, self.goodlines
|
||||
i = len(goodlines) - 1
|
||||
p = len(str) # index of newest line
|
||||
while i:
|
||||
assert p
|
||||
# p is the index of the stmt at line number goodlines[i].
|
||||
# Move p back to the stmt at line number goodlines[i-1].
|
||||
q = p
|
||||
for nothing in range(goodlines[i-1], goodlines[i]):
|
||||
# tricky: sets p to 0 if no preceding newline
|
||||
p = str.rfind('\n', 0, p-1) + 1
|
||||
# The stmt str[p:q] isn't a continuation, but may be blank
|
||||
# or a non-indenting comment line.
|
||||
if _junkre(str, p):
|
||||
i = i-1
|
||||
else:
|
||||
break
|
||||
if i == 0:
|
||||
# nothing but junk!
|
||||
assert p == 0
|
||||
q = p
|
||||
self.stmt_start, self.stmt_end = p, q
|
||||
|
||||
# Analyze this stmt, to find the last open bracket (if any)
|
||||
# and last interesting character (if any).
|
||||
lastch = ""
|
||||
stack = [] # stack of open bracket indices
|
||||
push_stack = stack.append
|
||||
bracketing = [(p, 0)]
|
||||
while p < q:
|
||||
# suck up all except ()[]{}'"#\\
|
||||
m = _chew_ordinaryre(str, p, q)
|
||||
if m:
|
||||
# we skipped at least one boring char
|
||||
newp = m.end()
|
||||
# back up over totally boring whitespace
|
||||
i = newp - 1 # index of last boring char
|
||||
while i >= p and str[i] in " \t\n":
|
||||
i = i-1
|
||||
if i >= p:
|
||||
lastch = str[i]
|
||||
p = newp
|
||||
if p >= q:
|
||||
break
|
||||
|
||||
ch = str[p]
|
||||
|
||||
if ch in "([{":
|
||||
push_stack(p)
|
||||
bracketing.append((p, len(stack)))
|
||||
lastch = ch
|
||||
p = p+1
|
||||
continue
|
||||
|
||||
if ch in ")]}":
|
||||
if stack:
|
||||
del stack[-1]
|
||||
lastch = ch
|
||||
p = p+1
|
||||
bracketing.append((p, len(stack)))
|
||||
continue
|
||||
|
||||
if ch == '"' or ch == "'":
|
||||
# consume string
|
||||
# Note that study1 did this with a Python loop, but
|
||||
# we use a regexp here; the reason is speed in both
|
||||
# cases; the string may be huge, but study1 pre-squashed
|
||||
# strings to a couple of characters per line. study1
|
||||
# also needed to keep track of newlines, and we don't
|
||||
# have to.
|
||||
bracketing.append((p, len(stack)+1))
|
||||
lastch = ch
|
||||
p = _match_stringre(str, p, q).end()
|
||||
bracketing.append((p, len(stack)))
|
||||
continue
|
||||
|
||||
if ch == '#':
|
||||
# consume comment and trailing newline
|
||||
bracketing.append((p, len(stack)+1))
|
||||
p = str.find('\n', p, q) + 1
|
||||
assert p > 0
|
||||
bracketing.append((p, len(stack)))
|
||||
continue
|
||||
|
||||
assert ch == '\\'
|
||||
p = p+1 # beyond backslash
|
||||
assert p < q
|
||||
if str[p] != '\n':
|
||||
# the program is invalid, but can't complain
|
||||
lastch = ch + str[p]
|
||||
p = p+1 # beyond escaped char
|
||||
|
||||
# end while p < q:
|
||||
|
||||
self.lastch = lastch
|
||||
if stack:
|
||||
self.lastopenbracketpos = stack[-1]
|
||||
self.stmt_bracketing = tuple(bracketing)
|
||||
|
||||
# Assuming continuation is C_BRACKET, return the number
|
||||
# of spaces the next line should be indented.
|
||||
|
||||
def compute_bracket_indent(self):
|
||||
self._study2()
|
||||
assert self.continuation == C_BRACKET
|
||||
j = self.lastopenbracketpos
|
||||
str = self.str
|
||||
n = len(str)
|
||||
origi = i = str.rfind('\n', 0, j) + 1
|
||||
j = j+1 # one beyond open bracket
|
||||
# find first list item; set i to start of its line
|
||||
while j < n:
|
||||
m = _itemre(str, j)
|
||||
if m:
|
||||
j = m.end() - 1 # index of first interesting char
|
||||
extra = 0
|
||||
break
|
||||
else:
|
||||
# this line is junk; advance to next line
|
||||
i = j = str.find('\n', j) + 1
|
||||
else:
|
||||
# nothing interesting follows the bracket;
|
||||
# reproduce the bracket line's indentation + a level
|
||||
j = i = origi
|
||||
while str[j] in " \t":
|
||||
j = j+1
|
||||
extra = self.indentwidth
|
||||
return len(str[i:j].expandtabs(self.tabwidth)) + extra
|
||||
|
||||
# Return number of physical lines in last stmt (whether or not
|
||||
# it's an interesting stmt! this is intended to be called when
|
||||
# continuation is C_BACKSLASH).
|
||||
|
||||
def get_num_lines_in_stmt(self):
|
||||
self._study1()
|
||||
goodlines = self.goodlines
|
||||
return goodlines[-1] - goodlines[-2]
|
||||
|
||||
# Assuming continuation is C_BACKSLASH, return the number of spaces
|
||||
# the next line should be indented. Also assuming the new line is
|
||||
# the first one following the initial line of the stmt.
|
||||
|
||||
def compute_backslash_indent(self):
|
||||
self._study2()
|
||||
assert self.continuation == C_BACKSLASH
|
||||
str = self.str
|
||||
i = self.stmt_start
|
||||
while str[i] in " \t":
|
||||
i = i+1
|
||||
startpos = i
|
||||
|
||||
# See whether the initial line starts an assignment stmt; i.e.,
|
||||
# look for an = operator
|
||||
endpos = str.find('\n', startpos) + 1
|
||||
found = level = 0
|
||||
while i < endpos:
|
||||
ch = str[i]
|
||||
if ch in "([{":
|
||||
level = level + 1
|
||||
i = i+1
|
||||
elif ch in ")]}":
|
||||
if level:
|
||||
level = level - 1
|
||||
i = i+1
|
||||
elif ch == '"' or ch == "'":
|
||||
i = _match_stringre(str, i, endpos).end()
|
||||
elif ch == '#':
|
||||
break
|
||||
elif level == 0 and ch == '=' and \
|
||||
(i == 0 or str[i-1] not in "=<>!") and \
|
||||
str[i+1] != '=':
|
||||
found = 1
|
||||
break
|
||||
else:
|
||||
i = i+1
|
||||
|
||||
if found:
|
||||
# found a legit =, but it may be the last interesting
|
||||
# thing on the line
|
||||
i = i+1 # move beyond the =
|
||||
found = re.match(r"\s*\\", str[i:endpos]) is None
|
||||
|
||||
if not found:
|
||||
# oh well ... settle for moving beyond the first chunk
|
||||
# of non-whitespace chars
|
||||
i = startpos
|
||||
while str[i] not in " \t\n":
|
||||
i = i+1
|
||||
|
||||
return len(str[self.stmt_start:i].expandtabs(\
|
||||
self.tabwidth)) + 1
|
||||
|
||||
# Return the leading whitespace on the initial line of the last
|
||||
# interesting stmt.
|
||||
|
||||
def get_base_indent_string(self):
|
||||
self._study2()
|
||||
i, n = self.stmt_start, self.stmt_end
|
||||
j = i
|
||||
str = self.str
|
||||
while j < n and str[j] in " \t":
|
||||
j = j + 1
|
||||
return str[i:j]
|
||||
|
||||
# Did the last interesting stmt open a block?
|
||||
|
||||
def is_block_opener(self):
|
||||
self._study2()
|
||||
return self.lastch == ':'
|
||||
|
||||
# Did the last interesting stmt close a block?
|
||||
|
||||
def is_block_closer(self):
|
||||
self._study2()
|
||||
return _closere(self.str, self.stmt_start) is not None
|
||||
|
||||
# index of last open bracket ({[, or None if none
|
||||
lastopenbracketpos = None
|
||||
|
||||
def get_last_open_bracket_pos(self):
|
||||
self._study2()
|
||||
return self.lastopenbracketpos
|
||||
|
||||
# the structure of the bracketing of the last interesting statement,
|
||||
# in the format defined in _study2, or None if the text didn't contain
|
||||
# anything
|
||||
stmt_bracketing = None
|
||||
|
||||
def get_last_stmt_bracketing(self):
|
||||
self._study2()
|
||||
return self.stmt_bracketing
|
||||
1610
Darwin/lib/python3.5/idlelib/PyShell.py
Executable file
60
Darwin/lib/python3.5/idlelib/README.txt
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
IDLE is Python's Tkinter-based Integrated DeveLopment Environment.
|
||||
|
||||
IDLE emphasizes a lightweight, clean design with a simple user interface.
|
||||
Although it is suitable for beginners, even advanced users will find that
|
||||
IDLE has everything they really need to develop pure Python code.
|
||||
|
||||
IDLE features a multi-window text editor with multiple undo, Python colorizing,
|
||||
and many other capabilities, e.g. smart indent, call tips, and autocompletion.
|
||||
|
||||
The editor has comprehensive search functions, including searching through
|
||||
multiple files. Class browsers and path browsers provide fast access to
|
||||
code objects from a top level viewpoint without dealing with code folding.
|
||||
|
||||
There is a Python Shell window which features colorizing and command recall.
|
||||
|
||||
IDLE executes Python code in a separate process, which is restarted for each
|
||||
Run (F5) initiated from an editor window. The environment can also be
|
||||
restarted from the Shell window without restarting IDLE.
|
||||
|
||||
This enhancement has often been requested, and is now finally available. The
|
||||
magic "reload/import *" incantations are no longer required when editing and
|
||||
testing a module two or three steps down the import chain.
|
||||
|
||||
(Personal firewall software may warn about the connection IDLE makes to its
|
||||
subprocess using this computer's internal loopback interface. This connection
|
||||
is not visible on any external interface and no data is sent to or received
|
||||
from the Internet.)
|
||||
|
||||
It is possible to interrupt tightly looping user code, even on Windows.
|
||||
|
||||
Applications which cannot support subprocesses and/or sockets can still run
|
||||
IDLE in a single process.
|
||||
|
||||
IDLE has an integrated debugger with stepping, persistent breakpoints, and call
|
||||
stack visibility.
|
||||
|
||||
There is a GUI configuration manager which makes it easy to select fonts,
|
||||
colors, keybindings, and startup options. This facility includes a feature
|
||||
which allows the user to specify additional help sources, either locally or on
|
||||
the web.
|
||||
|
||||
IDLE is coded in 100% pure Python, using the Tkinter GUI toolkit (Tk/Tcl)
|
||||
and is cross-platform, working on Unix, Mac, and Windows.
|
||||
|
||||
IDLE accepts command line arguments. Try idle -h to see the options.
|
||||
|
||||
|
||||
If you find bugs or have suggestions or patches, let us know about
|
||||
them by using the Python issue tracker:
|
||||
|
||||
http://bugs.python.org
|
||||
|
||||
For further details and links, read the Help files and check the IDLE home
|
||||
page at
|
||||
|
||||
http://www.python.org/idle/
|
||||
|
||||
There is a mail list for IDLE: idle-dev@python.org. You can join at
|
||||
|
||||
http://mail.python.org/mailman/listinfo/idle-dev
|
||||
388
Darwin/lib/python3.5/idlelib/RemoteDebugger.py
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
"""Support for remote Python debugging.
|
||||
|
||||
Some ASCII art to describe the structure:
|
||||
|
||||
IN PYTHON SUBPROCESS # IN IDLE PROCESS
|
||||
#
|
||||
# oid='gui_adapter'
|
||||
+----------+ # +------------+ +-----+
|
||||
| GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
|
||||
+-----+--calls-->+----------+ # +------------+ +-----+
|
||||
| Idb | # /
|
||||
+-----+<-calls--+------------+ # +----------+<--calls-/
|
||||
| IdbAdapter |<--remote#call--| IdbProxy |
|
||||
+------------+ # +----------+
|
||||
oid='idb_adapter' #
|
||||
|
||||
The purpose of the Proxy and Adapter classes is to translate certain
|
||||
arguments and return values that cannot be transported through the RPC
|
||||
barrier, in particular frame and traceback objects.
|
||||
|
||||
"""
|
||||
|
||||
import types
|
||||
from idlelib import Debugger
|
||||
|
||||
debugging = 0
|
||||
|
||||
idb_adap_oid = "idb_adapter"
|
||||
gui_adap_oid = "gui_adapter"
|
||||
|
||||
#=======================================
|
||||
#
|
||||
# In the PYTHON subprocess:
|
||||
|
||||
frametable = {}
|
||||
dicttable = {}
|
||||
codetable = {}
|
||||
tracebacktable = {}
|
||||
|
||||
def wrap_frame(frame):
|
||||
fid = id(frame)
|
||||
frametable[fid] = frame
|
||||
return fid
|
||||
|
||||
def wrap_info(info):
|
||||
"replace info[2], a traceback instance, by its ID"
|
||||
if info is None:
|
||||
return None
|
||||
else:
|
||||
traceback = info[2]
|
||||
assert isinstance(traceback, types.TracebackType)
|
||||
traceback_id = id(traceback)
|
||||
tracebacktable[traceback_id] = traceback
|
||||
modified_info = (info[0], info[1], traceback_id)
|
||||
return modified_info
|
||||
|
||||
class GUIProxy:
|
||||
|
||||
def __init__(self, conn, gui_adap_oid):
|
||||
self.conn = conn
|
||||
self.oid = gui_adap_oid
|
||||
|
||||
def interaction(self, message, frame, info=None):
|
||||
# calls rpc.SocketIO.remotecall() via run.MyHandler instance
|
||||
# pass frame and traceback object IDs instead of the objects themselves
|
||||
self.conn.remotecall(self.oid, "interaction",
|
||||
(message, wrap_frame(frame), wrap_info(info)),
|
||||
{})
|
||||
|
||||
class IdbAdapter:
|
||||
|
||||
def __init__(self, idb):
|
||||
self.idb = idb
|
||||
|
||||
#----------called by an IdbProxy----------
|
||||
|
||||
def set_step(self):
|
||||
self.idb.set_step()
|
||||
|
||||
def set_quit(self):
|
||||
self.idb.set_quit()
|
||||
|
||||
def set_continue(self):
|
||||
self.idb.set_continue()
|
||||
|
||||
def set_next(self, fid):
|
||||
frame = frametable[fid]
|
||||
self.idb.set_next(frame)
|
||||
|
||||
def set_return(self, fid):
|
||||
frame = frametable[fid]
|
||||
self.idb.set_return(frame)
|
||||
|
||||
def get_stack(self, fid, tbid):
|
||||
frame = frametable[fid]
|
||||
if tbid is None:
|
||||
tb = None
|
||||
else:
|
||||
tb = tracebacktable[tbid]
|
||||
stack, i = self.idb.get_stack(frame, tb)
|
||||
stack = [(wrap_frame(frame2), k) for frame2, k in stack]
|
||||
return stack, i
|
||||
|
||||
def run(self, cmd):
|
||||
import __main__
|
||||
self.idb.run(cmd, __main__.__dict__)
|
||||
|
||||
def set_break(self, filename, lineno):
|
||||
msg = self.idb.set_break(filename, lineno)
|
||||
return msg
|
||||
|
||||
def clear_break(self, filename, lineno):
|
||||
msg = self.idb.clear_break(filename, lineno)
|
||||
return msg
|
||||
|
||||
def clear_all_file_breaks(self, filename):
|
||||
msg = self.idb.clear_all_file_breaks(filename)
|
||||
return msg
|
||||
|
||||
#----------called by a FrameProxy----------
|
||||
|
||||
def frame_attr(self, fid, name):
|
||||
frame = frametable[fid]
|
||||
return getattr(frame, name)
|
||||
|
||||
def frame_globals(self, fid):
|
||||
frame = frametable[fid]
|
||||
dict = frame.f_globals
|
||||
did = id(dict)
|
||||
dicttable[did] = dict
|
||||
return did
|
||||
|
||||
def frame_locals(self, fid):
|
||||
frame = frametable[fid]
|
||||
dict = frame.f_locals
|
||||
did = id(dict)
|
||||
dicttable[did] = dict
|
||||
return did
|
||||
|
||||
def frame_code(self, fid):
|
||||
frame = frametable[fid]
|
||||
code = frame.f_code
|
||||
cid = id(code)
|
||||
codetable[cid] = code
|
||||
return cid
|
||||
|
||||
#----------called by a CodeProxy----------
|
||||
|
||||
def code_name(self, cid):
|
||||
code = codetable[cid]
|
||||
return code.co_name
|
||||
|
||||
def code_filename(self, cid):
|
||||
code = codetable[cid]
|
||||
return code.co_filename
|
||||
|
||||
#----------called by a DictProxy----------
|
||||
|
||||
def dict_keys(self, did):
|
||||
raise NotImplemented("dict_keys not public or pickleable")
|
||||
## dict = dicttable[did]
|
||||
## return dict.keys()
|
||||
|
||||
### Needed until dict_keys is type is finished and pickealable.
|
||||
### Will probably need to extend rpc.py:SocketIO._proxify at that time.
|
||||
def dict_keys_list(self, did):
|
||||
dict = dicttable[did]
|
||||
return list(dict.keys())
|
||||
|
||||
def dict_item(self, did, key):
|
||||
dict = dicttable[did]
|
||||
value = dict[key]
|
||||
value = repr(value) ### can't pickle module 'builtins'
|
||||
return value
|
||||
|
||||
#----------end class IdbAdapter----------
|
||||
|
||||
|
||||
def start_debugger(rpchandler, gui_adap_oid):
|
||||
"""Start the debugger and its RPC link in the Python subprocess
|
||||
|
||||
Start the subprocess side of the split debugger and set up that side of the
|
||||
RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
|
||||
objects and linking them together. Register the IdbAdapter with the
|
||||
RPCServer to handle RPC requests from the split debugger GUI via the
|
||||
IdbProxy.
|
||||
|
||||
"""
|
||||
gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
|
||||
idb = Debugger.Idb(gui_proxy)
|
||||
idb_adap = IdbAdapter(idb)
|
||||
rpchandler.register(idb_adap_oid, idb_adap)
|
||||
return idb_adap_oid
|
||||
|
||||
|
||||
#=======================================
|
||||
#
|
||||
# In the IDLE process:
|
||||
|
||||
|
||||
class FrameProxy:
|
||||
|
||||
def __init__(self, conn, fid):
|
||||
self._conn = conn
|
||||
self._fid = fid
|
||||
self._oid = "idb_adapter"
|
||||
self._dictcache = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name[:1] == "_":
|
||||
raise AttributeError(name)
|
||||
if name == "f_code":
|
||||
return self._get_f_code()
|
||||
if name == "f_globals":
|
||||
return self._get_f_globals()
|
||||
if name == "f_locals":
|
||||
return self._get_f_locals()
|
||||
return self._conn.remotecall(self._oid, "frame_attr",
|
||||
(self._fid, name), {})
|
||||
|
||||
def _get_f_code(self):
|
||||
cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
|
||||
return CodeProxy(self._conn, self._oid, cid)
|
||||
|
||||
def _get_f_globals(self):
|
||||
did = self._conn.remotecall(self._oid, "frame_globals",
|
||||
(self._fid,), {})
|
||||
return self._get_dict_proxy(did)
|
||||
|
||||
def _get_f_locals(self):
|
||||
did = self._conn.remotecall(self._oid, "frame_locals",
|
||||
(self._fid,), {})
|
||||
return self._get_dict_proxy(did)
|
||||
|
||||
def _get_dict_proxy(self, did):
|
||||
if did in self._dictcache:
|
||||
return self._dictcache[did]
|
||||
dp = DictProxy(self._conn, self._oid, did)
|
||||
self._dictcache[did] = dp
|
||||
return dp
|
||||
|
||||
|
||||
class CodeProxy:
|
||||
|
||||
def __init__(self, conn, oid, cid):
|
||||
self._conn = conn
|
||||
self._oid = oid
|
||||
self._cid = cid
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == "co_name":
|
||||
return self._conn.remotecall(self._oid, "code_name",
|
||||
(self._cid,), {})
|
||||
if name == "co_filename":
|
||||
return self._conn.remotecall(self._oid, "code_filename",
|
||||
(self._cid,), {})
|
||||
|
||||
|
||||
class DictProxy:
|
||||
|
||||
def __init__(self, conn, oid, did):
|
||||
self._conn = conn
|
||||
self._oid = oid
|
||||
self._did = did
|
||||
|
||||
## def keys(self):
|
||||
## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
|
||||
|
||||
# 'temporary' until dict_keys is a pickleable built-in type
|
||||
def keys(self):
|
||||
return self._conn.remotecall(self._oid,
|
||||
"dict_keys_list", (self._did,), {})
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._conn.remotecall(self._oid, "dict_item",
|
||||
(self._did, key), {})
|
||||
|
||||
def __getattr__(self, name):
|
||||
##print("*** Failed DictProxy.__getattr__:", name)
|
||||
raise AttributeError(name)
|
||||
|
||||
|
||||
class GUIAdapter:
|
||||
|
||||
def __init__(self, conn, gui):
|
||||
self.conn = conn
|
||||
self.gui = gui
|
||||
|
||||
def interaction(self, message, fid, modified_info):
|
||||
##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))
|
||||
frame = FrameProxy(self.conn, fid)
|
||||
self.gui.interaction(message, frame, modified_info)
|
||||
|
||||
|
||||
class IdbProxy:
|
||||
|
||||
def __init__(self, conn, shell, oid):
|
||||
self.oid = oid
|
||||
self.conn = conn
|
||||
self.shell = shell
|
||||
|
||||
def call(self, methodname, *args, **kwargs):
|
||||
##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))
|
||||
value = self.conn.remotecall(self.oid, methodname, args, kwargs)
|
||||
##print("*** IdbProxy.call %s returns %r" % (methodname, value))
|
||||
return value
|
||||
|
||||
def run(self, cmd, locals):
|
||||
# Ignores locals on purpose!
|
||||
seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
|
||||
self.shell.interp.active_seq = seq
|
||||
|
||||
def get_stack(self, frame, tbid):
|
||||
# passing frame and traceback IDs, not the objects themselves
|
||||
stack, i = self.call("get_stack", frame._fid, tbid)
|
||||
stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
|
||||
return stack, i
|
||||
|
||||
def set_continue(self):
|
||||
self.call("set_continue")
|
||||
|
||||
def set_step(self):
|
||||
self.call("set_step")
|
||||
|
||||
def set_next(self, frame):
|
||||
self.call("set_next", frame._fid)
|
||||
|
||||
def set_return(self, frame):
|
||||
self.call("set_return", frame._fid)
|
||||
|
||||
def set_quit(self):
|
||||
self.call("set_quit")
|
||||
|
||||
def set_break(self, filename, lineno):
|
||||
msg = self.call("set_break", filename, lineno)
|
||||
return msg
|
||||
|
||||
def clear_break(self, filename, lineno):
|
||||
msg = self.call("clear_break", filename, lineno)
|
||||
return msg
|
||||
|
||||
def clear_all_file_breaks(self, filename):
|
||||
msg = self.call("clear_all_file_breaks", filename)
|
||||
return msg
|
||||
|
||||
def start_remote_debugger(rpcclt, pyshell):
|
||||
"""Start the subprocess debugger, initialize the debugger GUI and RPC link
|
||||
|
||||
Request the RPCServer start the Python subprocess debugger and link. Set
|
||||
up the Idle side of the split debugger by instantiating the IdbProxy,
|
||||
debugger GUI, and debugger GUIAdapter objects and linking them together.
|
||||
|
||||
Register the GUIAdapter with the RPCClient to handle debugger GUI
|
||||
interaction requests coming from the subprocess debugger via the GUIProxy.
|
||||
|
||||
The IdbAdapter will pass execution and environment requests coming from the
|
||||
Idle debugger GUI to the subprocess debugger via the IdbProxy.
|
||||
|
||||
"""
|
||||
global idb_adap_oid
|
||||
|
||||
idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
|
||||
(gui_adap_oid,), {})
|
||||
idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
|
||||
gui = Debugger.Debugger(pyshell, idb_proxy)
|
||||
gui_adap = GUIAdapter(rpcclt, gui)
|
||||
rpcclt.register(gui_adap_oid, gui_adap)
|
||||
return gui
|
||||
|
||||
def close_remote_debugger(rpcclt):
|
||||
"""Shut down subprocess debugger and Idle side of debugger RPC link
|
||||
|
||||
Request that the RPCServer shut down the subprocess debugger and link.
|
||||
Unregister the GUIAdapter, which will cause a GC on the Idle process
|
||||
debugger and RPC link objects. (The second reference to the debugger GUI
|
||||
is deleted in PyShell.close_remote_debugger().)
|
||||
|
||||
"""
|
||||
close_subprocess_debugger(rpcclt)
|
||||
rpcclt.unregister(gui_adap_oid)
|
||||
|
||||
def close_subprocess_debugger(rpcclt):
|
||||
rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
|
||||
|
||||
def restart_subprocess_debugger(rpcclt):
|
||||
idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
|
||||
(gui_adap_oid,), {})
|
||||
assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
|
||||
36
Darwin/lib/python3.5/idlelib/RemoteObjectBrowser.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
from idlelib import rpc
|
||||
|
||||
def remote_object_tree_item(item):
|
||||
wrapper = WrappedObjectTreeItem(item)
|
||||
oid = id(wrapper)
|
||||
rpc.objecttable[oid] = wrapper
|
||||
return oid
|
||||
|
||||
class WrappedObjectTreeItem:
|
||||
# Lives in PYTHON subprocess
|
||||
|
||||
def __init__(self, item):
|
||||
self.__item = item
|
||||
|
||||
def __getattr__(self, name):
|
||||
value = getattr(self.__item, name)
|
||||
return value
|
||||
|
||||
def _GetSubList(self):
|
||||
sub_list = self.__item._GetSubList()
|
||||
return list(map(remote_object_tree_item, sub_list))
|
||||
|
||||
class StubObjectTreeItem:
|
||||
# Lives in IDLE process
|
||||
|
||||
def __init__(self, sockio, oid):
|
||||
self.sockio = sockio
|
||||
self.oid = oid
|
||||
|
||||
def __getattr__(self, name):
|
||||
value = rpc.MethodProxy(self.sockio, self.oid, name)
|
||||
return value
|
||||
|
||||
def _GetSubList(self):
|
||||
sub_list = self.sockio.remotecall(self.oid, "_GetSubList", (), {})
|
||||
return [StubObjectTreeItem(self.sockio, oid) for oid in sub_list]
|
||||
221
Darwin/lib/python3.5/idlelib/ReplaceDialog.py
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
from tkinter import *
|
||||
|
||||
from idlelib import SearchEngine
|
||||
from idlelib.SearchDialogBase import SearchDialogBase
|
||||
import re
|
||||
|
||||
|
||||
def replace(text):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_replacedialog"):
|
||||
engine._replacedialog = ReplaceDialog(root, engine)
|
||||
dialog = engine._replacedialog
|
||||
dialog.open(text)
|
||||
|
||||
|
||||
class ReplaceDialog(SearchDialogBase):
|
||||
|
||||
title = "Replace Dialog"
|
||||
icon = "Replace"
|
||||
|
||||
def __init__(self, root, engine):
|
||||
SearchDialogBase.__init__(self, root, engine)
|
||||
self.replvar = StringVar(root)
|
||||
|
||||
def open(self, text):
|
||||
SearchDialogBase.open(self, text)
|
||||
try:
|
||||
first = text.index("sel.first")
|
||||
except TclError:
|
||||
first = None
|
||||
try:
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
last = None
|
||||
first = first or text.index("insert")
|
||||
last = last or first
|
||||
self.show_hit(first, last)
|
||||
self.ok = 1
|
||||
|
||||
def create_entries(self):
|
||||
SearchDialogBase.create_entries(self)
|
||||
self.replent = self.make_entry("Replace with:", self.replvar)[0]
|
||||
|
||||
def create_command_buttons(self):
|
||||
SearchDialogBase.create_command_buttons(self)
|
||||
self.make_button("Find", self.find_it)
|
||||
self.make_button("Replace", self.replace_it)
|
||||
self.make_button("Replace+Find", self.default_command, 1)
|
||||
self.make_button("Replace All", self.replace_all)
|
||||
|
||||
def find_it(self, event=None):
|
||||
self.do_find(0)
|
||||
|
||||
def replace_it(self, event=None):
|
||||
if self.do_find(self.ok):
|
||||
self.do_replace()
|
||||
|
||||
def default_command(self, event=None):
|
||||
if self.do_find(self.ok):
|
||||
if self.do_replace(): # Only find next match if replace succeeded.
|
||||
# A bad re can cause a it to fail.
|
||||
self.do_find(0)
|
||||
|
||||
def _replace_expand(self, m, repl):
|
||||
""" Helper function for expanding a regular expression
|
||||
in the replace field, if needed. """
|
||||
if self.engine.isre():
|
||||
try:
|
||||
new = m.expand(repl)
|
||||
except re.error:
|
||||
self.engine.report_error(repl, 'Invalid Replace Expression')
|
||||
new = None
|
||||
else:
|
||||
new = repl
|
||||
|
||||
return new
|
||||
|
||||
def replace_all(self, event=None):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return
|
||||
repl = self.replvar.get()
|
||||
text = self.text
|
||||
res = self.engine.search_text(text, prog)
|
||||
if not res:
|
||||
text.bell()
|
||||
return
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_remove("hit", "1.0", "end")
|
||||
line = res[0]
|
||||
col = res[1].start()
|
||||
if self.engine.iswrap():
|
||||
line = 1
|
||||
col = 0
|
||||
ok = 1
|
||||
first = last = None
|
||||
# XXX ought to replace circular instead of top-to-bottom when wrapping
|
||||
text.undo_block_start()
|
||||
while 1:
|
||||
res = self.engine.search_forward(text, prog, line, col, 0, ok)
|
||||
if not res:
|
||||
break
|
||||
line, m = res
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
orig = m.group()
|
||||
new = self._replace_expand(m, repl)
|
||||
if new is None:
|
||||
break
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
if new == orig:
|
||||
text.mark_set("insert", last)
|
||||
else:
|
||||
text.mark_set("insert", first)
|
||||
if first != last:
|
||||
text.delete(first, last)
|
||||
if new:
|
||||
text.insert(first, new)
|
||||
col = i + len(new)
|
||||
ok = 0
|
||||
text.undo_block_stop()
|
||||
if first and last:
|
||||
self.show_hit(first, last)
|
||||
self.close()
|
||||
|
||||
def do_find(self, ok=0):
|
||||
if not self.engine.getprog():
|
||||
return False
|
||||
text = self.text
|
||||
res = self.engine.search_text(text, None, ok)
|
||||
if not res:
|
||||
text.bell()
|
||||
return False
|
||||
line, m = res
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
self.show_hit(first, last)
|
||||
self.ok = 1
|
||||
return True
|
||||
|
||||
def do_replace(self):
|
||||
prog = self.engine.getprog()
|
||||
if not prog:
|
||||
return False
|
||||
text = self.text
|
||||
try:
|
||||
first = pos = text.index("sel.first")
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
pos = None
|
||||
if not pos:
|
||||
first = last = pos = text.index("insert")
|
||||
line, col = SearchEngine.get_line_col(pos)
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
m = prog.match(chars, col)
|
||||
if not prog:
|
||||
return False
|
||||
new = self._replace_expand(m, self.replvar.get())
|
||||
if new is None:
|
||||
return False
|
||||
text.mark_set("insert", first)
|
||||
text.undo_block_start()
|
||||
if m.group():
|
||||
text.delete(first, last)
|
||||
if new:
|
||||
text.insert(first, new)
|
||||
text.undo_block_stop()
|
||||
self.show_hit(first, text.index("insert"))
|
||||
self.ok = 0
|
||||
return True
|
||||
|
||||
def show_hit(self, first, last):
|
||||
text = self.text
|
||||
text.mark_set("insert", first)
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_add("sel", first, last)
|
||||
text.tag_remove("hit", "1.0", "end")
|
||||
if first == last:
|
||||
text.tag_add("hit", first)
|
||||
else:
|
||||
text.tag_add("hit", first, last)
|
||||
text.see("insert")
|
||||
text.update_idletasks()
|
||||
|
||||
def close(self, event=None):
|
||||
SearchDialogBase.close(self, event)
|
||||
self.text.tag_remove("hit", "1.0", "end")
|
||||
|
||||
def _replace_dialog(parent):
|
||||
root = Tk()
|
||||
root.title("Test ReplaceDialog")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
|
||||
# mock undo delegator methods
|
||||
def undo_block_start():
|
||||
pass
|
||||
|
||||
def undo_block_stop():
|
||||
pass
|
||||
|
||||
text = Text(root)
|
||||
text.undo_block_start = undo_block_start
|
||||
text.undo_block_stop = undo_block_stop
|
||||
text.pack()
|
||||
text.insert("insert","This is a sample string.\n"*10)
|
||||
|
||||
def show_replace():
|
||||
text.tag_add(SEL, "1.0", END)
|
||||
replace(text)
|
||||
text.tag_remove(SEL, "1.0", END)
|
||||
|
||||
button = Button(root, text="Replace", command=show_replace)
|
||||
button.pack()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_replace_dialog)
|
||||
33
Darwin/lib/python3.5/idlelib/RstripExtension.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'Provides "Strip trailing whitespace" under the "Format" menu.'
|
||||
|
||||
class RstripExtension:
|
||||
|
||||
menudefs = [
|
||||
('format', [None, ('Strip trailing whitespace', '<<do-rstrip>>'), ] ), ]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.editwin.text.bind("<<do-rstrip>>", self.do_rstrip)
|
||||
|
||||
def do_rstrip(self, event=None):
|
||||
|
||||
text = self.editwin.text
|
||||
undo = self.editwin.undo
|
||||
|
||||
undo.undo_block_start()
|
||||
|
||||
end_line = int(float(text.index('end')))
|
||||
for cur in range(1, end_line):
|
||||
txt = text.get('%i.0' % cur, '%i.end' % cur)
|
||||
raw = len(txt)
|
||||
cut = len(txt.rstrip())
|
||||
# Since text.delete() marks file as changed, even if not,
|
||||
# only call it when needed to actually delete something.
|
||||
if cut < raw:
|
||||
text.delete('%i.%i' % (cur, cut), '%i.end' % cur)
|
||||
|
||||
undo.undo_block_stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_rstrip', verbosity=2, exit=False)
|
||||
206
Darwin/lib/python3.5/idlelib/ScriptBinding.py
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
"""Extension to execute code outside the Python shell window.
|
||||
|
||||
This adds the following commands:
|
||||
|
||||
- Check module does a full syntax check of the current module.
|
||||
It also runs the tabnanny to catch any inconsistent tabs.
|
||||
|
||||
- Run module executes the module's code in the __main__ namespace. The window
|
||||
must have been saved previously. The module is added to sys.modules, and is
|
||||
also added to the __main__ namespace.
|
||||
|
||||
XXX GvR Redesign this interface (yet again) as follows:
|
||||
|
||||
- Present a dialog box for ``Run Module''
|
||||
|
||||
- Allow specify command line arguments in the dialog box
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import tabnanny
|
||||
import tokenize
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
from idlelib import PyShell
|
||||
|
||||
from idlelib.configHandler import idleConf
|
||||
from idlelib import macosxSupport
|
||||
|
||||
indent_message = """Error: Inconsistent indentation detected!
|
||||
|
||||
1) Your indentation is outright incorrect (easy to fix), OR
|
||||
|
||||
2) Your indentation mixes tabs and spaces.
|
||||
|
||||
To fix case 2, change all tabs to spaces by using Edit->Select All followed \
|
||||
by Format->Untabify Region and specify the number of columns used by each tab.
|
||||
"""
|
||||
|
||||
|
||||
class ScriptBinding:
|
||||
|
||||
menudefs = [
|
||||
('run', [None,
|
||||
('Check Module', '<<check-module>>'),
|
||||
('Run Module', '<<run-module>>'), ]), ]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
# Provide instance variables referenced by Debugger
|
||||
# XXX This should be done differently
|
||||
self.flist = self.editwin.flist
|
||||
self.root = self.editwin.root
|
||||
|
||||
if macosxSupport.isCocoaTk():
|
||||
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
|
||||
|
||||
def check_module_event(self, event):
|
||||
filename = self.getfilename()
|
||||
if not filename:
|
||||
return 'break'
|
||||
if not self.checksyntax(filename):
|
||||
return 'break'
|
||||
if not self.tabnanny(filename):
|
||||
return 'break'
|
||||
|
||||
def tabnanny(self, filename):
|
||||
# XXX: tabnanny should work on binary files as well
|
||||
with tokenize.open(filename) as f:
|
||||
try:
|
||||
tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
|
||||
except tokenize.TokenError as msg:
|
||||
msgtxt, (lineno, start) = msg
|
||||
self.editwin.gotoline(lineno)
|
||||
self.errorbox("Tabnanny Tokenizing Error",
|
||||
"Token Error: %s" % msgtxt)
|
||||
return False
|
||||
except tabnanny.NannyNag as nag:
|
||||
# The error messages from tabnanny are too confusing...
|
||||
self.editwin.gotoline(nag.get_lineno())
|
||||
self.errorbox("Tab/space error", indent_message)
|
||||
return False
|
||||
return True
|
||||
|
||||
def checksyntax(self, filename):
|
||||
self.shell = shell = self.flist.open_shell()
|
||||
saved_stream = shell.get_warning_stream()
|
||||
shell.set_warning_stream(shell.stderr)
|
||||
with open(filename, 'rb') as f:
|
||||
source = f.read()
|
||||
if b'\r' in source:
|
||||
source = source.replace(b'\r\n', b'\n')
|
||||
source = source.replace(b'\r', b'\n')
|
||||
if source and source[-1] != ord(b'\n'):
|
||||
source = source + b'\n'
|
||||
editwin = self.editwin
|
||||
text = editwin.text
|
||||
text.tag_remove("ERROR", "1.0", "end")
|
||||
try:
|
||||
# If successful, return the compiled code
|
||||
return compile(source, filename, "exec")
|
||||
except (SyntaxError, OverflowError, ValueError) as value:
|
||||
msg = getattr(value, 'msg', '') or value or "<no detail available>"
|
||||
lineno = getattr(value, 'lineno', '') or 1
|
||||
offset = getattr(value, 'offset', '') or 0
|
||||
if offset == 0:
|
||||
lineno += 1 #mark end of offending line
|
||||
pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
|
||||
editwin.colorize_syntax_error(text, pos)
|
||||
self.errorbox("SyntaxError", "%-20s" % msg)
|
||||
return False
|
||||
finally:
|
||||
shell.set_warning_stream(saved_stream)
|
||||
|
||||
def run_module_event(self, event):
|
||||
if macosxSupport.isCocoaTk():
|
||||
# Tk-Cocoa in MacOSX is broken until at least
|
||||
# Tk 8.5.9, and without this rather
|
||||
# crude workaround IDLE would hang when a user
|
||||
# tries to run a module using the keyboard shortcut
|
||||
# (the menu item works fine).
|
||||
self.editwin.text_frame.after(200,
|
||||
lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>'))
|
||||
return 'break'
|
||||
else:
|
||||
return self._run_module_event(event)
|
||||
|
||||
def _run_module_event(self, event):
|
||||
"""Run the module after setting up the environment.
|
||||
|
||||
First check the syntax. If OK, make sure the shell is active and
|
||||
then transfer the arguments, set the run environment's working
|
||||
directory to the directory of the module being executed and also
|
||||
add that directory to its sys.path if not already included.
|
||||
"""
|
||||
|
||||
filename = self.getfilename()
|
||||
if not filename:
|
||||
return 'break'
|
||||
code = self.checksyntax(filename)
|
||||
if not code:
|
||||
return 'break'
|
||||
if not self.tabnanny(filename):
|
||||
return 'break'
|
||||
interp = self.shell.interp
|
||||
if PyShell.use_subprocess:
|
||||
interp.restart_subprocess(with_cwd=False, filename=
|
||||
self.editwin._filename_to_unicode(filename))
|
||||
dirname = os.path.dirname(filename)
|
||||
# XXX Too often this discards arguments the user just set...
|
||||
interp.runcommand("""if 1:
|
||||
__file__ = {filename!r}
|
||||
import sys as _sys
|
||||
from os.path import basename as _basename
|
||||
if (not _sys.argv or
|
||||
_basename(_sys.argv[0]) != _basename(__file__)):
|
||||
_sys.argv = [__file__]
|
||||
import os as _os
|
||||
_os.chdir({dirname!r})
|
||||
del _sys, _basename, _os
|
||||
\n""".format(filename=filename, dirname=dirname))
|
||||
interp.prepend_syspath(filename)
|
||||
# XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
|
||||
# go to __stderr__. With subprocess, they go to the shell.
|
||||
# Need to change streams in PyShell.ModifiedInterpreter.
|
||||
interp.runcode(code)
|
||||
return 'break'
|
||||
|
||||
def getfilename(self):
|
||||
"""Get source filename. If not saved, offer to save (or create) file
|
||||
|
||||
The debugger requires a source file. Make sure there is one, and that
|
||||
the current version of the source buffer has been saved. If the user
|
||||
declines to save or cancels the Save As dialog, return None.
|
||||
|
||||
If the user has configured IDLE for Autosave, the file will be
|
||||
silently saved if it already exists and is dirty.
|
||||
|
||||
"""
|
||||
filename = self.editwin.io.filename
|
||||
if not self.editwin.get_saved():
|
||||
autosave = idleConf.GetOption('main', 'General',
|
||||
'autosave', type='bool')
|
||||
if autosave and filename:
|
||||
self.editwin.io.save(None)
|
||||
else:
|
||||
confirm = self.ask_save_dialog()
|
||||
self.editwin.text.focus_set()
|
||||
if confirm:
|
||||
self.editwin.io.save(None)
|
||||
filename = self.editwin.io.filename
|
||||
else:
|
||||
filename = None
|
||||
return filename
|
||||
|
||||
def ask_save_dialog(self):
|
||||
msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
|
||||
confirm = tkMessageBox.askokcancel(title="Save Before Run or Check",
|
||||
message=msg,
|
||||
default=tkMessageBox.OK,
|
||||
master=self.editwin.text)
|
||||
return confirm
|
||||
|
||||
def errorbox(self, title, message):
|
||||
# XXX This should really be a function of EditorWindow...
|
||||
tkMessageBox.showerror(title, message, master=self.editwin.text)
|
||||
self.editwin.text.focus_set()
|
||||
140
Darwin/lib/python3.5/idlelib/ScrolledList.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
from tkinter import *
|
||||
|
||||
class ScrolledList:
|
||||
|
||||
default = "(None)"
|
||||
|
||||
def __init__(self, master, **options):
|
||||
# Create top frame, with scrollbar and listbox
|
||||
self.master = master
|
||||
self.frame = frame = Frame(master)
|
||||
self.frame.pack(fill="both", expand=1)
|
||||
self.vbar = vbar = Scrollbar(frame, name="vbar")
|
||||
self.vbar.pack(side="right", fill="y")
|
||||
self.listbox = listbox = Listbox(frame, exportselection=0,
|
||||
background="white")
|
||||
if options:
|
||||
listbox.configure(options)
|
||||
listbox.pack(expand=1, fill="both")
|
||||
# Tie listbox and scrollbar together
|
||||
vbar["command"] = listbox.yview
|
||||
listbox["yscrollcommand"] = vbar.set
|
||||
# Bind events to the list box
|
||||
listbox.bind("<ButtonRelease-1>", self.click_event)
|
||||
listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
|
||||
listbox.bind("<ButtonPress-3>", self.popup_event)
|
||||
listbox.bind("<Key-Up>", self.up_event)
|
||||
listbox.bind("<Key-Down>", self.down_event)
|
||||
# Mark as empty
|
||||
self.clear()
|
||||
|
||||
def close(self):
|
||||
self.frame.destroy()
|
||||
|
||||
def clear(self):
|
||||
self.listbox.delete(0, "end")
|
||||
self.empty = 1
|
||||
self.listbox.insert("end", self.default)
|
||||
|
||||
def append(self, item):
|
||||
if self.empty:
|
||||
self.listbox.delete(0, "end")
|
||||
self.empty = 0
|
||||
self.listbox.insert("end", str(item))
|
||||
|
||||
def get(self, index):
|
||||
return self.listbox.get(index)
|
||||
|
||||
def click_event(self, event):
|
||||
self.listbox.activate("@%d,%d" % (event.x, event.y))
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def double_click_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
self.on_double(index)
|
||||
return "break"
|
||||
|
||||
menu = None
|
||||
|
||||
def popup_event(self, event):
|
||||
if not self.menu:
|
||||
self.make_menu()
|
||||
menu = self.menu
|
||||
self.listbox.activate("@%d,%d" % (event.x, event.y))
|
||||
index = self.listbox.index("active")
|
||||
self.select(index)
|
||||
menu.tk_popup(event.x_root, event.y_root)
|
||||
|
||||
def make_menu(self):
|
||||
menu = Menu(self.listbox, tearoff=0)
|
||||
self.menu = menu
|
||||
self.fill_menu()
|
||||
|
||||
def up_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
if self.listbox.selection_includes(index):
|
||||
index = index - 1
|
||||
else:
|
||||
index = self.listbox.size() - 1
|
||||
if index < 0:
|
||||
self.listbox.bell()
|
||||
else:
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def down_event(self, event):
|
||||
index = self.listbox.index("active")
|
||||
if self.listbox.selection_includes(index):
|
||||
index = index + 1
|
||||
else:
|
||||
index = 0
|
||||
if index >= self.listbox.size():
|
||||
self.listbox.bell()
|
||||
else:
|
||||
self.select(index)
|
||||
self.on_select(index)
|
||||
return "break"
|
||||
|
||||
def select(self, index):
|
||||
self.listbox.focus_set()
|
||||
self.listbox.activate(index)
|
||||
self.listbox.selection_clear(0, "end")
|
||||
self.listbox.selection_set(index)
|
||||
self.listbox.see(index)
|
||||
|
||||
# Methods to override for specific actions
|
||||
|
||||
def fill_menu(self):
|
||||
pass
|
||||
|
||||
def on_select(self, index):
|
||||
pass
|
||||
|
||||
def on_double(self, index):
|
||||
pass
|
||||
|
||||
|
||||
def _scrolled_list(parent):
|
||||
root = Tk()
|
||||
root.title("Test ScrolledList")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
class MyScrolledList(ScrolledList):
|
||||
def fill_menu(self): self.menu.add_command(label="right click")
|
||||
def on_select(self, index): print("select", self.get(index))
|
||||
def on_double(self, index): print("double", self.get(index))
|
||||
|
||||
scrolled_list = MyScrolledList(root)
|
||||
for i in range(30):
|
||||
scrolled_list.append("Item %02d" % i)
|
||||
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_scrolled_list)
|
||||
89
Darwin/lib/python3.5/idlelib/SearchDialog.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
from tkinter import *
|
||||
|
||||
from idlelib import SearchEngine
|
||||
from idlelib.SearchDialogBase import SearchDialogBase
|
||||
|
||||
def _setup(text):
|
||||
root = text._root()
|
||||
engine = SearchEngine.get(root)
|
||||
if not hasattr(engine, "_searchdialog"):
|
||||
engine._searchdialog = SearchDialog(root, engine)
|
||||
return engine._searchdialog
|
||||
|
||||
def find(text):
|
||||
pat = text.get("sel.first", "sel.last")
|
||||
return _setup(text).open(text,pat)
|
||||
|
||||
def find_again(text):
|
||||
return _setup(text).find_again(text)
|
||||
|
||||
def find_selection(text):
|
||||
return _setup(text).find_selection(text)
|
||||
|
||||
class SearchDialog(SearchDialogBase):
|
||||
|
||||
def create_widgets(self):
|
||||
SearchDialogBase.create_widgets(self)
|
||||
self.make_button("Find Next", self.default_command, 1)
|
||||
|
||||
def default_command(self, event=None):
|
||||
if not self.engine.getprog():
|
||||
return
|
||||
self.find_again(self.text)
|
||||
|
||||
def find_again(self, text):
|
||||
if not self.engine.getpat():
|
||||
self.open(text)
|
||||
return False
|
||||
if not self.engine.getprog():
|
||||
return False
|
||||
res = self.engine.search_text(text)
|
||||
if res:
|
||||
line, m = res
|
||||
i, j = m.span()
|
||||
first = "%d.%d" % (line, i)
|
||||
last = "%d.%d" % (line, j)
|
||||
try:
|
||||
selfirst = text.index("sel.first")
|
||||
sellast = text.index("sel.last")
|
||||
if selfirst == first and sellast == last:
|
||||
text.bell()
|
||||
return False
|
||||
except TclError:
|
||||
pass
|
||||
text.tag_remove("sel", "1.0", "end")
|
||||
text.tag_add("sel", first, last)
|
||||
text.mark_set("insert", self.engine.isback() and first or last)
|
||||
text.see("insert")
|
||||
return True
|
||||
else:
|
||||
text.bell()
|
||||
return False
|
||||
|
||||
def find_selection(self, text):
|
||||
pat = text.get("sel.first", "sel.last")
|
||||
if pat:
|
||||
self.engine.setcookedpat(pat)
|
||||
return self.find_again(text)
|
||||
|
||||
def _search_dialog(parent):
|
||||
root = Tk()
|
||||
root.title("Test SearchDialog")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
text = Text(root)
|
||||
text.pack()
|
||||
text.insert("insert","This is a sample string.\n"*10)
|
||||
|
||||
def show_find():
|
||||
text.tag_add(SEL, "1.0", END)
|
||||
s = _setup(text)
|
||||
s.open(text)
|
||||
text.tag_remove(SEL, "1.0", END)
|
||||
|
||||
button = Button(root, text="Search", command=show_find)
|
||||
button.pack()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_search_dialog)
|
||||
184
Darwin/lib/python3.5/idlelib/SearchDialogBase.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
|
||||
|
||||
from tkinter import (Toplevel, Frame, Entry, Label, Button,
|
||||
Checkbutton, Radiobutton)
|
||||
|
||||
class SearchDialogBase:
|
||||
'''Create most of a 3 or 4 row, 3 column search dialog.
|
||||
|
||||
The left and wide middle column contain:
|
||||
1 or 2 labeled text entry lines (make_entry, create_entries);
|
||||
a row of standard Checkbuttons (make_frame, create_option_buttons),
|
||||
each of which corresponds to a search engine Variable;
|
||||
a row of dialog-specific Check/Radiobuttons (create_other_buttons).
|
||||
|
||||
The narrow right column contains command buttons
|
||||
(make_button, create_command_buttons).
|
||||
These are bound to functions that execute the command.
|
||||
|
||||
Except for command buttons, this base class is not limited to items
|
||||
common to all three subclasses. Rather, it is the Find dialog minus
|
||||
the "Find Next" command, its execution function, and the
|
||||
default_command attribute needed in create_widgets. The other
|
||||
dialogs override attributes and methods, the latter to replace and
|
||||
add widgets.
|
||||
'''
|
||||
|
||||
title = "Search Dialog" # replace in subclasses
|
||||
icon = "Search"
|
||||
needwrapbutton = 1 # not in Find in Files
|
||||
|
||||
def __init__(self, root, engine):
|
||||
'''Initialize root, engine, and top attributes.
|
||||
|
||||
top (level widget): set in create_widgets() called from open().
|
||||
text (Text searched): set in open(), only used in subclasses().
|
||||
ent (ry): created in make_entry() called from create_entry().
|
||||
row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
|
||||
default_command: set in subclasses, used in create_widgers().
|
||||
|
||||
title (of dialog): class attribute, override in subclasses.
|
||||
icon (of dialog): ditto, use unclear if cannot minimize dialog.
|
||||
'''
|
||||
self.root = root
|
||||
self.engine = engine
|
||||
self.top = None
|
||||
|
||||
def open(self, text, searchphrase=None):
|
||||
"Make dialog visible on top of others and ready to use."
|
||||
self.text = text
|
||||
if not self.top:
|
||||
self.create_widgets()
|
||||
else:
|
||||
self.top.deiconify()
|
||||
self.top.tkraise()
|
||||
if searchphrase:
|
||||
self.ent.delete(0,"end")
|
||||
self.ent.insert("end",searchphrase)
|
||||
self.ent.focus_set()
|
||||
self.ent.selection_range(0, "end")
|
||||
self.ent.icursor(0)
|
||||
self.top.grab_set()
|
||||
|
||||
def close(self, event=None):
|
||||
"Put dialog away for later use."
|
||||
if self.top:
|
||||
self.top.grab_release()
|
||||
self.top.withdraw()
|
||||
|
||||
def create_widgets(self):
|
||||
'''Create basic 3 row x 3 col search (find) dialog.
|
||||
|
||||
Other dialogs override subsidiary create_x methods as needed.
|
||||
Replace and Find-in-Files add another entry row.
|
||||
'''
|
||||
top = Toplevel(self.root)
|
||||
top.bind("<Return>", self.default_command)
|
||||
top.bind("<Escape>", self.close)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.wm_title(self.title)
|
||||
top.wm_iconname(self.icon)
|
||||
self.top = top
|
||||
|
||||
self.row = 0
|
||||
self.top.grid_columnconfigure(0, pad=2, weight=0)
|
||||
self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
|
||||
|
||||
self.create_entries() # row 0 (and maybe 1), cols 0, 1
|
||||
self.create_option_buttons() # next row, cols 0, 1
|
||||
self.create_other_buttons() # next row, cols 0, 1
|
||||
self.create_command_buttons() # col 2, all rows
|
||||
|
||||
def make_entry(self, label_text, var):
|
||||
'''Return (entry, label), .
|
||||
|
||||
entry - gridded labeled Entry for text entry.
|
||||
label - Label widget, returned for testing.
|
||||
'''
|
||||
label = Label(self.top, text=label_text)
|
||||
label.grid(row=self.row, column=0, sticky="nw")
|
||||
entry = Entry(self.top, textvariable=var, exportselection=0)
|
||||
entry.grid(row=self.row, column=1, sticky="nwe")
|
||||
self.row = self.row + 1
|
||||
return entry, label
|
||||
|
||||
def create_entries(self):
|
||||
"Create one or more entry lines with make_entry."
|
||||
self.ent = self.make_entry("Find:", self.engine.patvar)[0]
|
||||
|
||||
def make_frame(self,labeltext=None):
|
||||
'''Return (frame, label).
|
||||
|
||||
frame - gridded labeled Frame for option or other buttons.
|
||||
label - Label widget, returned for testing.
|
||||
'''
|
||||
if labeltext:
|
||||
label = Label(self.top, text=labeltext)
|
||||
label.grid(row=self.row, column=0, sticky="nw")
|
||||
else:
|
||||
label = ''
|
||||
frame = Frame(self.top)
|
||||
frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
|
||||
self.row = self.row + 1
|
||||
return frame, label
|
||||
|
||||
def create_option_buttons(self):
|
||||
'''Return (filled frame, options) for testing.
|
||||
|
||||
Options is a list of SearchEngine booleanvar, label pairs.
|
||||
A gridded frame from make_frame is filled with a Checkbutton
|
||||
for each pair, bound to the var, with the corresponding label.
|
||||
'''
|
||||
frame = self.make_frame("Options")[0]
|
||||
engine = self.engine
|
||||
options = [(engine.revar, "Regular expression"),
|
||||
(engine.casevar, "Match case"),
|
||||
(engine.wordvar, "Whole word")]
|
||||
if self.needwrapbutton:
|
||||
options.append((engine.wrapvar, "Wrap around"))
|
||||
for var, label in options:
|
||||
btn = Checkbutton(frame, anchor="w", variable=var, text=label)
|
||||
btn.pack(side="left", fill="both")
|
||||
if var.get():
|
||||
btn.select()
|
||||
return frame, options
|
||||
|
||||
def create_other_buttons(self):
|
||||
'''Return (frame, others) for testing.
|
||||
|
||||
Others is a list of value, label pairs.
|
||||
A gridded frame from make_frame is filled with radio buttons.
|
||||
'''
|
||||
frame = self.make_frame("Direction")[0]
|
||||
var = self.engine.backvar
|
||||
others = [(1, 'Up'), (0, 'Down')]
|
||||
for val, label in others:
|
||||
btn = Radiobutton(frame, anchor="w",
|
||||
variable=var, value=val, text=label)
|
||||
btn.pack(side="left", fill="both")
|
||||
if var.get() == val:
|
||||
btn.select()
|
||||
return frame, others
|
||||
|
||||
def make_button(self, label, command, isdef=0):
|
||||
"Return command button gridded in command frame."
|
||||
b = Button(self.buttonframe,
|
||||
text=label, command=command,
|
||||
default=isdef and "active" or "normal")
|
||||
cols,rows=self.buttonframe.grid_size()
|
||||
b.grid(pady=1,row=rows,column=0,sticky="ew")
|
||||
self.buttonframe.grid(rowspan=rows+1)
|
||||
return b
|
||||
|
||||
def create_command_buttons(self):
|
||||
"Place buttons in vertical command frame gridded on right."
|
||||
f = self.buttonframe = Frame(self.top)
|
||||
f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
|
||||
|
||||
b = self.make_button("close", self.close)
|
||||
b.lower()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main(
|
||||
'idlelib.idle_test.test_searchdialogbase', verbosity=2)
|
||||
233
Darwin/lib/python3.5/idlelib/SearchEngine.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
'''Define SearchEngine for search dialogs.'''
|
||||
import re
|
||||
from tkinter import StringVar, BooleanVar, TclError
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
|
||||
def get(root):
|
||||
'''Return the singleton SearchEngine instance for the process.
|
||||
|
||||
The single SearchEngine saves settings between dialog instances.
|
||||
If there is not a SearchEngine already, make one.
|
||||
'''
|
||||
if not hasattr(root, "_searchengine"):
|
||||
root._searchengine = SearchEngine(root)
|
||||
# This creates a cycle that persists until root is deleted.
|
||||
return root._searchengine
|
||||
|
||||
class SearchEngine:
|
||||
"""Handles searching a text widget for Find, Replace, and Grep."""
|
||||
|
||||
def __init__(self, root):
|
||||
'''Initialize Variables that save search state.
|
||||
|
||||
The dialogs bind these to the UI elements present in the dialogs.
|
||||
'''
|
||||
self.root = root # need for report_error()
|
||||
self.patvar = StringVar(root, '') # search pattern
|
||||
self.revar = BooleanVar(root, False) # regular expression?
|
||||
self.casevar = BooleanVar(root, False) # match case?
|
||||
self.wordvar = BooleanVar(root, False) # match whole word?
|
||||
self.wrapvar = BooleanVar(root, True) # wrap around buffer?
|
||||
self.backvar = BooleanVar(root, False) # search backwards?
|
||||
|
||||
# Access methods
|
||||
|
||||
def getpat(self):
|
||||
return self.patvar.get()
|
||||
|
||||
def setpat(self, pat):
|
||||
self.patvar.set(pat)
|
||||
|
||||
def isre(self):
|
||||
return self.revar.get()
|
||||
|
||||
def iscase(self):
|
||||
return self.casevar.get()
|
||||
|
||||
def isword(self):
|
||||
return self.wordvar.get()
|
||||
|
||||
def iswrap(self):
|
||||
return self.wrapvar.get()
|
||||
|
||||
def isback(self):
|
||||
return self.backvar.get()
|
||||
|
||||
# Higher level access methods
|
||||
|
||||
def setcookedpat(self, pat):
|
||||
"Set pattern after escaping if re."
|
||||
# called only in SearchDialog.py: 66
|
||||
if self.isre():
|
||||
pat = re.escape(pat)
|
||||
self.setpat(pat)
|
||||
|
||||
def getcookedpat(self):
|
||||
pat = self.getpat()
|
||||
if not self.isre(): # if True, see setcookedpat
|
||||
pat = re.escape(pat)
|
||||
if self.isword():
|
||||
pat = r"\b%s\b" % pat
|
||||
return pat
|
||||
|
||||
def getprog(self):
|
||||
"Return compiled cooked search pattern."
|
||||
pat = self.getpat()
|
||||
if not pat:
|
||||
self.report_error(pat, "Empty regular expression")
|
||||
return None
|
||||
pat = self.getcookedpat()
|
||||
flags = 0
|
||||
if not self.iscase():
|
||||
flags = flags | re.IGNORECASE
|
||||
try:
|
||||
prog = re.compile(pat, flags)
|
||||
except re.error as what:
|
||||
args = what.args
|
||||
msg = args[0]
|
||||
col = args[1] if len(args) >= 2 else -1
|
||||
self.report_error(pat, msg, col)
|
||||
return None
|
||||
return prog
|
||||
|
||||
def report_error(self, pat, msg, col=-1):
|
||||
# Derived class could override this with something fancier
|
||||
msg = "Error: " + str(msg)
|
||||
if pat:
|
||||
msg = msg + "\nPattern: " + str(pat)
|
||||
if col >= 0:
|
||||
msg = msg + "\nOffset: " + str(col)
|
||||
tkMessageBox.showerror("Regular expression error",
|
||||
msg, master=self.root)
|
||||
|
||||
def search_text(self, text, prog=None, ok=0):
|
||||
'''Return (lineno, matchobj) or None for forward/backward search.
|
||||
|
||||
This function calls the right function with the right arguments.
|
||||
It directly return the result of that call.
|
||||
|
||||
Text is a text widget. Prog is a precompiled pattern.
|
||||
The ok parameter is a bit complicated as it has two effects.
|
||||
|
||||
If there is a selection, the search begin at either end,
|
||||
depending on the direction setting and ok, with ok meaning that
|
||||
the search starts with the selection. Otherwise, search begins
|
||||
at the insert mark.
|
||||
|
||||
To aid progress, the search functions do not return an empty
|
||||
match at the starting position unless ok is True.
|
||||
'''
|
||||
|
||||
if not prog:
|
||||
prog = self.getprog()
|
||||
if not prog:
|
||||
return None # Compilation failed -- stop
|
||||
wrap = self.wrapvar.get()
|
||||
first, last = get_selection(text)
|
||||
if self.isback():
|
||||
if ok:
|
||||
start = last
|
||||
else:
|
||||
start = first
|
||||
line, col = get_line_col(start)
|
||||
res = self.search_backward(text, prog, line, col, wrap, ok)
|
||||
else:
|
||||
if ok:
|
||||
start = first
|
||||
else:
|
||||
start = last
|
||||
line, col = get_line_col(start)
|
||||
res = self.search_forward(text, prog, line, col, wrap, ok)
|
||||
return res
|
||||
|
||||
def search_forward(self, text, prog, line, col, wrap, ok=0):
|
||||
wrapped = 0
|
||||
startline = line
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
while chars:
|
||||
m = prog.search(chars[:-1], col)
|
||||
if m:
|
||||
if ok or m.end() > col:
|
||||
return line, m
|
||||
line = line + 1
|
||||
if wrapped and line > startline:
|
||||
break
|
||||
col = 0
|
||||
ok = 1
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
if not chars and wrap:
|
||||
wrapped = 1
|
||||
wrap = 0
|
||||
line = 1
|
||||
chars = text.get("1.0", "2.0")
|
||||
return None
|
||||
|
||||
def search_backward(self, text, prog, line, col, wrap, ok=0):
|
||||
wrapped = 0
|
||||
startline = line
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
while 1:
|
||||
m = search_reverse(prog, chars[:-1], col)
|
||||
if m:
|
||||
if ok or m.start() < col:
|
||||
return line, m
|
||||
line = line - 1
|
||||
if wrapped and line < startline:
|
||||
break
|
||||
ok = 1
|
||||
if line <= 0:
|
||||
if not wrap:
|
||||
break
|
||||
wrapped = 1
|
||||
wrap = 0
|
||||
pos = text.index("end-1c")
|
||||
line, col = map(int, pos.split("."))
|
||||
chars = text.get("%d.0" % line, "%d.0" % (line+1))
|
||||
col = len(chars) - 1
|
||||
return None
|
||||
|
||||
def search_reverse(prog, chars, col):
|
||||
'''Search backwards and return an re match object or None.
|
||||
|
||||
This is done by searching forwards until there is no match.
|
||||
Prog: compiled re object with a search method returning a match.
|
||||
Chars: line of text, without \\n.
|
||||
Col: stop index for the search; the limit for match.end().
|
||||
'''
|
||||
m = prog.search(chars)
|
||||
if not m:
|
||||
return None
|
||||
found = None
|
||||
i, j = m.span() # m.start(), m.end() == match slice indexes
|
||||
while i < col and j <= col:
|
||||
found = m
|
||||
if i == j:
|
||||
j = j+1
|
||||
m = prog.search(chars, j)
|
||||
if not m:
|
||||
break
|
||||
i, j = m.span()
|
||||
return found
|
||||
|
||||
def get_selection(text):
|
||||
'''Return tuple of 'line.col' indexes from selection or insert mark.
|
||||
'''
|
||||
try:
|
||||
first = text.index("sel.first")
|
||||
last = text.index("sel.last")
|
||||
except TclError:
|
||||
first = last = None
|
||||
if not first:
|
||||
first = text.index("insert")
|
||||
if not last:
|
||||
last = first
|
||||
return first, last
|
||||
|
||||
def get_line_col(index):
|
||||
'''Return (line, col) tuple of ints from 'line.col' string.'''
|
||||
line, col = map(int, index.split(".")) # Fails on invalid index
|
||||
return line, col
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False)
|
||||
152
Darwin/lib/python3.5/idlelib/StackViewer.py
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import os
|
||||
import sys
|
||||
import linecache
|
||||
import re
|
||||
import tkinter as tk
|
||||
|
||||
from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||
from idlelib.ObjectBrowser import ObjectTreeItem, make_objecttreeitem
|
||||
from idlelib.PyShell import PyShellFileList
|
||||
|
||||
def StackBrowser(root, flist=None, tb=None, top=None):
|
||||
if top is None:
|
||||
from tkinter import Toplevel
|
||||
top = Toplevel(root)
|
||||
sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = StackTreeItem(flist, tb)
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.expand()
|
||||
|
||||
class StackTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, flist=None, tb=None):
|
||||
self.flist = flist
|
||||
self.stack = self.get_stack(tb)
|
||||
self.text = self.get_exception()
|
||||
|
||||
def get_stack(self, tb):
|
||||
if tb is None:
|
||||
tb = sys.last_traceback
|
||||
stack = []
|
||||
if tb and tb.tb_frame is None:
|
||||
tb = tb.tb_next
|
||||
while tb is not None:
|
||||
stack.append((tb.tb_frame, tb.tb_lineno))
|
||||
tb = tb.tb_next
|
||||
return stack
|
||||
|
||||
def get_exception(self):
|
||||
type = sys.last_type
|
||||
value = sys.last_value
|
||||
if hasattr(type, "__name__"):
|
||||
type = type.__name__
|
||||
s = str(type)
|
||||
if value is not None:
|
||||
s = s + ": " + str(value)
|
||||
return s
|
||||
|
||||
def GetText(self):
|
||||
return self.text
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for info in self.stack:
|
||||
item = FrameTreeItem(info, self.flist)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
class FrameTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, info, flist):
|
||||
self.info = info
|
||||
self.flist = flist
|
||||
|
||||
def GetText(self):
|
||||
frame, lineno = self.info
|
||||
try:
|
||||
modname = frame.f_globals["__name__"]
|
||||
except:
|
||||
modname = "?"
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
funcname = code.co_name
|
||||
sourceline = linecache.getline(filename, lineno)
|
||||
sourceline = sourceline.strip()
|
||||
if funcname in ("?", "", None):
|
||||
item = "%s, line %d: %s" % (modname, lineno, sourceline)
|
||||
else:
|
||||
item = "%s.%s(...), line %d: %s" % (modname, funcname,
|
||||
lineno, sourceline)
|
||||
return item
|
||||
|
||||
def GetSubList(self):
|
||||
frame, lineno = self.info
|
||||
sublist = []
|
||||
if frame.f_globals is not frame.f_locals:
|
||||
item = VariablesTreeItem("<locals>", frame.f_locals, self.flist)
|
||||
sublist.append(item)
|
||||
item = VariablesTreeItem("<globals>", frame.f_globals, self.flist)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if self.flist:
|
||||
frame, lineno = self.info
|
||||
filename = frame.f_code.co_filename
|
||||
if os.path.isfile(filename):
|
||||
self.flist.gotofileline(filename, lineno)
|
||||
|
||||
class VariablesTreeItem(ObjectTreeItem):
|
||||
|
||||
def GetText(self):
|
||||
return self.labeltext
|
||||
|
||||
def GetLabelText(self):
|
||||
return None
|
||||
|
||||
def IsExpandable(self):
|
||||
return len(self.object) > 0
|
||||
|
||||
def keys(self):
|
||||
return list(self.object.keys())
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for key in self.keys():
|
||||
try:
|
||||
value = self.object[key]
|
||||
except KeyError:
|
||||
continue
|
||||
def setfunction(value, key=key, object=self.object):
|
||||
object[key] = value
|
||||
item = make_objecttreeitem(key + " =", value, setfunction)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def _stack_viewer(parent):
|
||||
root = tk.Tk()
|
||||
root.title("Test StackViewer")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
flist = PyShellFileList(root)
|
||||
try: # to obtain a traceback object
|
||||
intentional_name_error
|
||||
except NameError:
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
|
||||
# inject stack trace to sys
|
||||
sys.last_type = exc_type
|
||||
sys.last_value = exc_value
|
||||
sys.last_traceback = exc_tb
|
||||
|
||||
StackBrowser(root, flist=flist, top=root, tb=exc_tb)
|
||||
|
||||
# restore sys to original state
|
||||
del sys.last_type
|
||||
del sys.last_value
|
||||
del sys.last_traceback
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_stack_viewer)
|
||||
210
Darwin/lib/python3.5/idlelib/TODO.txt
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
Original IDLE todo, much of it now outdated:
|
||||
============================================
|
||||
TO DO:
|
||||
|
||||
- improve debugger:
|
||||
- manage breakpoints globally, allow bp deletion, tbreak, cbreak etc.
|
||||
- real object browser
|
||||
- help on how to use it (a simple help button will do wonders)
|
||||
- performance? (updates of large sets of locals are slow)
|
||||
- better integration of "debug module"
|
||||
- debugger should be global resource (attached to flist, not to shell)
|
||||
- fix the stupid bug where you need to step twice
|
||||
- display class name in stack viewer entries for methods
|
||||
- suppress tracing through IDLE internals (e.g. print) DONE
|
||||
- add a button to suppress through a specific module or class or method
|
||||
- more object inspection to stack viewer, e.g. to view all array items
|
||||
- insert the initial current directory into sys.path DONE
|
||||
- default directory attribute for each window instead of only for windows
|
||||
that have an associated filename
|
||||
- command expansion from keywords, module contents, other buffers, etc.
|
||||
- "Recent documents" menu item DONE
|
||||
- Filter region command
|
||||
- Optional horizontal scroll bar
|
||||
- more Emacsisms:
|
||||
- ^K should cut to buffer
|
||||
- M-[, M-] to move by paragraphs
|
||||
- incremental search?
|
||||
- search should indicate wrap-around in some way
|
||||
- restructure state sensitive code to avoid testing flags all the time
|
||||
- persistent user state (e.g. window and cursor positions, bindings)
|
||||
- make backups when saving
|
||||
- check file mtimes at various points
|
||||
- Pluggable interface with RCS/CVS/Perforce/Clearcase
|
||||
- better help?
|
||||
- don't open second class browser on same module (nor second path browser)
|
||||
- unify class and path browsers
|
||||
- Need to define a standard way whereby one can determine one is running
|
||||
inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP)
|
||||
- Add more utility methods for use by extensions (a la get_selection)
|
||||
- Way to run command in totally separate interpreter (fork+os.system?) DONE
|
||||
- Way to find definition of fully-qualified name:
|
||||
In other words, select "UserDict.UserDict", hit some magic key and
|
||||
it loads up UserDict.py and finds the first def or class for UserDict.
|
||||
- need a way to force colorization on/off
|
||||
- need a way to force auto-indent on/off
|
||||
|
||||
Details:
|
||||
|
||||
- ^O (on Unix -- open-line) should honor autoindent
|
||||
- after paste, show end of pasted text
|
||||
- on Windows, should turn short filename to long filename (not only in argv!)
|
||||
(shouldn't this be done -- or undone -- by ntpath.normpath?)
|
||||
- new autoindent after colon even indents when the colon is in a comment!
|
||||
- sometimes forward slashes in pathname remain
|
||||
- sometimes star in window name remains in Windows menu
|
||||
- With unix bindings, ESC by itself is ignored
|
||||
- Sometimes for no apparent reason a selection from the cursor to the
|
||||
end of the command buffer appears, which is hard to get rid of
|
||||
because it stays when you are typing!
|
||||
- The Line/Col in the status bar can be wrong initially in PyShell DONE
|
||||
|
||||
Structural problems:
|
||||
|
||||
- too much knowledge in FileList about EditorWindow (for example)
|
||||
- should add some primitives for accessing the selection etc.
|
||||
to repeat cumbersome code over and over
|
||||
|
||||
======================================================================
|
||||
|
||||
Jeff Bauer suggests:
|
||||
|
||||
- Open Module doesn't appear to handle hierarchical packages.
|
||||
- Class browser should also allow hierarchical packages.
|
||||
- Open and Open Module could benefit from a history, DONE
|
||||
either command line style, or Microsoft recent-file
|
||||
style.
|
||||
- Add a Smalltalk-style inspector (i.e. Tkinspect)
|
||||
|
||||
The last suggestion is already a reality, but not yet
|
||||
integrated into IDLE. I use a module called inspector.py,
|
||||
that used to be available from python.org(?) It no longer
|
||||
appears to be in the contributed section, and the source
|
||||
has no author attribution.
|
||||
|
||||
In any case, the code is useful for visually navigating
|
||||
an object's attributes, including its container hierarchy.
|
||||
|
||||
>>> from inspector import Tkinspect
|
||||
>>> Tkinspect(None, myObject)
|
||||
|
||||
Tkinspect could probably be extended and refined to
|
||||
integrate better into IDLE.
|
||||
|
||||
======================================================================
|
||||
|
||||
Comparison to PTUI
|
||||
------------------
|
||||
|
||||
+ PTUI's help is better (HTML!)
|
||||
|
||||
+ PTUI can attach a shell to any module
|
||||
|
||||
+ PTUI has some more I/O commands:
|
||||
open multiple
|
||||
append
|
||||
examine (what's that?)
|
||||
|
||||
======================================================================
|
||||
|
||||
Notes after trying to run Grail
|
||||
-------------------------------
|
||||
|
||||
- Grail does stuff to sys.path based on sys.argv[0]; you must set
|
||||
sys.argv[0] to something decent first (it is normally set to the path of
|
||||
the idle script).
|
||||
|
||||
- Grail must be exec'ed in __main__ because that's imported by some
|
||||
other parts of Grail.
|
||||
|
||||
- Grail uses a module called History and so does idle :-(
|
||||
|
||||
======================================================================
|
||||
|
||||
Robin Friedrich's items:
|
||||
|
||||
Things I'd like to see:
|
||||
- I'd like support for shift-click extending the selection. There's a
|
||||
bug now that it doesn't work the first time you try it.
|
||||
- Printing is needed. How hard can that be on Windows? FIRST CUT DONE
|
||||
- The python-mode trick of autoindenting a line with <tab> is neat and
|
||||
very handy.
|
||||
- (someday) a spellchecker for docstrings and comments.
|
||||
- a pagedown/up command key which moves to next class/def statement (top
|
||||
level)
|
||||
- split window capability
|
||||
- DnD text relocation/copying
|
||||
|
||||
Things I don't want to see.
|
||||
- line numbers... will probably slow things down way too much.
|
||||
- Please use another icon for the tree browser leaf. The small snake
|
||||
isn't cutting it.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
- Customizable views (multi-window or multi-pane). (Markus Gritsch)
|
||||
|
||||
- Being able to double click (maybe double right click) on a callable
|
||||
object in the editor which shows the source of the object, if
|
||||
possible. (Gerrit Holl)
|
||||
|
||||
- Hooks into the guts, like in Emacs. (Mike Romberg)
|
||||
|
||||
- Sharing the editor with a remote tutor. (Martijn Faassen)
|
||||
|
||||
- Multiple views on the same file. (Tony J Ibbs)
|
||||
|
||||
- Store breakpoints in a global (per-project) database (GvR); Dirk
|
||||
Heise adds: save some space-trimmed context and search around when
|
||||
reopening a file that might have been edited by someone else.
|
||||
|
||||
- Capture menu events in extensions without changing the IDLE source.
|
||||
(Matthias Barmeier)
|
||||
|
||||
- Use overlapping panels (a "notebook" in MFC terms I think) for info
|
||||
that doesn't need to be accessible simultaneously (e.g. HTML source
|
||||
and output). Use multi-pane windows for info that does need to be
|
||||
shown together (e.g. class browser and source). (Albert Brandl)
|
||||
|
||||
- A project should invisibly track all symbols, for instant search,
|
||||
replace and cross-ref. Projects should be allowed to span multiple
|
||||
directories, hosts, etc. Project management files are placed in a
|
||||
directory you specify. A global mapping between project names and
|
||||
project directories should exist [not so sure --GvR]. (Tim Peters)
|
||||
|
||||
- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters)
|
||||
|
||||
- Python Shell should behave more like a "shell window" as users know
|
||||
it -- i.e. you can only edit the current command, and the cursor can't
|
||||
escape from the command area. (Albert Brandl)
|
||||
|
||||
- Set X11 class to "idle/Idle", set icon and title to something
|
||||
beginning with "idle" -- for window manangers. (Randall Hopper)
|
||||
|
||||
- Config files editable through a preferences dialog. (me) DONE
|
||||
|
||||
- Config files still editable outside the preferences dialog.
|
||||
(Randall Hopper) DONE
|
||||
|
||||
- When you're editing a command in PyShell, and there are only blank
|
||||
lines below the cursor, hitting Return should ignore or delete those
|
||||
blank lines rather than deciding you're not on the last line. (me)
|
||||
|
||||
- Run command (F5 c.s.) should be more like Pythonwin's Run -- a
|
||||
dialog with options to give command line arguments, run the debugger,
|
||||
etc. (me)
|
||||
|
||||
- Shouldn't be able to delete part of the prompt (or any text before
|
||||
it) in the PyShell. (Martijn Faassen) DONE
|
||||
|
||||
- Emacs style auto-fill (also smart about comments and strings).
|
||||
(Jeremy Hylton)
|
||||
|
||||
- Output of Run Script should go to a separate output window, not to
|
||||
the shell window. Output of separate runs should all go to the same
|
||||
window but clearly delimited. (David Scherer) REJECT FIRST, LATTER DONE
|
||||
|
||||
- GUI form designer to kick VB's butt. (Robert Geiger) THAT'S NOT IDLE
|
||||
|
||||
- Printing! Possibly via generation of PDF files which the user must
|
||||
then send to the printer separately. (Dinu Gherman) FIRST CUT
|
||||
97
Darwin/lib/python3.5/idlelib/ToolTip.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# general purpose 'tooltip' routines - currently unused in idlefork
|
||||
# (although the 'calltips' extension is partly based on this code)
|
||||
# may be useful for some purposes in (or almost in ;) the current project scope
|
||||
# Ideas gleaned from PySol
|
||||
|
||||
from tkinter import *
|
||||
|
||||
class ToolTipBase:
|
||||
|
||||
def __init__(self, button):
|
||||
self.button = button
|
||||
self.tipwindow = None
|
||||
self.id = None
|
||||
self.x = self.y = 0
|
||||
self._id1 = self.button.bind("<Enter>", self.enter)
|
||||
self._id2 = self.button.bind("<Leave>", self.leave)
|
||||
self._id3 = self.button.bind("<ButtonPress>", self.leave)
|
||||
|
||||
def enter(self, event=None):
|
||||
self.schedule()
|
||||
|
||||
def leave(self, event=None):
|
||||
self.unschedule()
|
||||
self.hidetip()
|
||||
|
||||
def schedule(self):
|
||||
self.unschedule()
|
||||
self.id = self.button.after(1500, self.showtip)
|
||||
|
||||
def unschedule(self):
|
||||
id = self.id
|
||||
self.id = None
|
||||
if id:
|
||||
self.button.after_cancel(id)
|
||||
|
||||
def showtip(self):
|
||||
if self.tipwindow:
|
||||
return
|
||||
# The tip window must be completely outside the button;
|
||||
# otherwise when the mouse enters the tip window we get
|
||||
# a leave event and it disappears, and then we get an enter
|
||||
# event and it reappears, and so on forever :-(
|
||||
x = self.button.winfo_rootx() + 20
|
||||
y = self.button.winfo_rooty() + self.button.winfo_height() + 1
|
||||
self.tipwindow = tw = Toplevel(self.button)
|
||||
tw.wm_overrideredirect(1)
|
||||
tw.wm_geometry("+%d+%d" % (x, y))
|
||||
self.showcontents()
|
||||
|
||||
def showcontents(self, text="Your text here"):
|
||||
# Override this in derived class
|
||||
label = Label(self.tipwindow, text=text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1)
|
||||
label.pack()
|
||||
|
||||
def hidetip(self):
|
||||
tw = self.tipwindow
|
||||
self.tipwindow = None
|
||||
if tw:
|
||||
tw.destroy()
|
||||
|
||||
class ToolTip(ToolTipBase):
|
||||
def __init__(self, button, text):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.text = text
|
||||
def showcontents(self):
|
||||
ToolTipBase.showcontents(self, self.text)
|
||||
|
||||
class ListboxToolTip(ToolTipBase):
|
||||
def __init__(self, button, items):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.items = items
|
||||
def showcontents(self):
|
||||
listbox = Listbox(self.tipwindow, background="#ffffe0")
|
||||
listbox.pack()
|
||||
for item in self.items:
|
||||
listbox.insert(END, item)
|
||||
|
||||
def _tooltip(parent):
|
||||
root = Tk()
|
||||
root.title("Test tooltip")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
label = Label(root, text="Place your mouse over buttons")
|
||||
label.pack()
|
||||
button1 = Button(root, text="Button 1")
|
||||
button2 = Button(root, text="Button 2")
|
||||
button1.pack()
|
||||
button2.pack()
|
||||
ToolTip(button1, "This is tooltip text for button1.")
|
||||
ListboxToolTip(button2, ["This is","multiple line",
|
||||
"tooltip text","for button2"])
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_tooltip)
|
||||
466
Darwin/lib/python3.5/idlelib/TreeWidget.py
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
# XXX TO DO:
|
||||
# - popup menu
|
||||
# - support partial or total redisplay
|
||||
# - key bindings (instead of quick-n-dirty bindings on Canvas):
|
||||
# - up/down arrow keys to move focus around
|
||||
# - ditto for page up/down, home/end
|
||||
# - left/right arrows to expand/collapse & move out/in
|
||||
# - more doc strings
|
||||
# - add icons for "file", "module", "class", "method"; better "python" icon
|
||||
# - callback for selection???
|
||||
# - multiple-item selection
|
||||
# - tooltips
|
||||
# - redo geometry without magic numbers
|
||||
# - keep track of object ids to allow more careful cleaning
|
||||
# - optimize tree redraw after expand of subnode
|
||||
|
||||
import os
|
||||
from tkinter import *
|
||||
|
||||
from idlelib import ZoomHeight
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
ICONDIR = "Icons"
|
||||
|
||||
# Look for Icons subdirectory in the same directory as this module
|
||||
try:
|
||||
_icondir = os.path.join(os.path.dirname(__file__), ICONDIR)
|
||||
except NameError:
|
||||
_icondir = ICONDIR
|
||||
if os.path.isdir(_icondir):
|
||||
ICONDIR = _icondir
|
||||
elif not os.path.isdir(ICONDIR):
|
||||
raise RuntimeError("can't find icon directory (%r)" % (ICONDIR,))
|
||||
|
||||
def listicons(icondir=ICONDIR):
|
||||
"""Utility to display the available icons."""
|
||||
root = Tk()
|
||||
import glob
|
||||
list = glob.glob(os.path.join(icondir, "*.gif"))
|
||||
list.sort()
|
||||
images = []
|
||||
row = column = 0
|
||||
for file in list:
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
image = PhotoImage(file=file, master=root)
|
||||
images.append(image)
|
||||
label = Label(root, image=image, bd=1, relief="raised")
|
||||
label.grid(row=row, column=column)
|
||||
label = Label(root, text=name)
|
||||
label.grid(row=row+1, column=column)
|
||||
column = column + 1
|
||||
if column >= 10:
|
||||
row = row+2
|
||||
column = 0
|
||||
root.images = images
|
||||
|
||||
|
||||
class TreeNode:
|
||||
|
||||
def __init__(self, canvas, parent, item):
|
||||
self.canvas = canvas
|
||||
self.parent = parent
|
||||
self.item = item
|
||||
self.state = 'collapsed'
|
||||
self.selected = False
|
||||
self.children = []
|
||||
self.x = self.y = None
|
||||
self.iconimages = {} # cache of PhotoImage instances for icons
|
||||
|
||||
def destroy(self):
|
||||
for c in self.children[:]:
|
||||
self.children.remove(c)
|
||||
c.destroy()
|
||||
self.parent = None
|
||||
|
||||
def geticonimage(self, name):
|
||||
try:
|
||||
return self.iconimages[name]
|
||||
except KeyError:
|
||||
pass
|
||||
file, ext = os.path.splitext(name)
|
||||
ext = ext or ".gif"
|
||||
fullname = os.path.join(ICONDIR, file + ext)
|
||||
image = PhotoImage(master=self.canvas, file=fullname)
|
||||
self.iconimages[name] = image
|
||||
return image
|
||||
|
||||
def select(self, event=None):
|
||||
if self.selected:
|
||||
return
|
||||
self.deselectall()
|
||||
self.selected = True
|
||||
self.canvas.delete(self.image_id)
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
|
||||
def deselect(self, event=None):
|
||||
if not self.selected:
|
||||
return
|
||||
self.selected = False
|
||||
self.canvas.delete(self.image_id)
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
|
||||
def deselectall(self):
|
||||
if self.parent:
|
||||
self.parent.deselectall()
|
||||
else:
|
||||
self.deselecttree()
|
||||
|
||||
def deselecttree(self):
|
||||
if self.selected:
|
||||
self.deselect()
|
||||
for child in self.children:
|
||||
child.deselecttree()
|
||||
|
||||
def flip(self, event=None):
|
||||
if self.state == 'expanded':
|
||||
self.collapse()
|
||||
else:
|
||||
self.expand()
|
||||
self.item.OnDoubleClick()
|
||||
return "break"
|
||||
|
||||
def expand(self, event=None):
|
||||
if not self.item._IsExpandable():
|
||||
return
|
||||
if self.state != 'expanded':
|
||||
self.state = 'expanded'
|
||||
self.update()
|
||||
self.view()
|
||||
|
||||
def collapse(self, event=None):
|
||||
if self.state != 'collapsed':
|
||||
self.state = 'collapsed'
|
||||
self.update()
|
||||
|
||||
def view(self):
|
||||
top = self.y - 2
|
||||
bottom = self.lastvisiblechild().y + 17
|
||||
height = bottom - top
|
||||
visible_top = self.canvas.canvasy(0)
|
||||
visible_height = self.canvas.winfo_height()
|
||||
visible_bottom = self.canvas.canvasy(visible_height)
|
||||
if visible_top <= top and bottom <= visible_bottom:
|
||||
return
|
||||
x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
|
||||
if top >= visible_top and height <= visible_height:
|
||||
fraction = top + height - visible_height
|
||||
else:
|
||||
fraction = top
|
||||
fraction = float(fraction) / y1
|
||||
self.canvas.yview_moveto(fraction)
|
||||
|
||||
def lastvisiblechild(self):
|
||||
if self.children and self.state == 'expanded':
|
||||
return self.children[-1].lastvisiblechild()
|
||||
else:
|
||||
return self
|
||||
|
||||
def update(self):
|
||||
if self.parent:
|
||||
self.parent.update()
|
||||
else:
|
||||
oldcursor = self.canvas['cursor']
|
||||
self.canvas['cursor'] = "watch"
|
||||
self.canvas.update()
|
||||
self.canvas.delete(ALL) # XXX could be more subtle
|
||||
self.draw(7, 2)
|
||||
x0, y0, x1, y1 = self.canvas.bbox(ALL)
|
||||
self.canvas.configure(scrollregion=(0, 0, x1, y1))
|
||||
self.canvas['cursor'] = oldcursor
|
||||
|
||||
def draw(self, x, y):
|
||||
# XXX This hard-codes too many geometry constants!
|
||||
dy = 20
|
||||
self.x, self.y = x, y
|
||||
self.drawicon()
|
||||
self.drawtext()
|
||||
if self.state != 'expanded':
|
||||
return y + dy
|
||||
# draw children
|
||||
if not self.children:
|
||||
sublist = self.item._GetSubList()
|
||||
if not sublist:
|
||||
# _IsExpandable() was mistaken; that's allowed
|
||||
return y+17
|
||||
for item in sublist:
|
||||
child = self.__class__(self.canvas, self, item)
|
||||
self.children.append(child)
|
||||
cx = x+20
|
||||
cy = y + dy
|
||||
cylast = 0
|
||||
for child in self.children:
|
||||
cylast = cy
|
||||
self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
|
||||
cy = child.draw(cx, cy)
|
||||
if child.item._IsExpandable():
|
||||
if child.state == 'expanded':
|
||||
iconname = "minusnode"
|
||||
callback = child.collapse
|
||||
else:
|
||||
iconname = "plusnode"
|
||||
callback = child.expand
|
||||
image = self.geticonimage(iconname)
|
||||
id = self.canvas.create_image(x+9, cylast+7, image=image)
|
||||
# XXX This leaks bindings until canvas is deleted:
|
||||
self.canvas.tag_bind(id, "<1>", callback)
|
||||
self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
|
||||
id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
|
||||
##stipple="gray50", # XXX Seems broken in Tk 8.0.x
|
||||
fill="gray50")
|
||||
self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
|
||||
return cy
|
||||
|
||||
def drawicon(self):
|
||||
if self.selected:
|
||||
imagename = (self.item.GetSelectedIconName() or
|
||||
self.item.GetIconName() or
|
||||
"openfolder")
|
||||
else:
|
||||
imagename = self.item.GetIconName() or "folder"
|
||||
image = self.geticonimage(imagename)
|
||||
id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
|
||||
self.image_id = id
|
||||
self.canvas.tag_bind(id, "<1>", self.select)
|
||||
self.canvas.tag_bind(id, "<Double-1>", self.flip)
|
||||
|
||||
def drawtext(self):
|
||||
textx = self.x+20-1
|
||||
texty = self.y-4
|
||||
labeltext = self.item.GetLabelText()
|
||||
if labeltext:
|
||||
id = self.canvas.create_text(textx, texty, anchor="nw",
|
||||
text=labeltext)
|
||||
self.canvas.tag_bind(id, "<1>", self.select)
|
||||
self.canvas.tag_bind(id, "<Double-1>", self.flip)
|
||||
x0, y0, x1, y1 = self.canvas.bbox(id)
|
||||
textx = max(x1, 200) + 10
|
||||
text = self.item.GetText() or "<no text>"
|
||||
try:
|
||||
self.entry
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
self.edit_finish()
|
||||
try:
|
||||
self.label
|
||||
except AttributeError:
|
||||
# padding carefully selected (on Windows) to match Entry widget:
|
||||
self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
|
||||
theme = idleConf.GetOption('main','Theme','name')
|
||||
if self.selected:
|
||||
self.label.configure(idleConf.GetHighlight(theme, 'hilite'))
|
||||
else:
|
||||
self.label.configure(idleConf.GetHighlight(theme, 'normal'))
|
||||
id = self.canvas.create_window(textx, texty,
|
||||
anchor="nw", window=self.label)
|
||||
self.label.bind("<1>", self.select_or_edit)
|
||||
self.label.bind("<Double-1>", self.flip)
|
||||
self.text_id = id
|
||||
|
||||
def select_or_edit(self, event=None):
|
||||
if self.selected and self.item.IsEditable():
|
||||
self.edit(event)
|
||||
else:
|
||||
self.select(event)
|
||||
|
||||
def edit(self, event=None):
|
||||
self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
|
||||
self.entry.insert(0, self.label['text'])
|
||||
self.entry.selection_range(0, END)
|
||||
self.entry.pack(ipadx=5)
|
||||
self.entry.focus_set()
|
||||
self.entry.bind("<Return>", self.edit_finish)
|
||||
self.entry.bind("<Escape>", self.edit_cancel)
|
||||
|
||||
def edit_finish(self, event=None):
|
||||
try:
|
||||
entry = self.entry
|
||||
del self.entry
|
||||
except AttributeError:
|
||||
return
|
||||
text = entry.get()
|
||||
entry.destroy()
|
||||
if text and text != self.item.GetText():
|
||||
self.item.SetText(text)
|
||||
text = self.item.GetText()
|
||||
self.label['text'] = text
|
||||
self.drawtext()
|
||||
self.canvas.focus_set()
|
||||
|
||||
def edit_cancel(self, event=None):
|
||||
try:
|
||||
entry = self.entry
|
||||
del self.entry
|
||||
except AttributeError:
|
||||
return
|
||||
entry.destroy()
|
||||
self.drawtext()
|
||||
self.canvas.focus_set()
|
||||
|
||||
|
||||
class TreeItem:
|
||||
|
||||
"""Abstract class representing tree items.
|
||||
|
||||
Methods should typically be overridden, otherwise a default action
|
||||
is used.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor. Do whatever you need to do."""
|
||||
|
||||
def GetText(self):
|
||||
"""Return text string to display."""
|
||||
|
||||
def GetLabelText(self):
|
||||
"""Return label text string to display in front of text (if any)."""
|
||||
|
||||
expandable = None
|
||||
|
||||
def _IsExpandable(self):
|
||||
"""Do not override! Called by TreeNode."""
|
||||
if self.expandable is None:
|
||||
self.expandable = self.IsExpandable()
|
||||
return self.expandable
|
||||
|
||||
def IsExpandable(self):
|
||||
"""Return whether there are subitems."""
|
||||
return 1
|
||||
|
||||
def _GetSubList(self):
|
||||
"""Do not override! Called by TreeNode."""
|
||||
if not self.IsExpandable():
|
||||
return []
|
||||
sublist = self.GetSubList()
|
||||
if not sublist:
|
||||
self.expandable = 0
|
||||
return sublist
|
||||
|
||||
def IsEditable(self):
|
||||
"""Return whether the item's text may be edited."""
|
||||
|
||||
def SetText(self, text):
|
||||
"""Change the item's text (if it is editable)."""
|
||||
|
||||
def GetIconName(self):
|
||||
"""Return name of icon to be displayed normally."""
|
||||
|
||||
def GetSelectedIconName(self):
|
||||
"""Return name of icon to be displayed when selected."""
|
||||
|
||||
def GetSubList(self):
|
||||
"""Return list of items forming sublist."""
|
||||
|
||||
def OnDoubleClick(self):
|
||||
"""Called on a double-click on the item."""
|
||||
|
||||
|
||||
# Example application
|
||||
|
||||
class FileTreeItem(TreeItem):
|
||||
|
||||
"""Example TreeItem subclass -- browse the file system."""
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def GetText(self):
|
||||
return os.path.basename(self.path) or self.path
|
||||
|
||||
def IsEditable(self):
|
||||
return os.path.basename(self.path) != ""
|
||||
|
||||
def SetText(self, text):
|
||||
newpath = os.path.dirname(self.path)
|
||||
newpath = os.path.join(newpath, text)
|
||||
if os.path.dirname(newpath) != os.path.dirname(self.path):
|
||||
return
|
||||
try:
|
||||
os.rename(self.path, newpath)
|
||||
self.path = newpath
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def GetIconName(self):
|
||||
if not self.IsExpandable():
|
||||
return "python" # XXX wish there was a "file" icon
|
||||
|
||||
def IsExpandable(self):
|
||||
return os.path.isdir(self.path)
|
||||
|
||||
def GetSubList(self):
|
||||
try:
|
||||
names = os.listdir(self.path)
|
||||
except OSError:
|
||||
return []
|
||||
names.sort(key = os.path.normcase)
|
||||
sublist = []
|
||||
for name in names:
|
||||
item = FileTreeItem(os.path.join(self.path, name))
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
|
||||
# A canvas widget with scroll bars and some useful bindings
|
||||
|
||||
class ScrolledCanvas:
|
||||
def __init__(self, master, **opts):
|
||||
if 'yscrollincrement' not in opts:
|
||||
opts['yscrollincrement'] = 17
|
||||
self.master = master
|
||||
self.frame = Frame(master)
|
||||
self.frame.rowconfigure(0, weight=1)
|
||||
self.frame.columnconfigure(0, weight=1)
|
||||
self.canvas = Canvas(self.frame, **opts)
|
||||
self.canvas.grid(row=0, column=0, sticky="nsew")
|
||||
self.vbar = Scrollbar(self.frame, name="vbar")
|
||||
self.vbar.grid(row=0, column=1, sticky="nse")
|
||||
self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal")
|
||||
self.hbar.grid(row=1, column=0, sticky="ews")
|
||||
self.canvas['yscrollcommand'] = self.vbar.set
|
||||
self.vbar['command'] = self.canvas.yview
|
||||
self.canvas['xscrollcommand'] = self.hbar.set
|
||||
self.hbar['command'] = self.canvas.xview
|
||||
self.canvas.bind("<Key-Prior>", self.page_up)
|
||||
self.canvas.bind("<Key-Next>", self.page_down)
|
||||
self.canvas.bind("<Key-Up>", self.unit_up)
|
||||
self.canvas.bind("<Key-Down>", self.unit_down)
|
||||
#if isinstance(master, Toplevel) or isinstance(master, Tk):
|
||||
self.canvas.bind("<Alt-Key-2>", self.zoom_height)
|
||||
self.canvas.focus_set()
|
||||
def page_up(self, event):
|
||||
self.canvas.yview_scroll(-1, "page")
|
||||
return "break"
|
||||
def page_down(self, event):
|
||||
self.canvas.yview_scroll(1, "page")
|
||||
return "break"
|
||||
def unit_up(self, event):
|
||||
self.canvas.yview_scroll(-1, "unit")
|
||||
return "break"
|
||||
def unit_down(self, event):
|
||||
self.canvas.yview_scroll(1, "unit")
|
||||
return "break"
|
||||
def zoom_height(self, event):
|
||||
ZoomHeight.zoom_height(self.master)
|
||||
return "break"
|
||||
|
||||
|
||||
def _tree_widget(parent):
|
||||
root = Tk()
|
||||
root.title("Test TreeWidget")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both", side=LEFT)
|
||||
item = FileTreeItem(os.getcwd())
|
||||
node = TreeNode(sc.canvas, None, item)
|
||||
node.expand()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_tree_widget)
|
||||
365
Darwin/lib/python3.5/idlelib/UndoDelegator.py
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
import string
|
||||
from tkinter import *
|
||||
|
||||
from idlelib.Delegator import Delegator
|
||||
|
||||
#$ event <<redo>>
|
||||
#$ win <Control-y>
|
||||
#$ unix <Alt-z>
|
||||
|
||||
#$ event <<undo>>
|
||||
#$ win <Control-z>
|
||||
#$ unix <Control-z>
|
||||
|
||||
#$ event <<dump-undo-state>>
|
||||
#$ win <Control-backslash>
|
||||
#$ unix <Control-backslash>
|
||||
|
||||
|
||||
class UndoDelegator(Delegator):
|
||||
|
||||
max_undo = 1000
|
||||
|
||||
def __init__(self):
|
||||
Delegator.__init__(self)
|
||||
self.reset_undo()
|
||||
|
||||
def setdelegate(self, delegate):
|
||||
if self.delegate is not None:
|
||||
self.unbind("<<undo>>")
|
||||
self.unbind("<<redo>>")
|
||||
self.unbind("<<dump-undo-state>>")
|
||||
Delegator.setdelegate(self, delegate)
|
||||
if delegate is not None:
|
||||
self.bind("<<undo>>", self.undo_event)
|
||||
self.bind("<<redo>>", self.redo_event)
|
||||
self.bind("<<dump-undo-state>>", self.dump_event)
|
||||
|
||||
def dump_event(self, event):
|
||||
from pprint import pprint
|
||||
pprint(self.undolist[:self.pointer])
|
||||
print("pointer:", self.pointer, end=' ')
|
||||
print("saved:", self.saved, end=' ')
|
||||
print("can_merge:", self.can_merge, end=' ')
|
||||
print("get_saved():", self.get_saved())
|
||||
pprint(self.undolist[self.pointer:])
|
||||
return "break"
|
||||
|
||||
def reset_undo(self):
|
||||
self.was_saved = -1
|
||||
self.pointer = 0
|
||||
self.undolist = []
|
||||
self.undoblock = 0 # or a CommandSequence instance
|
||||
self.set_saved(1)
|
||||
|
||||
def set_saved(self, flag):
|
||||
if flag:
|
||||
self.saved = self.pointer
|
||||
else:
|
||||
self.saved = -1
|
||||
self.can_merge = False
|
||||
self.check_saved()
|
||||
|
||||
def get_saved(self):
|
||||
return self.saved == self.pointer
|
||||
|
||||
saved_change_hook = None
|
||||
|
||||
def set_saved_change_hook(self, hook):
|
||||
self.saved_change_hook = hook
|
||||
|
||||
was_saved = -1
|
||||
|
||||
def check_saved(self):
|
||||
is_saved = self.get_saved()
|
||||
if is_saved != self.was_saved:
|
||||
self.was_saved = is_saved
|
||||
if self.saved_change_hook:
|
||||
self.saved_change_hook()
|
||||
|
||||
def insert(self, index, chars, tags=None):
|
||||
self.addcmd(InsertCommand(index, chars, tags))
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
self.addcmd(DeleteCommand(index1, index2))
|
||||
|
||||
# Clients should call undo_block_start() and undo_block_stop()
|
||||
# around a sequence of editing cmds to be treated as a unit by
|
||||
# undo & redo. Nested matching calls are OK, and the inner calls
|
||||
# then act like nops. OK too if no editing cmds, or only one
|
||||
# editing cmd, is issued in between: if no cmds, the whole
|
||||
# sequence has no effect; and if only one cmd, that cmd is entered
|
||||
# directly into the undo list, as if undo_block_xxx hadn't been
|
||||
# called. The intent of all that is to make this scheme easy
|
||||
# to use: all the client has to worry about is making sure each
|
||||
# _start() call is matched by a _stop() call.
|
||||
|
||||
def undo_block_start(self):
|
||||
if self.undoblock == 0:
|
||||
self.undoblock = CommandSequence()
|
||||
self.undoblock.bump_depth()
|
||||
|
||||
def undo_block_stop(self):
|
||||
if self.undoblock.bump_depth(-1) == 0:
|
||||
cmd = self.undoblock
|
||||
self.undoblock = 0
|
||||
if len(cmd) > 0:
|
||||
if len(cmd) == 1:
|
||||
# no need to wrap a single cmd
|
||||
cmd = cmd.getcmd(0)
|
||||
# this blk of cmds, or single cmd, has already
|
||||
# been done, so don't execute it again
|
||||
self.addcmd(cmd, 0)
|
||||
|
||||
def addcmd(self, cmd, execute=True):
|
||||
if execute:
|
||||
cmd.do(self.delegate)
|
||||
if self.undoblock != 0:
|
||||
self.undoblock.append(cmd)
|
||||
return
|
||||
if self.can_merge and self.pointer > 0:
|
||||
lastcmd = self.undolist[self.pointer-1]
|
||||
if lastcmd.merge(cmd):
|
||||
return
|
||||
self.undolist[self.pointer:] = [cmd]
|
||||
if self.saved > self.pointer:
|
||||
self.saved = -1
|
||||
self.pointer = self.pointer + 1
|
||||
if len(self.undolist) > self.max_undo:
|
||||
##print "truncating undo list"
|
||||
del self.undolist[0]
|
||||
self.pointer = self.pointer - 1
|
||||
if self.saved >= 0:
|
||||
self.saved = self.saved - 1
|
||||
self.can_merge = True
|
||||
self.check_saved()
|
||||
|
||||
def undo_event(self, event):
|
||||
if self.pointer == 0:
|
||||
self.bell()
|
||||
return "break"
|
||||
cmd = self.undolist[self.pointer - 1]
|
||||
cmd.undo(self.delegate)
|
||||
self.pointer = self.pointer - 1
|
||||
self.can_merge = False
|
||||
self.check_saved()
|
||||
return "break"
|
||||
|
||||
def redo_event(self, event):
|
||||
if self.pointer >= len(self.undolist):
|
||||
self.bell()
|
||||
return "break"
|
||||
cmd = self.undolist[self.pointer]
|
||||
cmd.redo(self.delegate)
|
||||
self.pointer = self.pointer + 1
|
||||
self.can_merge = False
|
||||
self.check_saved()
|
||||
return "break"
|
||||
|
||||
|
||||
class Command:
|
||||
|
||||
# Base class for Undoable commands
|
||||
|
||||
tags = None
|
||||
|
||||
def __init__(self, index1, index2, chars, tags=None):
|
||||
self.marks_before = {}
|
||||
self.marks_after = {}
|
||||
self.index1 = index1
|
||||
self.index2 = index2
|
||||
self.chars = chars
|
||||
if tags:
|
||||
self.tags = tags
|
||||
|
||||
def __repr__(self):
|
||||
s = self.__class__.__name__
|
||||
t = (self.index1, self.index2, self.chars, self.tags)
|
||||
if self.tags is None:
|
||||
t = t[:-1]
|
||||
return s + repr(t)
|
||||
|
||||
def do(self, text):
|
||||
pass
|
||||
|
||||
def redo(self, text):
|
||||
pass
|
||||
|
||||
def undo(self, text):
|
||||
pass
|
||||
|
||||
def merge(self, cmd):
|
||||
return 0
|
||||
|
||||
def save_marks(self, text):
|
||||
marks = {}
|
||||
for name in text.mark_names():
|
||||
if name != "insert" and name != "current":
|
||||
marks[name] = text.index(name)
|
||||
return marks
|
||||
|
||||
def set_marks(self, text, marks):
|
||||
for name, index in marks.items():
|
||||
text.mark_set(name, index)
|
||||
|
||||
|
||||
class InsertCommand(Command):
|
||||
|
||||
# Undoable insert command
|
||||
|
||||
def __init__(self, index1, chars, tags=None):
|
||||
Command.__init__(self, index1, None, chars, tags)
|
||||
|
||||
def do(self, text):
|
||||
self.marks_before = self.save_marks(text)
|
||||
self.index1 = text.index(self.index1)
|
||||
if text.compare(self.index1, ">", "end-1c"):
|
||||
# Insert before the final newline
|
||||
self.index1 = text.index("end-1c")
|
||||
text.insert(self.index1, self.chars, self.tags)
|
||||
self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
|
||||
self.marks_after = self.save_marks(text)
|
||||
##sys.__stderr__.write("do: %s\n" % self)
|
||||
|
||||
def redo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.insert(self.index1, self.chars, self.tags)
|
||||
self.set_marks(text, self.marks_after)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("redo: %s\n" % self)
|
||||
|
||||
def undo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.set_marks(text, self.marks_before)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("undo: %s\n" % self)
|
||||
|
||||
def merge(self, cmd):
|
||||
if self.__class__ is not cmd.__class__:
|
||||
return False
|
||||
if self.index2 != cmd.index1:
|
||||
return False
|
||||
if self.tags != cmd.tags:
|
||||
return False
|
||||
if len(cmd.chars) != 1:
|
||||
return False
|
||||
if self.chars and \
|
||||
self.classify(self.chars[-1]) != self.classify(cmd.chars):
|
||||
return False
|
||||
self.index2 = cmd.index2
|
||||
self.chars = self.chars + cmd.chars
|
||||
return True
|
||||
|
||||
alphanumeric = string.ascii_letters + string.digits + "_"
|
||||
|
||||
def classify(self, c):
|
||||
if c in self.alphanumeric:
|
||||
return "alphanumeric"
|
||||
if c == "\n":
|
||||
return "newline"
|
||||
return "punctuation"
|
||||
|
||||
|
||||
class DeleteCommand(Command):
|
||||
|
||||
# Undoable delete command
|
||||
|
||||
def __init__(self, index1, index2=None):
|
||||
Command.__init__(self, index1, index2, None, None)
|
||||
|
||||
def do(self, text):
|
||||
self.marks_before = self.save_marks(text)
|
||||
self.index1 = text.index(self.index1)
|
||||
if self.index2:
|
||||
self.index2 = text.index(self.index2)
|
||||
else:
|
||||
self.index2 = text.index(self.index1 + " +1c")
|
||||
if text.compare(self.index2, ">", "end-1c"):
|
||||
# Don't delete the final newline
|
||||
self.index2 = text.index("end-1c")
|
||||
self.chars = text.get(self.index1, self.index2)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.marks_after = self.save_marks(text)
|
||||
##sys.__stderr__.write("do: %s\n" % self)
|
||||
|
||||
def redo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.delete(self.index1, self.index2)
|
||||
self.set_marks(text, self.marks_after)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("redo: %s\n" % self)
|
||||
|
||||
def undo(self, text):
|
||||
text.mark_set('insert', self.index1)
|
||||
text.insert(self.index1, self.chars)
|
||||
self.set_marks(text, self.marks_before)
|
||||
text.see('insert')
|
||||
##sys.__stderr__.write("undo: %s\n" % self)
|
||||
|
||||
class CommandSequence(Command):
|
||||
|
||||
# Wrapper for a sequence of undoable cmds to be undone/redone
|
||||
# as a unit
|
||||
|
||||
def __init__(self):
|
||||
self.cmds = []
|
||||
self.depth = 0
|
||||
|
||||
def __repr__(self):
|
||||
s = self.__class__.__name__
|
||||
strs = []
|
||||
for cmd in self.cmds:
|
||||
strs.append(" %r" % (cmd,))
|
||||
return s + "(\n" + ",\n".join(strs) + "\n)"
|
||||
|
||||
def __len__(self):
|
||||
return len(self.cmds)
|
||||
|
||||
def append(self, cmd):
|
||||
self.cmds.append(cmd)
|
||||
|
||||
def getcmd(self, i):
|
||||
return self.cmds[i]
|
||||
|
||||
def redo(self, text):
|
||||
for cmd in self.cmds:
|
||||
cmd.redo(text)
|
||||
|
||||
def undo(self, text):
|
||||
cmds = self.cmds[:]
|
||||
cmds.reverse()
|
||||
for cmd in cmds:
|
||||
cmd.undo(text)
|
||||
|
||||
def bump_depth(self, incr=1):
|
||||
self.depth = self.depth + incr
|
||||
return self.depth
|
||||
|
||||
def _undo_delegator(parent):
|
||||
from idlelib.Percolator import Percolator
|
||||
root = Tk()
|
||||
root.title("Test UndoDelegator")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
|
||||
text = Text(root)
|
||||
text.config(height=10)
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
p = Percolator(text)
|
||||
d = UndoDelegator()
|
||||
p.insertfilter(d)
|
||||
|
||||
undo = Button(root, text="Undo", command=lambda:d.undo_event(None))
|
||||
undo.pack(side='left')
|
||||
redo = Button(root, text="Redo", command=lambda:d.redo_event(None))
|
||||
redo.pack(side='left')
|
||||
dump = Button(root, text="Dump", command=lambda:d.dump_event(None))
|
||||
dump.pack(side='left')
|
||||
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_undo_delegator)
|
||||
176
Darwin/lib/python3.5/idlelib/WidgetRedirector.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
from tkinter import TclError
|
||||
|
||||
class WidgetRedirector:
|
||||
"""Support for redirecting arbitrary widget subcommands.
|
||||
|
||||
Some Tk operations don't normally pass through tkinter. For example, if a
|
||||
character is inserted into a Text widget by pressing a key, a default Tk
|
||||
binding to the widget's 'insert' operation is activated, and the Tk library
|
||||
processes the insert without calling back into tkinter.
|
||||
|
||||
Although a binding to <Key> could be made via tkinter, what we really want
|
||||
to do is to hook the Tk 'insert' operation itself. For one thing, we want
|
||||
a text.insert call in idle code to have the same effect as a key press.
|
||||
|
||||
When a widget is instantiated, a Tcl command is created whose name is the
|
||||
same as the pathname widget._w. This command is used to invoke the various
|
||||
widget operations, e.g. insert (for a Text widget). We are going to hook
|
||||
this command and provide a facility ('register') to intercept the widget
|
||||
operation. We will also intercept method calls on the tkinter class
|
||||
instance that represents the tk widget.
|
||||
|
||||
In IDLE, WidgetRedirector is used in Percolator to intercept Text
|
||||
commands. The function being registered provides access to the top
|
||||
of a Percolator chain. At the bottom of the chain is a call to the
|
||||
original Tk widget operation.
|
||||
"""
|
||||
def __init__(self, widget):
|
||||
'''Initialize attributes and setup redirection.
|
||||
|
||||
_operations: dict mapping operation name to new function.
|
||||
widget: the widget whose tcl command is to be intercepted.
|
||||
tk: widget.tk, a convenience attribute, probably not needed.
|
||||
orig: new name of the original tcl command.
|
||||
|
||||
Since renaming to orig fails with TclError when orig already
|
||||
exists, only one WidgetDirector can exist for a given widget.
|
||||
'''
|
||||
self._operations = {}
|
||||
self.widget = widget # widget instance
|
||||
self.tk = tk = widget.tk # widget's root
|
||||
w = widget._w # widget's (full) Tk pathname
|
||||
self.orig = w + "_orig"
|
||||
# Rename the Tcl command within Tcl:
|
||||
tk.call("rename", w, self.orig)
|
||||
# Create a new Tcl command whose name is the widget's pathname, and
|
||||
# whose action is to dispatch on the operation passed to the widget:
|
||||
tk.createcommand(w, self.dispatch)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s<%s>)" % (self.__class__.__name__,
|
||||
self.widget.__class__.__name__,
|
||||
self.widget._w)
|
||||
|
||||
def close(self):
|
||||
"Unregister operations and revert redirection created by .__init__."
|
||||
for operation in list(self._operations):
|
||||
self.unregister(operation)
|
||||
widget = self.widget
|
||||
tk = widget.tk
|
||||
w = widget._w
|
||||
# Restore the original widget Tcl command.
|
||||
tk.deletecommand(w)
|
||||
tk.call("rename", self.orig, w)
|
||||
del self.widget, self.tk # Should not be needed
|
||||
# if instance is deleted after close, as in Percolator.
|
||||
|
||||
def register(self, operation, function):
|
||||
'''Return OriginalCommand(operation) after registering function.
|
||||
|
||||
Registration adds an operation: function pair to ._operations.
|
||||
It also adds an widget function attribute that masks the tkinter
|
||||
class instance method. Method masking operates independently
|
||||
from command dispatch.
|
||||
|
||||
If a second function is registered for the same operation, the
|
||||
first function is replaced in both places.
|
||||
'''
|
||||
self._operations[operation] = function
|
||||
setattr(self.widget, operation, function)
|
||||
return OriginalCommand(self, operation)
|
||||
|
||||
def unregister(self, operation):
|
||||
'''Return the function for the operation, or None.
|
||||
|
||||
Deleting the instance attribute unmasks the class attribute.
|
||||
'''
|
||||
if operation in self._operations:
|
||||
function = self._operations[operation]
|
||||
del self._operations[operation]
|
||||
try:
|
||||
delattr(self.widget, operation)
|
||||
except AttributeError:
|
||||
pass
|
||||
return function
|
||||
else:
|
||||
return None
|
||||
|
||||
def dispatch(self, operation, *args):
|
||||
'''Callback from Tcl which runs when the widget is referenced.
|
||||
|
||||
If an operation has been registered in self._operations, apply the
|
||||
associated function to the args passed into Tcl. Otherwise, pass the
|
||||
operation through to Tk via the original Tcl function.
|
||||
|
||||
Note that if a registered function is called, the operation is not
|
||||
passed through to Tk. Apply the function returned by self.register()
|
||||
to *args to accomplish that. For an example, see ColorDelegator.py.
|
||||
|
||||
'''
|
||||
m = self._operations.get(operation)
|
||||
try:
|
||||
if m:
|
||||
return m(*args)
|
||||
else:
|
||||
return self.tk.call((self.orig, operation) + args)
|
||||
except TclError:
|
||||
return ""
|
||||
|
||||
|
||||
class OriginalCommand:
|
||||
'''Callable for original tk command that has been redirected.
|
||||
|
||||
Returned by .register; can be used in the function registered.
|
||||
redir = WidgetRedirector(text)
|
||||
def my_insert(*args):
|
||||
print("insert", args)
|
||||
original_insert(*args)
|
||||
original_insert = redir.register("insert", my_insert)
|
||||
'''
|
||||
|
||||
def __init__(self, redir, operation):
|
||||
'''Create .tk_call and .orig_and_operation for .__call__ method.
|
||||
|
||||
.redir and .operation store the input args for __repr__.
|
||||
.tk and .orig copy attributes of .redir (probably not needed).
|
||||
'''
|
||||
self.redir = redir
|
||||
self.operation = operation
|
||||
self.tk = redir.tk # redundant with self.redir
|
||||
self.orig = redir.orig # redundant with self.redir
|
||||
# These two could be deleted after checking recipient code.
|
||||
self.tk_call = redir.tk.call
|
||||
self.orig_and_operation = (redir.orig, operation)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__,
|
||||
self.redir, self.operation)
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.tk_call(self.orig_and_operation + args)
|
||||
|
||||
|
||||
def _widget_redirector(parent): # htest #
|
||||
from tkinter import Tk, Text
|
||||
import re
|
||||
|
||||
root = Tk()
|
||||
root.title("Test WidgetRedirector")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
text = Text(root)
|
||||
text.pack()
|
||||
text.focus_set()
|
||||
redir = WidgetRedirector(text)
|
||||
def my_insert(*args):
|
||||
print("insert", args)
|
||||
original_insert(*args)
|
||||
original_insert = redir.register("insert", my_insert)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_widgetredir',
|
||||
verbosity=2, exit=False)
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_widget_redirector)
|
||||
90
Darwin/lib/python3.5/idlelib/WindowList.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
from tkinter import *
|
||||
|
||||
class WindowList:
|
||||
|
||||
def __init__(self):
|
||||
self.dict = {}
|
||||
self.callbacks = []
|
||||
|
||||
def add(self, window):
|
||||
window.after_idle(self.call_callbacks)
|
||||
self.dict[str(window)] = window
|
||||
|
||||
def delete(self, window):
|
||||
try:
|
||||
del self.dict[str(window)]
|
||||
except KeyError:
|
||||
# Sometimes, destroy() is called twice
|
||||
pass
|
||||
self.call_callbacks()
|
||||
|
||||
def add_windows_to_menu(self, menu):
|
||||
list = []
|
||||
for key in self.dict:
|
||||
window = self.dict[key]
|
||||
try:
|
||||
title = window.get_title()
|
||||
except TclError:
|
||||
continue
|
||||
list.append((title, key, window))
|
||||
list.sort()
|
||||
for title, key, window in list:
|
||||
menu.add_command(label=title, command=window.wakeup)
|
||||
|
||||
def register_callback(self, callback):
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def unregister_callback(self, callback):
|
||||
try:
|
||||
self.callbacks.remove(callback)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def call_callbacks(self):
|
||||
for callback in self.callbacks:
|
||||
try:
|
||||
callback()
|
||||
except:
|
||||
t, v, tb = sys.exc_info()
|
||||
print("warning: callback failed in WindowList", t, ":", v)
|
||||
|
||||
registry = WindowList()
|
||||
|
||||
add_windows_to_menu = registry.add_windows_to_menu
|
||||
register_callback = registry.register_callback
|
||||
unregister_callback = registry.unregister_callback
|
||||
|
||||
|
||||
class ListedToplevel(Toplevel):
|
||||
|
||||
def __init__(self, master, **kw):
|
||||
Toplevel.__init__(self, master, kw)
|
||||
registry.add(self)
|
||||
self.focused_widget = self
|
||||
|
||||
def destroy(self):
|
||||
registry.delete(self)
|
||||
Toplevel.destroy(self)
|
||||
# If this is Idle's last window then quit the mainloop
|
||||
# (Needed for clean exit on Windows 98)
|
||||
if not registry.dict:
|
||||
self.quit()
|
||||
|
||||
def update_windowlist_registry(self, window):
|
||||
registry.call_callbacks()
|
||||
|
||||
def get_title(self):
|
||||
# Subclass can override
|
||||
return self.wm_title()
|
||||
|
||||
def wakeup(self):
|
||||
try:
|
||||
if self.wm_state() == "iconic":
|
||||
self.wm_withdraw()
|
||||
self.wm_deiconify()
|
||||
self.tkraise()
|
||||
self.focused_widget.focus_set()
|
||||
except TclError:
|
||||
# This can happen when the window menu was torn off.
|
||||
# Simply ignore it.
|
||||
pass
|
||||
51
Darwin/lib/python3.5/idlelib/ZoomHeight.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# Sample extension: zoom a window to maximum height
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from idlelib import macosxSupport
|
||||
|
||||
class ZoomHeight:
|
||||
|
||||
menudefs = [
|
||||
('windows', [
|
||||
('_Zoom Height', '<<zoom-height>>'),
|
||||
])
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def zoom_height_event(self, event):
|
||||
top = self.editwin.top
|
||||
zoom_height(top)
|
||||
|
||||
def zoom_height(top):
|
||||
geom = top.wm_geometry()
|
||||
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
|
||||
if not m:
|
||||
top.bell()
|
||||
return
|
||||
width, height, x, y = map(int, m.groups())
|
||||
newheight = top.winfo_screenheight()
|
||||
if sys.platform == 'win32':
|
||||
newy = 0
|
||||
newheight = newheight - 72
|
||||
|
||||
elif macosxSupport.isAquaTk():
|
||||
# The '88' below is a magic number that avoids placing the bottom
|
||||
# of the window below the panel on my machine. I don't know how
|
||||
# to calculate the correct value for this with tkinter.
|
||||
newy = 22
|
||||
newheight = newheight - newy - 88
|
||||
|
||||
else:
|
||||
#newy = 24
|
||||
newy = 0
|
||||
#newheight = newheight - 96
|
||||
newheight = newheight - 88
|
||||
if height >= newheight:
|
||||
newgeom = ""
|
||||
else:
|
||||
newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
|
||||
top.wm_geometry(newgeom)
|
||||
1
Darwin/lib/python3.5/idlelib/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Dummy file to make this a package.
|
||||
8
Darwin/lib/python3.5/idlelib/__main__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
"""
|
||||
IDLE main entry point
|
||||
|
||||
Run IDLE as python -m idlelib
|
||||
"""
|
||||
import idlelib.PyShell
|
||||
idlelib.PyShell.main()
|
||||
# This file does not work for 2.7; See issue 24212.
|
||||
146
Darwin/lib/python3.5/idlelib/aboutDialog.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
"""About Dialog for IDLE
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
from sys import version
|
||||
from tkinter import *
|
||||
from idlelib import textView
|
||||
|
||||
class AboutDialog(Toplevel):
|
||||
"""Modal about dialog for idle
|
||||
|
||||
"""
|
||||
def __init__(self, parent, title, _htest=False):
|
||||
"""
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
# place dialog below parent if running htest
|
||||
self.geometry("+%d+%d" % (
|
||||
parent.winfo_rootx()+30,
|
||||
parent.winfo_rooty()+(30 if not _htest else 100)))
|
||||
self.bg = "#707070"
|
||||
self.fg = "#ffffff"
|
||||
self.CreateWidgets()
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Ok)
|
||||
self.parent = parent
|
||||
self.buttonOk.focus_set()
|
||||
self.bind('<Return>',self.Ok) #dismiss dialog
|
||||
self.bind('<Escape>',self.Ok) #dismiss dialog
|
||||
self.wait_window()
|
||||
|
||||
def CreateWidgets(self):
|
||||
release = version[:version.index(' ')]
|
||||
frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
|
||||
frameButtons = Frame(self)
|
||||
frameButtons.pack(side=BOTTOM, fill=X)
|
||||
frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
self.buttonOk = Button(frameButtons, text='Close',
|
||||
command=self.Ok)
|
||||
self.buttonOk.pack(padx=5, pady=5)
|
||||
#self.picture = Image('photo', data=self.pictureData)
|
||||
frameBg = Frame(frameMain, bg=self.bg)
|
||||
frameBg.pack(expand=TRUE, fill=BOTH)
|
||||
labelTitle = Label(frameBg, text='IDLE', fg=self.fg, bg=self.bg,
|
||||
font=('courier', 24, 'bold'))
|
||||
labelTitle.grid(row=0, column=0, sticky=W, padx=10, pady=10)
|
||||
#labelPicture = Label(frameBg, text='[picture]')
|
||||
#image=self.picture, bg=self.bg)
|
||||
#labelPicture.grid(row=1, column=1, sticky=W, rowspan=2,
|
||||
# padx=0, pady=3)
|
||||
byline = "Python's Integrated DeveLopment Environment" + 5*'\n'
|
||||
labelDesc = Label(frameBg, text=byline, justify=LEFT,
|
||||
fg=self.fg, bg=self.bg)
|
||||
labelDesc.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
|
||||
labelEmail = Label(frameBg, text='email: idle-dev@python.org',
|
||||
justify=LEFT, fg=self.fg, bg=self.bg)
|
||||
labelEmail.grid(row=6, column=0, columnspan=2,
|
||||
sticky=W, padx=10, pady=0)
|
||||
labelWWW = Label(frameBg, text='https://docs.python.org/' +
|
||||
version[:3] + '/library/idle.html',
|
||||
justify=LEFT, fg=self.fg, bg=self.bg)
|
||||
labelWWW.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
|
||||
Frame(frameBg, borderwidth=1, relief=SUNKEN,
|
||||
height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
|
||||
columnspan=3, padx=5, pady=5)
|
||||
labelPythonVer = Label(frameBg, text='Python version: ' +
|
||||
release, fg=self.fg, bg=self.bg)
|
||||
labelPythonVer.grid(row=9, column=0, sticky=W, padx=10, pady=0)
|
||||
tkVer = self.tk.call('info', 'patchlevel')
|
||||
labelTkVer = Label(frameBg, text='Tk version: '+
|
||||
tkVer, fg=self.fg, bg=self.bg)
|
||||
labelTkVer.grid(row=9, column=1, sticky=W, padx=2, pady=0)
|
||||
py_button_f = Frame(frameBg, bg=self.bg)
|
||||
py_button_f.grid(row=10, column=0, columnspan=2, sticky=NSEW)
|
||||
buttonLicense = Button(py_button_f, text='License', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowLicense)
|
||||
buttonLicense.pack(side=LEFT, padx=10, pady=10)
|
||||
buttonCopyright = Button(py_button_f, text='Copyright', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowCopyright)
|
||||
buttonCopyright.pack(side=LEFT, padx=10, pady=10)
|
||||
buttonCredits = Button(py_button_f, text='Credits', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowPythonCredits)
|
||||
buttonCredits.pack(side=LEFT, padx=10, pady=10)
|
||||
Frame(frameBg, borderwidth=1, relief=SUNKEN,
|
||||
height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
|
||||
columnspan=3, padx=5, pady=5)
|
||||
idle_v = Label(frameBg, text='IDLE version: ' + release,
|
||||
fg=self.fg, bg=self.bg)
|
||||
idle_v.grid(row=12, column=0, sticky=W, padx=10, pady=0)
|
||||
idle_button_f = Frame(frameBg, bg=self.bg)
|
||||
idle_button_f.grid(row=13, column=0, columnspan=3, sticky=NSEW)
|
||||
idle_about_b = Button(idle_button_f, text='README', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLEAbout)
|
||||
idle_about_b.pack(side=LEFT, padx=10, pady=10)
|
||||
idle_news_b = Button(idle_button_f, text='NEWS', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLENEWS)
|
||||
idle_news_b.pack(side=LEFT, padx=10, pady=10)
|
||||
idle_credits_b = Button(idle_button_f, text='Credits', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLECredits)
|
||||
idle_credits_b.pack(side=LEFT, padx=10, pady=10)
|
||||
|
||||
def ShowLicense(self):
|
||||
self.display_printer_text('About - License', license)
|
||||
|
||||
def ShowCopyright(self):
|
||||
self.display_printer_text('About - Copyright', copyright)
|
||||
|
||||
def ShowPythonCredits(self):
|
||||
self.display_printer_text('About - Python Credits', credits)
|
||||
|
||||
def ShowIDLECredits(self):
|
||||
self.display_file_text('About - Credits', 'CREDITS.txt', 'iso-8859-1')
|
||||
|
||||
def ShowIDLEAbout(self):
|
||||
self.display_file_text('About - Readme', 'README.txt')
|
||||
|
||||
def ShowIDLENEWS(self):
|
||||
self.display_file_text('About - NEWS', 'NEWS.txt')
|
||||
|
||||
def display_printer_text(self, title, printer):
|
||||
printer._Printer__setup()
|
||||
text = '\n'.join(printer._Printer__lines)
|
||||
textView.view_text(self, title, text)
|
||||
|
||||
def display_file_text(self, title, filename, encoding=None):
|
||||
fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
|
||||
textView.view_file(self, title, fn, encoding)
|
||||
|
||||
def Ok(self, event=None):
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(AboutDialog)
|
||||
99
Darwin/lib/python3.5/idlelib/config-extensions.def
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# config-extensions.def
|
||||
#
|
||||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default configuration file for IDLE extensions settings.
|
||||
#
|
||||
# Each extension must have at least one section, named after the
|
||||
# extension module. This section must contain an 'enable' item (=True to
|
||||
# enable the extension, =False to disable it), it may contain
|
||||
# 'enable_editor' or 'enable_shell' items, to apply it only to editor ir
|
||||
# shell windows, and may also contain any other general configuration
|
||||
# items for the extension. Other True/False values will also be
|
||||
# recognized as boolean by the Extension Configuration dialog.
|
||||
#
|
||||
# Each extension must define at least one section named
|
||||
# ExtensionName_bindings or ExtensionName_cfgBindings. If present,
|
||||
# ExtensionName_bindings defines virtual event bindings for the
|
||||
# extension that are not user re-configurable. If present,
|
||||
# ExtensionName_cfgBindings defines virtual event bindings for the
|
||||
# extension that may be sensibly re-configured.
|
||||
#
|
||||
# If there are no keybindings for a menus' virtual events, include lines
|
||||
# like <<toggle-code-context>>= (See [CodeContext], below.)
|
||||
#
|
||||
# Currently it is necessary to manually modify this file to change
|
||||
# extension key bindings and default values. To customize, create
|
||||
# ~/.idlerc/config-extensions.cfg and append the appropriate customized
|
||||
# section(s). Those sections will override the defaults in this file.
|
||||
#
|
||||
# Note: If a keybinding is already in use when the extension is loaded,
|
||||
# the extension's virtual event's keybinding will be set to ''.
|
||||
#
|
||||
# See config-keys.def for notes on specifying keys and extend.txt for
|
||||
# information on creating IDLE extensions.
|
||||
|
||||
[AutoComplete]
|
||||
enable=True
|
||||
popupwait=2000
|
||||
[AutoComplete_cfgBindings]
|
||||
force-open-completions=<Control-Key-space>
|
||||
[AutoComplete_bindings]
|
||||
autocomplete=<Key-Tab>
|
||||
try-open-completions=<KeyRelease-period> <KeyRelease-slash> <KeyRelease-backslash>
|
||||
|
||||
[AutoExpand]
|
||||
enable=True
|
||||
[AutoExpand_cfgBindings]
|
||||
expand-word=<Option-Key-slash>
|
||||
|
||||
[CallTips]
|
||||
enable=True
|
||||
[CallTips_cfgBindings]
|
||||
force-open-calltip=<Control-Key-backslash>
|
||||
[CallTips_bindings]
|
||||
try-open-calltip=<KeyRelease-parenleft>
|
||||
refresh-calltip=<KeyRelease-parenright> <KeyRelease-0>
|
||||
|
||||
[CodeContext]
|
||||
enable=True
|
||||
enable_shell=False
|
||||
numlines=3
|
||||
visible=False
|
||||
bgcolor=LightGray
|
||||
fgcolor=Black
|
||||
[CodeContext_bindings]
|
||||
toggle-code-context=
|
||||
|
||||
[FormatParagraph]
|
||||
enable=True
|
||||
max-width=72
|
||||
[FormatParagraph_cfgBindings]
|
||||
format-paragraph=<Option-Key-q>
|
||||
|
||||
[ParenMatch]
|
||||
enable=True
|
||||
style= expression
|
||||
flash-delay= 500
|
||||
bell=True
|
||||
[ParenMatch_cfgBindings]
|
||||
flash-paren=<Control-Key-0>
|
||||
[ParenMatch_bindings]
|
||||
paren-closed=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
|
||||
|
||||
[RstripExtension]
|
||||
enable=True
|
||||
enable_shell=False
|
||||
enable_editor=True
|
||||
|
||||
[ScriptBinding]
|
||||
enable=True
|
||||
enable_shell=False
|
||||
enable_editor=True
|
||||
[ScriptBinding_cfgBindings]
|
||||
run-module=<Key-F5>
|
||||
check-module=<Option-Key-x>
|
||||
|
||||
[ZoomHeight]
|
||||
enable=True
|
||||
[ZoomHeight_cfgBindings]
|
||||
zoom-height=<Option-Key-0>
|
||||
64
Darwin/lib/python3.5/idlelib/config-highlight.def
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default config file for idle highlight theme settings.
|
||||
|
||||
[IDLE Classic]
|
||||
normal-foreground= #000000
|
||||
normal-background= #ffffff
|
||||
keyword-foreground= #ff7700
|
||||
keyword-background= #ffffff
|
||||
builtin-foreground= #900090
|
||||
builtin-background= #ffffff
|
||||
comment-foreground= #dd0000
|
||||
comment-background= #ffffff
|
||||
string-foreground= #00aa00
|
||||
string-background= #ffffff
|
||||
definition-foreground= #0000ff
|
||||
definition-background= #ffffff
|
||||
hilite-foreground= #000000
|
||||
hilite-background= gray
|
||||
break-foreground= black
|
||||
break-background= #ffff55
|
||||
hit-foreground= #ffffff
|
||||
hit-background= #000000
|
||||
error-foreground= #000000
|
||||
error-background= #ff7777
|
||||
#cursor (only foreground can be set, restart IDLE)
|
||||
cursor-foreground= black
|
||||
#shell window
|
||||
stdout-foreground= blue
|
||||
stdout-background= #ffffff
|
||||
stderr-foreground= red
|
||||
stderr-background= #ffffff
|
||||
console-foreground= #770000
|
||||
console-background= #ffffff
|
||||
|
||||
[IDLE New]
|
||||
normal-foreground= #000000
|
||||
normal-background= #ffffff
|
||||
keyword-foreground= #ff7700
|
||||
keyword-background= #ffffff
|
||||
builtin-foreground= #900090
|
||||
builtin-background= #ffffff
|
||||
comment-foreground= #dd0000
|
||||
comment-background= #ffffff
|
||||
string-foreground= #00aa00
|
||||
string-background= #ffffff
|
||||
definition-foreground= #0000ff
|
||||
definition-background= #ffffff
|
||||
hilite-foreground= #000000
|
||||
hilite-background= gray
|
||||
break-foreground= black
|
||||
break-background= #ffff55
|
||||
hit-foreground= #ffffff
|
||||
hit-background= #000000
|
||||
error-foreground= #000000
|
||||
error-background= #ff7777
|
||||
#cursor (only foreground can be set, restart IDLE)
|
||||
cursor-foreground= black
|
||||
#shell window
|
||||
stdout-foreground= blue
|
||||
stdout-background= #ffffff
|
||||
stderr-foreground= red
|
||||
stderr-background= #ffffff
|
||||
console-foreground= #770000
|
||||
console-background= #ffffff
|
||||
214
Darwin/lib/python3.5/idlelib/config-keys.def
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default config file for idle key binding settings.
|
||||
# Where multiple keys are specified for an action: if they are separated
|
||||
# by a space (eg. action=<key1> <key2>) then the keys are alternatives, if
|
||||
# there is no space (eg. action=<key1><key2>) then the keys comprise a
|
||||
# single 'emacs style' multi-keystoke binding. The tk event specifier 'Key'
|
||||
# is used in all cases, for consistency in auto key conflict checking in the
|
||||
# configuration gui.
|
||||
|
||||
[IDLE Classic Windows]
|
||||
copy=<Control-Key-c> <Control-Key-C>
|
||||
cut=<Control-Key-x> <Control-Key-X>
|
||||
paste=<Control-Key-v> <Control-Key-V>
|
||||
beginning-of-line= <Key-Home>
|
||||
center-insert=<Control-Key-l> <Control-Key-L>
|
||||
close-all-windows=<Control-Key-q> <Control-Key-Q>
|
||||
close-window=<Alt-Key-F4> <Meta-Key-F4>
|
||||
do-nothing=<Control-Key-F12>
|
||||
end-of-file=<Control-Key-d> <Control-Key-D>
|
||||
python-docs=<Key-F1>
|
||||
python-context-help=<Shift-Key-F1>
|
||||
history-next=<Alt-Key-n> <Meta-Key-n> <Alt-Key-N> <Meta-Key-N>
|
||||
history-previous=<Alt-Key-p> <Meta-Key-p> <Alt-Key-P> <Meta-Key-P>
|
||||
interrupt-execution=<Control-Key-c> <Control-Key-C>
|
||||
view-restart=<Key-F6>
|
||||
restart-shell=<Control-Key-F6>
|
||||
open-class-browser=<Alt-Key-c> <Meta-Key-c> <Alt-Key-C> <Meta-Key-C>
|
||||
open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M> <Meta-Key-M>
|
||||
open-new-window=<Control-Key-n> <Control-Key-N>
|
||||
open-window-from-file=<Control-Key-o> <Control-Key-O>
|
||||
plain-newline-and-indent=<Control-Key-j> <Control-Key-J>
|
||||
print-window=<Control-Key-p> <Control-Key-P>
|
||||
redo=<Control-Shift-Key-Z> <Control-Shift-Key-z>
|
||||
remove-selection=<Key-Escape>
|
||||
save-copy-of-window-as-file=<Alt-Shift-Key-S> <Alt-Shift-Key-s>
|
||||
save-window-as-file=<Control-Shift-Key-S> <Control-Shift-Key-s>
|
||||
save-window=<Control-Key-s> <Control-Key-S>
|
||||
select-all=<Control-Key-a> <Control-Key-A>
|
||||
toggle-auto-coloring=<Control-Key-slash>
|
||||
undo=<Control-Key-z> <Control-Key-Z>
|
||||
find=<Control-Key-f> <Control-Key-F>
|
||||
find-again=<Control-Key-g> <Key-F3> <Control-Key-G>
|
||||
find-in-files=<Alt-Key-F3> <Meta-Key-F3>
|
||||
find-selection=<Control-Key-F3>
|
||||
replace=<Control-Key-h> <Control-Key-H>
|
||||
goto-line=<Alt-Key-g> <Meta-Key-g> <Alt-Key-G> <Meta-Key-G>
|
||||
smart-backspace=<Key-BackSpace>
|
||||
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
||||
smart-indent=<Key-Tab>
|
||||
indent-region=<Control-Key-bracketright>
|
||||
dedent-region=<Control-Key-bracketleft>
|
||||
comment-region=<Alt-Key-3> <Meta-Key-3>
|
||||
uncomment-region=<Alt-Key-4> <Meta-Key-4>
|
||||
tabify-region=<Alt-Key-5> <Meta-Key-5>
|
||||
untabify-region=<Alt-Key-6> <Meta-Key-6>
|
||||
toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T> <Meta-Key-T>
|
||||
change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
|
||||
del-word-left=<Control-Key-BackSpace>
|
||||
del-word-right=<Control-Key-Delete>
|
||||
|
||||
[IDLE Classic Unix]
|
||||
copy=<Alt-Key-w> <Meta-Key-w>
|
||||
cut=<Control-Key-w>
|
||||
paste=<Control-Key-y>
|
||||
beginning-of-line=<Control-Key-a> <Key-Home>
|
||||
center-insert=<Control-Key-l>
|
||||
close-all-windows=<Control-Key-x><Control-Key-c>
|
||||
close-window=<Control-Key-x><Control-Key-0>
|
||||
do-nothing=<Control-Key-x>
|
||||
end-of-file=<Control-Key-d>
|
||||
history-next=<Alt-Key-n> <Meta-Key-n>
|
||||
history-previous=<Alt-Key-p> <Meta-Key-p>
|
||||
interrupt-execution=<Control-Key-c>
|
||||
view-restart=<Key-F6>
|
||||
restart-shell=<Control-Key-F6>
|
||||
open-class-browser=<Control-Key-x><Control-Key-b>
|
||||
open-module=<Control-Key-x><Control-Key-m>
|
||||
open-new-window=<Control-Key-x><Control-Key-n>
|
||||
open-window-from-file=<Control-Key-x><Control-Key-f>
|
||||
plain-newline-and-indent=<Control-Key-j>
|
||||
print-window=<Control-x><Control-Key-p>
|
||||
python-docs=<Control-Key-h>
|
||||
python-context-help=<Control-Shift-Key-H>
|
||||
redo=<Alt-Key-z> <Meta-Key-z>
|
||||
remove-selection=<Key-Escape>
|
||||
save-copy-of-window-as-file=<Control-Key-x><Control-Key-y>
|
||||
save-window-as-file=<Control-Key-x><Control-Key-w>
|
||||
save-window=<Control-Key-x><Control-Key-s>
|
||||
select-all=<Alt-Key-a> <Meta-Key-a>
|
||||
toggle-auto-coloring=<Control-Key-slash>
|
||||
undo=<Control-Key-z>
|
||||
find=<Control-Key-u><Control-Key-u><Control-Key-s>
|
||||
find-again=<Control-Key-u><Control-Key-s>
|
||||
find-in-files=<Alt-Key-s> <Meta-Key-s>
|
||||
find-selection=<Control-Key-s>
|
||||
replace=<Control-Key-r>
|
||||
goto-line=<Alt-Key-g> <Meta-Key-g>
|
||||
smart-backspace=<Key-BackSpace>
|
||||
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
||||
smart-indent=<Key-Tab>
|
||||
indent-region=<Control-Key-bracketright>
|
||||
dedent-region=<Control-Key-bracketleft>
|
||||
comment-region=<Alt-Key-3>
|
||||
uncomment-region=<Alt-Key-4>
|
||||
tabify-region=<Alt-Key-5>
|
||||
untabify-region=<Alt-Key-6>
|
||||
toggle-tabs=<Alt-Key-t>
|
||||
change-indentwidth=<Alt-Key-u>
|
||||
del-word-left=<Alt-Key-BackSpace>
|
||||
del-word-right=<Alt-Key-d>
|
||||
|
||||
[IDLE Classic Mac]
|
||||
copy=<Command-Key-c>
|
||||
cut=<Command-Key-x>
|
||||
paste=<Command-Key-v>
|
||||
beginning-of-line= <Key-Home>
|
||||
center-insert=<Control-Key-l>
|
||||
close-all-windows=<Command-Key-q>
|
||||
close-window=<Command-Key-w>
|
||||
do-nothing=<Control-Key-F12>
|
||||
end-of-file=<Control-Key-d>
|
||||
python-docs=<Key-F1>
|
||||
python-context-help=<Shift-Key-F1>
|
||||
history-next=<Control-Key-n>
|
||||
history-previous=<Control-Key-p>
|
||||
interrupt-execution=<Control-Key-c>
|
||||
view-restart=<Key-F6>
|
||||
restart-shell=<Control-Key-F6>
|
||||
open-class-browser=<Command-Key-b>
|
||||
open-module=<Command-Key-m>
|
||||
open-new-window=<Command-Key-n>
|
||||
open-window-from-file=<Command-Key-o>
|
||||
plain-newline-and-indent=<Control-Key-j>
|
||||
print-window=<Command-Key-p>
|
||||
redo=<Shift-Command-Key-Z>
|
||||
remove-selection=<Key-Escape>
|
||||
save-window-as-file=<Shift-Command-Key-S>
|
||||
save-window=<Command-Key-s>
|
||||
save-copy-of-window-as-file=<Option-Command-Key-s>
|
||||
select-all=<Command-Key-a>
|
||||
toggle-auto-coloring=<Control-Key-slash>
|
||||
undo=<Command-Key-z>
|
||||
find=<Command-Key-f>
|
||||
find-again=<Command-Key-g> <Key-F3>
|
||||
find-in-files=<Command-Key-F3>
|
||||
find-selection=<Shift-Command-Key-F3>
|
||||
replace=<Command-Key-r>
|
||||
goto-line=<Command-Key-j>
|
||||
smart-backspace=<Key-BackSpace>
|
||||
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
||||
smart-indent=<Key-Tab>
|
||||
indent-region=<Command-Key-bracketright>
|
||||
dedent-region=<Command-Key-bracketleft>
|
||||
comment-region=<Control-Key-3>
|
||||
uncomment-region=<Control-Key-4>
|
||||
tabify-region=<Control-Key-5>
|
||||
untabify-region=<Control-Key-6>
|
||||
toggle-tabs=<Control-Key-t>
|
||||
change-indentwidth=<Control-Key-u>
|
||||
del-word-left=<Control-Key-BackSpace>
|
||||
del-word-right=<Control-Key-Delete>
|
||||
|
||||
[IDLE Classic OSX]
|
||||
toggle-tabs = <Control-Key-t>
|
||||
interrupt-execution = <Control-Key-c>
|
||||
untabify-region = <Control-Key-6>
|
||||
remove-selection = <Key-Escape>
|
||||
print-window = <Command-Key-p>
|
||||
replace = <Command-Key-r>
|
||||
goto-line = <Command-Key-j>
|
||||
plain-newline-and-indent = <Control-Key-j>
|
||||
history-previous = <Control-Key-p>
|
||||
beginning-of-line = <Control-Key-Left>
|
||||
end-of-line = <Control-Key-Right>
|
||||
comment-region = <Control-Key-3>
|
||||
redo = <Shift-Command-Key-Z>
|
||||
close-window = <Command-Key-w>
|
||||
restart-shell = <Control-Key-F6>
|
||||
save-window-as-file = <Shift-Command-Key-S>
|
||||
close-all-windows = <Command-Key-q>
|
||||
view-restart = <Key-F6>
|
||||
tabify-region = <Control-Key-5>
|
||||
find-again = <Command-Key-g> <Key-F3>
|
||||
find = <Command-Key-f>
|
||||
toggle-auto-coloring = <Control-Key-slash>
|
||||
select-all = <Command-Key-a>
|
||||
smart-backspace = <Key-BackSpace>
|
||||
change-indentwidth = <Control-Key-u>
|
||||
do-nothing = <Control-Key-F12>
|
||||
smart-indent = <Key-Tab>
|
||||
center-insert = <Control-Key-l>
|
||||
history-next = <Control-Key-n>
|
||||
del-word-right = <Option-Key-Delete>
|
||||
undo = <Command-Key-z>
|
||||
save-window = <Command-Key-s>
|
||||
uncomment-region = <Control-Key-4>
|
||||
cut = <Command-Key-x>
|
||||
find-in-files = <Command-Key-F3>
|
||||
dedent-region = <Command-Key-bracketleft>
|
||||
copy = <Command-Key-c>
|
||||
paste = <Command-Key-v>
|
||||
indent-region = <Command-Key-bracketright>
|
||||
del-word-left = <Option-Key-BackSpace> <Option-Command-Key-BackSpace>
|
||||
newline-and-indent = <Key-Return> <Key-KP_Enter>
|
||||
end-of-file = <Control-Key-d>
|
||||
open-class-browser = <Command-Key-b>
|
||||
open-new-window = <Command-Key-n>
|
||||
open-module = <Command-Key-m>
|
||||
find-selection = <Shift-Command-Key-F3>
|
||||
python-context-help = <Shift-Key-F1>
|
||||
save-copy-of-window-as-file = <Option-Command-Key-s>
|
||||
open-window-from-file = <Command-Key-o>
|
||||
python-docs = <Key-F1>
|
||||
|
||||
76
Darwin/lib/python3.5/idlelib/config-main.def
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default config file for general idle settings.
|
||||
#
|
||||
# When IDLE starts, it will look in
|
||||
# the following two sets of files, in order:
|
||||
#
|
||||
# default configuration
|
||||
# ---------------------
|
||||
# config-main.def the default general config file
|
||||
# config-extensions.def the default extension config file
|
||||
# config-highlight.def the default highlighting config file
|
||||
# config-keys.def the default keybinding config file
|
||||
#
|
||||
# user configuration
|
||||
# -------------------
|
||||
# ~/.idlerc/config-main.cfg the user general config file
|
||||
# ~/.idlerc/config-extensions.cfg the user extension config file
|
||||
# ~/.idlerc/config-highlight.cfg the user highlighting config file
|
||||
# ~/.idlerc/config-keys.cfg the user keybinding config file
|
||||
#
|
||||
# On Windows2000 and Windows XP the .idlerc directory is at
|
||||
# Documents and Settings\<username>\.idlerc
|
||||
#
|
||||
# On Windows98 it is at c:\.idlerc
|
||||
#
|
||||
# Any options the user saves through the config dialog will be saved to
|
||||
# the relevant user config file. Reverting any general setting to the
|
||||
# default causes that entry to be wiped from the user file and re-read
|
||||
# from the default file. User highlighting themes or keybinding sets are
|
||||
# retained unless specifically deleted within the config dialog. Choosing
|
||||
# one of the default themes or keysets just applies the relevant settings
|
||||
# from the default file.
|
||||
#
|
||||
# Additional help sources are listed in the [HelpFiles] section and must be
|
||||
# viewable by a web browser (or the Windows Help viewer in the case of .chm
|
||||
# files). These sources will be listed on the Help menu. The pattern is
|
||||
# <sequence_number = menu item;/path/to/help/source>
|
||||
# You can't use a semi-colon in a menu item or path. The path will be platform
|
||||
# specific because of path separators, drive specs etc.
|
||||
#
|
||||
# It is best to use the Configuration GUI to set up additional help sources!
|
||||
# Example:
|
||||
#1 = My Extra Help Source;/usr/share/doc/foo/index.html
|
||||
#2 = Another Help Source;/path/to/another.pdf
|
||||
|
||||
[General]
|
||||
editor-on-startup= 0
|
||||
autosave= 0
|
||||
print-command-posix=lpr %%s
|
||||
print-command-win=start /min notepad /p %%s
|
||||
delete-exitfunc= 1
|
||||
|
||||
[EditorWindow]
|
||||
width= 80
|
||||
height= 40
|
||||
font= TkFixedFont
|
||||
font-size= 10
|
||||
font-bold= 0
|
||||
encoding= none
|
||||
|
||||
[Indent]
|
||||
use-spaces= 1
|
||||
num-spaces= 4
|
||||
|
||||
[Theme]
|
||||
default= 1
|
||||
name= IDLE Classic
|
||||
|
||||
[Keys]
|
||||
default= 1
|
||||
name= IDLE Classic OSX
|
||||
|
||||
[History]
|
||||
cyclic=1
|
||||
|
||||
[HelpFiles]
|
||||
1420
Darwin/lib/python3.5/idlelib/configDialog.py
Normal file
736
Darwin/lib/python3.5/idlelib/configHandler.py
Normal file
|
|
@ -0,0 +1,736 @@
|
|||
"""Provides access to stored IDLE configuration information.
|
||||
|
||||
Refer to the comments at the beginning of config-main.def for a description of
|
||||
the available configuration files and the design implemented to update user
|
||||
configuration information. In particular, user configuration choices which
|
||||
duplicate the defaults will be removed from the user's configuration files,
|
||||
and if a file becomes empty, it will be deleted.
|
||||
|
||||
The contents of the user files may be altered using the Options/Configure IDLE
|
||||
menu to access the configuration GUI (configDialog.py), or manually.
|
||||
|
||||
Throughout this module there is an emphasis on returning useable defaults
|
||||
when a problem occurs in returning a requested configuration value back to
|
||||
idle. This is to allow IDLE to continue to function in spite of errors in
|
||||
the retrieval of config information. When a default is returned instead of
|
||||
a requested config value, a message is printed to stderr to aid in
|
||||
configuration problem notification and resolution.
|
||||
"""
|
||||
# TODOs added Oct 2014, tjr
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from configparser import ConfigParser
|
||||
from tkinter import TkVersion
|
||||
from tkinter.font import Font, nametofont
|
||||
|
||||
class InvalidConfigType(Exception): pass
|
||||
class InvalidConfigSet(Exception): pass
|
||||
class InvalidFgBg(Exception): pass
|
||||
class InvalidTheme(Exception): pass
|
||||
|
||||
class IdleConfParser(ConfigParser):
|
||||
"""
|
||||
A ConfigParser specialised for idle configuration file handling
|
||||
"""
|
||||
def __init__(self, cfgFile, cfgDefaults=None):
|
||||
"""
|
||||
cfgFile - string, fully specified configuration file name
|
||||
"""
|
||||
self.file = cfgFile
|
||||
ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
|
||||
|
||||
def Get(self, section, option, type=None, default=None, raw=False):
|
||||
"""
|
||||
Get an option value for given section/option or return default.
|
||||
If type is specified, return as type.
|
||||
"""
|
||||
# TODO Use default as fallback, at least if not None
|
||||
# Should also print Warning(file, section, option).
|
||||
# Currently may raise ValueError
|
||||
if not self.has_option(section, option):
|
||||
return default
|
||||
if type == 'bool':
|
||||
return self.getboolean(section, option)
|
||||
elif type == 'int':
|
||||
return self.getint(section, option)
|
||||
else:
|
||||
return self.get(section, option, raw=raw)
|
||||
|
||||
def GetOptionList(self, section):
|
||||
"Return a list of options for given section, else []."
|
||||
if self.has_section(section):
|
||||
return self.options(section)
|
||||
else: #return a default value
|
||||
return []
|
||||
|
||||
def Load(self):
|
||||
"Load the configuration file from disk."
|
||||
self.read(self.file)
|
||||
|
||||
class IdleUserConfParser(IdleConfParser):
|
||||
"""
|
||||
IdleConfigParser specialised for user configuration handling.
|
||||
"""
|
||||
|
||||
def AddSection(self, section):
|
||||
"If section doesn't exist, add it."
|
||||
if not self.has_section(section):
|
||||
self.add_section(section)
|
||||
|
||||
def RemoveEmptySections(self):
|
||||
"Remove any sections that have no options."
|
||||
for section in self.sections():
|
||||
if not self.GetOptionList(section):
|
||||
self.remove_section(section)
|
||||
|
||||
def IsEmpty(self):
|
||||
"Return True if no sections after removing empty sections."
|
||||
self.RemoveEmptySections()
|
||||
return not self.sections()
|
||||
|
||||
def RemoveOption(self, section, option):
|
||||
"""Return True if option is removed from section, else False.
|
||||
|
||||
False if either section does not exist or did not have option.
|
||||
"""
|
||||
if self.has_section(section):
|
||||
return self.remove_option(section, option)
|
||||
return False
|
||||
|
||||
def SetOption(self, section, option, value):
|
||||
"""Return True if option is added or changed to value, else False.
|
||||
|
||||
Add section if required. False means option already had value.
|
||||
"""
|
||||
if self.has_option(section, option):
|
||||
if self.get(section, option) == value:
|
||||
return False
|
||||
else:
|
||||
self.set(section, option, value)
|
||||
return True
|
||||
else:
|
||||
if not self.has_section(section):
|
||||
self.add_section(section)
|
||||
self.set(section, option, value)
|
||||
return True
|
||||
|
||||
def RemoveFile(self):
|
||||
"Remove user config file self.file from disk if it exists."
|
||||
if os.path.exists(self.file):
|
||||
os.remove(self.file)
|
||||
|
||||
def Save(self):
|
||||
"""Update user configuration file.
|
||||
|
||||
Remove empty sections. If resulting config isn't empty, write the file
|
||||
to disk. If config is empty, remove the file from disk if it exists.
|
||||
|
||||
"""
|
||||
if not self.IsEmpty():
|
||||
fname = self.file
|
||||
try:
|
||||
cfgFile = open(fname, 'w')
|
||||
except OSError:
|
||||
os.unlink(fname)
|
||||
cfgFile = open(fname, 'w')
|
||||
with cfgFile:
|
||||
self.write(cfgFile)
|
||||
else:
|
||||
self.RemoveFile()
|
||||
|
||||
class IdleConf:
|
||||
"""Hold config parsers for all idle config files in singleton instance.
|
||||
|
||||
Default config files, self.defaultCfg --
|
||||
for config_type in self.config_types:
|
||||
(idle install dir)/config-{config-type}.def
|
||||
|
||||
User config files, self.userCfg --
|
||||
for config_type in self.config_types:
|
||||
(user home dir)/.idlerc/config-{config-type}.cfg
|
||||
"""
|
||||
def __init__(self):
|
||||
self.config_types = ('main', 'extensions', 'highlight', 'keys')
|
||||
self.defaultCfg = {}
|
||||
self.userCfg = {}
|
||||
self.cfg = {} # TODO use to select userCfg vs defaultCfg
|
||||
self.CreateConfigHandlers()
|
||||
self.LoadCfgFiles()
|
||||
|
||||
|
||||
def CreateConfigHandlers(self):
|
||||
"Populate default and user config parser dictionaries."
|
||||
#build idle install path
|
||||
if __name__ != '__main__': # we were imported
|
||||
idleDir=os.path.dirname(__file__)
|
||||
else: # we were exec'ed (for testing only)
|
||||
idleDir=os.path.abspath(sys.path[0])
|
||||
userDir=self.GetUserCfgDir()
|
||||
|
||||
defCfgFiles = {}
|
||||
usrCfgFiles = {}
|
||||
# TODO eliminate these temporaries by combining loops
|
||||
for cfgType in self.config_types: #build config file names
|
||||
defCfgFiles[cfgType] = os.path.join(
|
||||
idleDir, 'config-' + cfgType + '.def')
|
||||
usrCfgFiles[cfgType] = os.path.join(
|
||||
userDir, 'config-' + cfgType + '.cfg')
|
||||
for cfgType in self.config_types: #create config parsers
|
||||
self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
|
||||
self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
|
||||
|
||||
def GetUserCfgDir(self):
|
||||
"""Return a filesystem directory for storing user config files.
|
||||
|
||||
Creates it if required.
|
||||
"""
|
||||
cfgDir = '.idlerc'
|
||||
userDir = os.path.expanduser('~')
|
||||
if userDir != '~': # expanduser() found user home dir
|
||||
if not os.path.exists(userDir):
|
||||
warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
|
||||
userDir + ',\n but the path does not exist.')
|
||||
try:
|
||||
print(warn, file=sys.stderr)
|
||||
except OSError:
|
||||
pass
|
||||
userDir = '~'
|
||||
if userDir == "~": # still no path to home!
|
||||
# traditionally IDLE has defaulted to os.getcwd(), is this adequate?
|
||||
userDir = os.getcwd()
|
||||
userDir = os.path.join(userDir, cfgDir)
|
||||
if not os.path.exists(userDir):
|
||||
try:
|
||||
os.mkdir(userDir)
|
||||
except OSError:
|
||||
warn = ('\n Warning: unable to create user config directory\n' +
|
||||
userDir + '\n Check path and permissions.\n Exiting!\n')
|
||||
print(warn, file=sys.stderr)
|
||||
raise SystemExit
|
||||
# TODO continue without userDIr instead of exit
|
||||
return userDir
|
||||
|
||||
def GetOption(self, configType, section, option, default=None, type=None,
|
||||
warn_on_default=True, raw=False):
|
||||
"""Return a value for configType section option, or default.
|
||||
|
||||
If type is not None, return a value of that type. Also pass raw
|
||||
to the config parser. First try to return a valid value
|
||||
(including type) from a user configuration. If that fails, try
|
||||
the default configuration. If that fails, return default, with a
|
||||
default of None.
|
||||
|
||||
Warn if either user or default configurations have an invalid value.
|
||||
Warn if default is returned and warn_on_default is True.
|
||||
"""
|
||||
try:
|
||||
if self.userCfg[configType].has_option(section, option):
|
||||
return self.userCfg[configType].Get(section, option,
|
||||
type=type, raw=raw)
|
||||
except ValueError:
|
||||
warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
|
||||
' invalid %r value for configuration option %r\n'
|
||||
' from section %r: %r' %
|
||||
(type, option, section,
|
||||
self.userCfg[configType].Get(section, option, raw=raw)))
|
||||
try:
|
||||
print(warning, file=sys.stderr)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
if self.defaultCfg[configType].has_option(section,option):
|
||||
return self.defaultCfg[configType].Get(
|
||||
section, option, type=type, raw=raw)
|
||||
except ValueError:
|
||||
pass
|
||||
#returning default, print warning
|
||||
if warn_on_default:
|
||||
warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
|
||||
' problem retrieving configuration option %r\n'
|
||||
' from section %r.\n'
|
||||
' returning default value: %r' %
|
||||
(option, section, default))
|
||||
try:
|
||||
print(warning, file=sys.stderr)
|
||||
except OSError:
|
||||
pass
|
||||
return default
|
||||
|
||||
def SetOption(self, configType, section, option, value):
|
||||
"""Set section option to value in user config file."""
|
||||
self.userCfg[configType].SetOption(section, option, value)
|
||||
|
||||
def GetSectionList(self, configSet, configType):
|
||||
"""Return sections for configSet configType configuration.
|
||||
|
||||
configSet must be either 'user' or 'default'
|
||||
configType must be in self.config_types.
|
||||
"""
|
||||
if not (configType in self.config_types):
|
||||
raise InvalidConfigType('Invalid configType specified')
|
||||
if configSet == 'user':
|
||||
cfgParser = self.userCfg[configType]
|
||||
elif configSet == 'default':
|
||||
cfgParser=self.defaultCfg[configType]
|
||||
else:
|
||||
raise InvalidConfigSet('Invalid configSet specified')
|
||||
return cfgParser.sections()
|
||||
|
||||
def GetHighlight(self, theme, element, fgBg=None):
|
||||
"""Return individual theme element highlight color(s).
|
||||
|
||||
fgBg - string ('fg' or 'bg') or None.
|
||||
If None, return a dictionary containing fg and bg colors with
|
||||
keys 'foreground' and 'background'. Otherwise, only return
|
||||
fg or bg color, as specified. Colors are intended to be
|
||||
appropriate for passing to Tkinter in, e.g., a tag_config call).
|
||||
"""
|
||||
if self.defaultCfg['highlight'].has_section(theme):
|
||||
themeDict = self.GetThemeDict('default', theme)
|
||||
else:
|
||||
themeDict = self.GetThemeDict('user', theme)
|
||||
fore = themeDict[element + '-foreground']
|
||||
if element == 'cursor': # There is no config value for cursor bg
|
||||
back = themeDict['normal-background']
|
||||
else:
|
||||
back = themeDict[element + '-background']
|
||||
highlight = {"foreground": fore, "background": back}
|
||||
if not fgBg: # Return dict of both colors
|
||||
return highlight
|
||||
else: # Return specified color only
|
||||
if fgBg == 'fg':
|
||||
return highlight["foreground"]
|
||||
if fgBg == 'bg':
|
||||
return highlight["background"]
|
||||
else:
|
||||
raise InvalidFgBg('Invalid fgBg specified')
|
||||
|
||||
def GetThemeDict(self, type, themeName):
|
||||
"""Return {option:value} dict for elements in themeName.
|
||||
|
||||
type - string, 'default' or 'user' theme type
|
||||
themeName - string, theme name
|
||||
Values are loaded over ultimate fallback defaults to guarantee
|
||||
that all theme elements are present in a newly created theme.
|
||||
"""
|
||||
if type == 'user':
|
||||
cfgParser = self.userCfg['highlight']
|
||||
elif type == 'default':
|
||||
cfgParser = self.defaultCfg['highlight']
|
||||
else:
|
||||
raise InvalidTheme('Invalid theme type specified')
|
||||
# Provide foreground and background colors for each theme
|
||||
# element (other than cursor) even though some values are not
|
||||
# yet used by idle, to allow for their use in the future.
|
||||
# Default values are generally black and white.
|
||||
# TODO copy theme from a class attribute.
|
||||
theme ={'normal-foreground':'#000000',
|
||||
'normal-background':'#ffffff',
|
||||
'keyword-foreground':'#000000',
|
||||
'keyword-background':'#ffffff',
|
||||
'builtin-foreground':'#000000',
|
||||
'builtin-background':'#ffffff',
|
||||
'comment-foreground':'#000000',
|
||||
'comment-background':'#ffffff',
|
||||
'string-foreground':'#000000',
|
||||
'string-background':'#ffffff',
|
||||
'definition-foreground':'#000000',
|
||||
'definition-background':'#ffffff',
|
||||
'hilite-foreground':'#000000',
|
||||
'hilite-background':'gray',
|
||||
'break-foreground':'#ffffff',
|
||||
'break-background':'#000000',
|
||||
'hit-foreground':'#ffffff',
|
||||
'hit-background':'#000000',
|
||||
'error-foreground':'#ffffff',
|
||||
'error-background':'#000000',
|
||||
#cursor (only foreground can be set)
|
||||
'cursor-foreground':'#000000',
|
||||
#shell window
|
||||
'stdout-foreground':'#000000',
|
||||
'stdout-background':'#ffffff',
|
||||
'stderr-foreground':'#000000',
|
||||
'stderr-background':'#ffffff',
|
||||
'console-foreground':'#000000',
|
||||
'console-background':'#ffffff' }
|
||||
for element in theme:
|
||||
if not cfgParser.has_option(themeName, element):
|
||||
# Print warning that will return a default color
|
||||
warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
|
||||
' -\n problem retrieving theme element %r'
|
||||
'\n from theme %r.\n'
|
||||
' returning default color: %r' %
|
||||
(element, themeName, theme[element]))
|
||||
try:
|
||||
print(warning, file=sys.stderr)
|
||||
except OSError:
|
||||
pass
|
||||
theme[element] = cfgParser.Get(
|
||||
themeName, element, default=theme[element])
|
||||
return theme
|
||||
|
||||
def CurrentTheme(self):
|
||||
"Return the name of the currently active theme."
|
||||
return self.GetOption('main', 'Theme', 'name', default='')
|
||||
|
||||
def CurrentKeys(self):
|
||||
"Return the name of the currently active key set."
|
||||
return self.GetOption('main', 'Keys', 'name', default='')
|
||||
|
||||
def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
|
||||
"""Return extensions in default and user config-extensions files.
|
||||
|
||||
If active_only True, only return active (enabled) extensions
|
||||
and optionally only editor or shell extensions.
|
||||
If active_only False, return all extensions.
|
||||
"""
|
||||
extns = self.RemoveKeyBindNames(
|
||||
self.GetSectionList('default', 'extensions'))
|
||||
userExtns = self.RemoveKeyBindNames(
|
||||
self.GetSectionList('user', 'extensions'))
|
||||
for extn in userExtns:
|
||||
if extn not in extns: #user has added own extension
|
||||
extns.append(extn)
|
||||
if active_only:
|
||||
activeExtns = []
|
||||
for extn in extns:
|
||||
if self.GetOption('extensions', extn, 'enable', default=True,
|
||||
type='bool'):
|
||||
#the extension is enabled
|
||||
if editor_only or shell_only: # TODO if both, contradictory
|
||||
if editor_only:
|
||||
option = "enable_editor"
|
||||
else:
|
||||
option = "enable_shell"
|
||||
if self.GetOption('extensions', extn,option,
|
||||
default=True, type='bool',
|
||||
warn_on_default=False):
|
||||
activeExtns.append(extn)
|
||||
else:
|
||||
activeExtns.append(extn)
|
||||
return activeExtns
|
||||
else:
|
||||
return extns
|
||||
|
||||
def RemoveKeyBindNames(self, extnNameList):
|
||||
"Return extnNameList with keybinding section names removed."
|
||||
# TODO Easier to return filtered copy with list comp
|
||||
names = extnNameList
|
||||
kbNameIndicies = []
|
||||
for name in names:
|
||||
if name.endswith(('_bindings', '_cfgBindings')):
|
||||
kbNameIndicies.append(names.index(name))
|
||||
kbNameIndicies.sort(reverse=True)
|
||||
for index in kbNameIndicies: #delete each keybinding section name
|
||||
del(names[index])
|
||||
return names
|
||||
|
||||
def GetExtnNameForEvent(self, virtualEvent):
|
||||
"""Return the name of the extension binding virtualEvent, or None.
|
||||
|
||||
virtualEvent - string, name of the virtual event to test for,
|
||||
without the enclosing '<< >>'
|
||||
"""
|
||||
extName = None
|
||||
vEvent = '<<' + virtualEvent + '>>'
|
||||
for extn in self.GetExtensions(active_only=0):
|
||||
for event in self.GetExtensionKeys(extn):
|
||||
if event == vEvent:
|
||||
extName = extn # TODO return here?
|
||||
return extName
|
||||
|
||||
def GetExtensionKeys(self, extensionName):
|
||||
"""Return dict: {configurable extensionName event : active keybinding}.
|
||||
|
||||
Events come from default config extension_cfgBindings section.
|
||||
Keybindings come from GetCurrentKeySet() active key dict,
|
||||
where previously used bindings are disabled.
|
||||
"""
|
||||
keysName = extensionName + '_cfgBindings'
|
||||
activeKeys = self.GetCurrentKeySet()
|
||||
extKeys = {}
|
||||
if self.defaultCfg['extensions'].has_section(keysName):
|
||||
eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
|
||||
for eventName in eventNames:
|
||||
event = '<<' + eventName + '>>'
|
||||
binding = activeKeys[event]
|
||||
extKeys[event] = binding
|
||||
return extKeys
|
||||
|
||||
def __GetRawExtensionKeys(self,extensionName):
|
||||
"""Return dict {configurable extensionName event : keybinding list}.
|
||||
|
||||
Events come from default config extension_cfgBindings section.
|
||||
Keybindings list come from the splitting of GetOption, which
|
||||
tries user config before default config.
|
||||
"""
|
||||
keysName = extensionName+'_cfgBindings'
|
||||
extKeys = {}
|
||||
if self.defaultCfg['extensions'].has_section(keysName):
|
||||
eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
|
||||
for eventName in eventNames:
|
||||
binding = self.GetOption(
|
||||
'extensions', keysName, eventName, default='').split()
|
||||
event = '<<' + eventName + '>>'
|
||||
extKeys[event] = binding
|
||||
return extKeys
|
||||
|
||||
def GetExtensionBindings(self, extensionName):
|
||||
"""Return dict {extensionName event : active or defined keybinding}.
|
||||
|
||||
Augment self.GetExtensionKeys(extensionName) with mapping of non-
|
||||
configurable events (from default config) to GetOption splits,
|
||||
as in self.__GetRawExtensionKeys.
|
||||
"""
|
||||
bindsName = extensionName + '_bindings'
|
||||
extBinds = self.GetExtensionKeys(extensionName)
|
||||
#add the non-configurable bindings
|
||||
if self.defaultCfg['extensions'].has_section(bindsName):
|
||||
eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
|
||||
for eventName in eventNames:
|
||||
binding = self.GetOption(
|
||||
'extensions', bindsName, eventName, default='').split()
|
||||
event = '<<' + eventName + '>>'
|
||||
extBinds[event] = binding
|
||||
|
||||
return extBinds
|
||||
|
||||
def GetKeyBinding(self, keySetName, eventStr):
|
||||
"""Return the keybinding list for keySetName eventStr.
|
||||
|
||||
keySetName - name of key binding set (config-keys section).
|
||||
eventStr - virtual event, including brackets, as in '<<event>>'.
|
||||
"""
|
||||
eventName = eventStr[2:-2] #trim off the angle brackets
|
||||
binding = self.GetOption('keys', keySetName, eventName, default='').split()
|
||||
return binding
|
||||
|
||||
def GetCurrentKeySet(self):
|
||||
"Return CurrentKeys with 'darwin' modifications."
|
||||
result = self.GetKeySet(self.CurrentKeys())
|
||||
|
||||
if sys.platform == "darwin":
|
||||
# OS X Tk variants do not support the "Alt" keyboard modifier.
|
||||
# So replace all keybingings that use "Alt" with ones that
|
||||
# use the "Option" keyboard modifier.
|
||||
# TODO (Ned?): the "Option" modifier does not work properly for
|
||||
# Cocoa Tk and XQuartz Tk so we should not use it
|
||||
# in default OS X KeySets.
|
||||
for k, v in result.items():
|
||||
v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
|
||||
if v != v2:
|
||||
result[k] = v2
|
||||
|
||||
return result
|
||||
|
||||
def GetKeySet(self, keySetName):
|
||||
"""Return event-key dict for keySetName core plus active extensions.
|
||||
|
||||
If a binding defined in an extension is already in use, the
|
||||
extension binding is disabled by being set to ''
|
||||
"""
|
||||
keySet = self.GetCoreKeys(keySetName)
|
||||
activeExtns = self.GetExtensions(active_only=1)
|
||||
for extn in activeExtns:
|
||||
extKeys = self.__GetRawExtensionKeys(extn)
|
||||
if extKeys: #the extension defines keybindings
|
||||
for event in extKeys:
|
||||
if extKeys[event] in keySet.values():
|
||||
#the binding is already in use
|
||||
extKeys[event] = '' #disable this binding
|
||||
keySet[event] = extKeys[event] #add binding
|
||||
return keySet
|
||||
|
||||
def IsCoreBinding(self, virtualEvent):
|
||||
"""Return True if the virtual event is one of the core idle key events.
|
||||
|
||||
virtualEvent - string, name of the virtual event to test for,
|
||||
without the enclosing '<< >>'
|
||||
"""
|
||||
return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
|
||||
|
||||
# TODO make keyBindins a file or class attribute used for test above
|
||||
# and copied in function below
|
||||
|
||||
def GetCoreKeys(self, keySetName=None):
|
||||
"""Return dict of core virtual-key keybindings for keySetName.
|
||||
|
||||
The default keySetName None corresponds to the keyBindings base
|
||||
dict. If keySetName is not None, bindings from the config
|
||||
file(s) are loaded _over_ these defaults, so if there is a
|
||||
problem getting any core binding there will be an 'ultimate last
|
||||
resort fallback' to the CUA-ish bindings defined here.
|
||||
"""
|
||||
keyBindings={
|
||||
'<<copy>>': ['<Control-c>', '<Control-C>'],
|
||||
'<<cut>>': ['<Control-x>', '<Control-X>'],
|
||||
'<<paste>>': ['<Control-v>', '<Control-V>'],
|
||||
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
||||
'<<center-insert>>': ['<Control-l>'],
|
||||
'<<close-all-windows>>': ['<Control-q>'],
|
||||
'<<close-window>>': ['<Alt-F4>'],
|
||||
'<<do-nothing>>': ['<Control-x>'],
|
||||
'<<end-of-file>>': ['<Control-d>'],
|
||||
'<<python-docs>>': ['<F1>'],
|
||||
'<<python-context-help>>': ['<Shift-F1>'],
|
||||
'<<history-next>>': ['<Alt-n>'],
|
||||
'<<history-previous>>': ['<Alt-p>'],
|
||||
'<<interrupt-execution>>': ['<Control-c>'],
|
||||
'<<view-restart>>': ['<F6>'],
|
||||
'<<restart-shell>>': ['<Control-F6>'],
|
||||
'<<open-class-browser>>': ['<Alt-c>'],
|
||||
'<<open-module>>': ['<Alt-m>'],
|
||||
'<<open-new-window>>': ['<Control-n>'],
|
||||
'<<open-window-from-file>>': ['<Control-o>'],
|
||||
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
||||
'<<print-window>>': ['<Control-p>'],
|
||||
'<<redo>>': ['<Control-y>'],
|
||||
'<<remove-selection>>': ['<Escape>'],
|
||||
'<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
|
||||
'<<save-window-as-file>>': ['<Alt-s>'],
|
||||
'<<save-window>>': ['<Control-s>'],
|
||||
'<<select-all>>': ['<Alt-a>'],
|
||||
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
||||
'<<undo>>': ['<Control-z>'],
|
||||
'<<find-again>>': ['<Control-g>', '<F3>'],
|
||||
'<<find-in-files>>': ['<Alt-F3>'],
|
||||
'<<find-selection>>': ['<Control-F3>'],
|
||||
'<<find>>': ['<Control-f>'],
|
||||
'<<replace>>': ['<Control-h>'],
|
||||
'<<goto-line>>': ['<Alt-g>'],
|
||||
'<<smart-backspace>>': ['<Key-BackSpace>'],
|
||||
'<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
|
||||
'<<smart-indent>>': ['<Key-Tab>'],
|
||||
'<<indent-region>>': ['<Control-Key-bracketright>'],
|
||||
'<<dedent-region>>': ['<Control-Key-bracketleft>'],
|
||||
'<<comment-region>>': ['<Alt-Key-3>'],
|
||||
'<<uncomment-region>>': ['<Alt-Key-4>'],
|
||||
'<<tabify-region>>': ['<Alt-Key-5>'],
|
||||
'<<untabify-region>>': ['<Alt-Key-6>'],
|
||||
'<<toggle-tabs>>': ['<Alt-Key-t>'],
|
||||
'<<change-indentwidth>>': ['<Alt-Key-u>'],
|
||||
'<<del-word-left>>': ['<Control-Key-BackSpace>'],
|
||||
'<<del-word-right>>': ['<Control-Key-Delete>']
|
||||
}
|
||||
if keySetName:
|
||||
for event in keyBindings:
|
||||
binding = self.GetKeyBinding(keySetName, event)
|
||||
if binding:
|
||||
keyBindings[event] = binding
|
||||
else: #we are going to return a default, print warning
|
||||
warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
|
||||
' -\n problem retrieving key binding for event %r'
|
||||
'\n from key set %r.\n'
|
||||
' returning default value: %r' %
|
||||
(event, keySetName, keyBindings[event]))
|
||||
try:
|
||||
print(warning, file=sys.stderr)
|
||||
except OSError:
|
||||
pass
|
||||
return keyBindings
|
||||
|
||||
def GetExtraHelpSourceList(self, configSet):
|
||||
"""Return list of extra help sources from a given configSet.
|
||||
|
||||
Valid configSets are 'user' or 'default'. Return a list of tuples of
|
||||
the form (menu_item , path_to_help_file , option), or return the empty
|
||||
list. 'option' is the sequence number of the help resource. 'option'
|
||||
values determine the position of the menu items on the Help menu,
|
||||
therefore the returned list must be sorted by 'option'.
|
||||
|
||||
"""
|
||||
helpSources = []
|
||||
if configSet == 'user':
|
||||
cfgParser = self.userCfg['main']
|
||||
elif configSet == 'default':
|
||||
cfgParser = self.defaultCfg['main']
|
||||
else:
|
||||
raise InvalidConfigSet('Invalid configSet specified')
|
||||
options=cfgParser.GetOptionList('HelpFiles')
|
||||
for option in options:
|
||||
value=cfgParser.Get('HelpFiles', option, default=';')
|
||||
if value.find(';') == -1: #malformed config entry with no ';'
|
||||
menuItem = '' #make these empty
|
||||
helpPath = '' #so value won't be added to list
|
||||
else: #config entry contains ';' as expected
|
||||
value=value.split(';')
|
||||
menuItem=value[0].strip()
|
||||
helpPath=value[1].strip()
|
||||
if menuItem and helpPath: #neither are empty strings
|
||||
helpSources.append( (menuItem,helpPath,option) )
|
||||
helpSources.sort(key=lambda x: x[2])
|
||||
return helpSources
|
||||
|
||||
def GetAllExtraHelpSourcesList(self):
|
||||
"""Return a list of the details of all additional help sources.
|
||||
|
||||
Tuples in the list are those of GetExtraHelpSourceList.
|
||||
"""
|
||||
allHelpSources = (self.GetExtraHelpSourceList('default') +
|
||||
self.GetExtraHelpSourceList('user') )
|
||||
return allHelpSources
|
||||
|
||||
def GetFont(self, root, configType, section):
|
||||
"""Retrieve a font from configuration (font, font-size, font-bold)
|
||||
Intercept the special value 'TkFixedFont' and substitute
|
||||
the actual font, factoring in some tweaks if needed for
|
||||
appearance sakes.
|
||||
|
||||
The 'root' parameter can normally be any valid Tkinter widget.
|
||||
|
||||
Return a tuple (family, size, weight) suitable for passing
|
||||
to tkinter.Font
|
||||
"""
|
||||
family = self.GetOption(configType, section, 'font', default='courier')
|
||||
size = self.GetOption(configType, section, 'font-size', type='int',
|
||||
default='10')
|
||||
bold = self.GetOption(configType, section, 'font-bold', default=0,
|
||||
type='bool')
|
||||
if (family == 'TkFixedFont'):
|
||||
if TkVersion < 8.5:
|
||||
family = 'Courier'
|
||||
else:
|
||||
f = Font(name='TkFixedFont', exists=True, root=root)
|
||||
actualFont = Font.actual(f)
|
||||
family = actualFont['family']
|
||||
size = actualFont['size']
|
||||
if size < 0:
|
||||
size = 10 # if font in pixels, ignore actual size
|
||||
bold = actualFont['weight']=='bold'
|
||||
return (family, size, 'bold' if bold else 'normal')
|
||||
|
||||
def LoadCfgFiles(self):
|
||||
"Load all configuration files."
|
||||
for key in self.defaultCfg:
|
||||
self.defaultCfg[key].Load()
|
||||
self.userCfg[key].Load() #same keys
|
||||
|
||||
def SaveUserCfgFiles(self):
|
||||
"Write all loaded user configuration files to disk."
|
||||
for key in self.userCfg:
|
||||
self.userCfg[key].Save()
|
||||
|
||||
|
||||
idleConf = IdleConf()
|
||||
|
||||
# TODO Revise test output, write expanded unittest
|
||||
### module test
|
||||
if __name__ == '__main__':
|
||||
def dumpCfg(cfg):
|
||||
print('\n', cfg, '\n')
|
||||
for key in cfg:
|
||||
sections = cfg[key].sections()
|
||||
print(key)
|
||||
print(sections)
|
||||
for section in sections:
|
||||
options = cfg[key].options(section)
|
||||
print(section)
|
||||
print(options)
|
||||
for option in options:
|
||||
print(option, '=', cfg[key].Get(section, option))
|
||||
dumpCfg(idleConf.defaultCfg)
|
||||
dumpCfg(idleConf.userCfg)
|
||||
print(idleConf.userCfg['main'].Get('Theme', 'name'))
|
||||
#print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')
|
||||
166
Darwin/lib/python3.5/idlelib/configHelpSourceEdit.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
"Dialog to specify or edit the parameters for a user configured help source."
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
import tkinter.filedialog as tkFileDialog
|
||||
|
||||
class GetHelpSourceDialog(Toplevel):
|
||||
def __init__(self, parent, title, menuItem='', filePath='', _htest=False):
|
||||
"""Get menu entry and url/ local file location for Additional Help
|
||||
|
||||
User selects a name for the Help resource and provides a web url
|
||||
or a local file as its source. The user can enter a url or browse
|
||||
for the file.
|
||||
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Cancel)
|
||||
self.parent = parent
|
||||
self.result = None
|
||||
self.CreateWidgets()
|
||||
self.menu.set(menuItem)
|
||||
self.path.set(filePath)
|
||||
self.withdraw() #hide while setting geometry
|
||||
#needs to be done here so that the winfo_reqwidth is valid
|
||||
self.update_idletasks()
|
||||
#centre dialog over parent. below parent if running htest.
|
||||
self.geometry(
|
||||
"+%d+%d" % (
|
||||
parent.winfo_rootx() +
|
||||
(parent.winfo_width()/2 - self.winfo_reqwidth()/2),
|
||||
parent.winfo_rooty() +
|
||||
((parent.winfo_height()/2 - self.winfo_reqheight()/2)
|
||||
if not _htest else 150)))
|
||||
self.deiconify() #geometry set, unhide
|
||||
self.bind('<Return>', self.Ok)
|
||||
self.wait_window()
|
||||
|
||||
def CreateWidgets(self):
|
||||
self.menu = StringVar(self)
|
||||
self.path = StringVar(self)
|
||||
self.fontSize = StringVar(self)
|
||||
self.frameMain = Frame(self, borderwidth=2, relief=GROOVE)
|
||||
self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
labelMenu = Label(self.frameMain, anchor=W, justify=LEFT,
|
||||
text='Menu Item:')
|
||||
self.entryMenu = Entry(self.frameMain, textvariable=self.menu,
|
||||
width=30)
|
||||
self.entryMenu.focus_set()
|
||||
labelPath = Label(self.frameMain, anchor=W, justify=LEFT,
|
||||
text='Help File Path: Enter URL or browse for file')
|
||||
self.entryPath = Entry(self.frameMain, textvariable=self.path,
|
||||
width=40)
|
||||
self.entryMenu.focus_set()
|
||||
labelMenu.pack(anchor=W, padx=5, pady=3)
|
||||
self.entryMenu.pack(anchor=W, padx=5, pady=3)
|
||||
labelPath.pack(anchor=W, padx=5, pady=3)
|
||||
self.entryPath.pack(anchor=W, padx=5, pady=3)
|
||||
browseButton = Button(self.frameMain, text='Browse', width=8,
|
||||
command=self.browseFile)
|
||||
browseButton.pack(pady=3)
|
||||
frameButtons = Frame(self)
|
||||
frameButtons.pack(side=BOTTOM, fill=X)
|
||||
self.buttonOk = Button(frameButtons, text='OK',
|
||||
width=8, default=ACTIVE, command=self.Ok)
|
||||
self.buttonOk.grid(row=0, column=0, padx=5,pady=5)
|
||||
self.buttonCancel = Button(frameButtons, text='Cancel',
|
||||
width=8, command=self.Cancel)
|
||||
self.buttonCancel.grid(row=0, column=1, padx=5, pady=5)
|
||||
|
||||
def browseFile(self):
|
||||
filetypes = [
|
||||
("HTML Files", "*.htm *.html", "TEXT"),
|
||||
("PDF Files", "*.pdf", "TEXT"),
|
||||
("Windows Help Files", "*.chm"),
|
||||
("Text Files", "*.txt", "TEXT"),
|
||||
("All Files", "*")]
|
||||
path = self.path.get()
|
||||
if path:
|
||||
dir, base = os.path.split(path)
|
||||
else:
|
||||
base = None
|
||||
if sys.platform[:3] == 'win':
|
||||
dir = os.path.join(os.path.dirname(sys.executable), 'Doc')
|
||||
if not os.path.isdir(dir):
|
||||
dir = os.getcwd()
|
||||
else:
|
||||
dir = os.getcwd()
|
||||
opendialog = tkFileDialog.Open(parent=self, filetypes=filetypes)
|
||||
file = opendialog.show(initialdir=dir, initialfile=base)
|
||||
if file:
|
||||
self.path.set(file)
|
||||
|
||||
def MenuOk(self):
|
||||
"Simple validity check for a sensible menu item name"
|
||||
menuOk = True
|
||||
menu = self.menu.get()
|
||||
menu.strip()
|
||||
if not menu:
|
||||
tkMessageBox.showerror(title='Menu Item Error',
|
||||
message='No menu item specified',
|
||||
parent=self)
|
||||
self.entryMenu.focus_set()
|
||||
menuOk = False
|
||||
elif len(menu) > 30:
|
||||
tkMessageBox.showerror(title='Menu Item Error',
|
||||
message='Menu item too long:'
|
||||
'\nLimit 30 characters.',
|
||||
parent=self)
|
||||
self.entryMenu.focus_set()
|
||||
menuOk = False
|
||||
return menuOk
|
||||
|
||||
def PathOk(self):
|
||||
"Simple validity check for menu file path"
|
||||
pathOk = True
|
||||
path = self.path.get()
|
||||
path.strip()
|
||||
if not path: #no path specified
|
||||
tkMessageBox.showerror(title='File Path Error',
|
||||
message='No help file path specified.',
|
||||
parent=self)
|
||||
self.entryPath.focus_set()
|
||||
pathOk = False
|
||||
elif path.startswith(('www.', 'http')):
|
||||
pass
|
||||
else:
|
||||
if path[:5] == 'file:':
|
||||
path = path[5:]
|
||||
if not os.path.exists(path):
|
||||
tkMessageBox.showerror(title='File Path Error',
|
||||
message='Help file path does not exist.',
|
||||
parent=self)
|
||||
self.entryPath.focus_set()
|
||||
pathOk = False
|
||||
return pathOk
|
||||
|
||||
def Ok(self, event=None):
|
||||
if self.MenuOk() and self.PathOk():
|
||||
self.result = (self.menu.get().strip(),
|
||||
self.path.get().strip())
|
||||
if sys.platform == 'darwin':
|
||||
path = self.result[1]
|
||||
if path.startswith(('www', 'file:', 'http:')):
|
||||
pass
|
||||
else:
|
||||
# Mac Safari insists on using the URI form for local files
|
||||
self.result = list(self.result)
|
||||
self.result[1] = "file://" + path
|
||||
self.destroy()
|
||||
|
||||
def Cancel(self, event=None):
|
||||
self.result = None
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(GetHelpSourceDialog)
|
||||
98
Darwin/lib/python3.5/idlelib/configSectionNameDialog.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
"""
|
||||
Dialog that allows user to specify a new config file section name.
|
||||
Used to get new highlight theme and keybinding set names.
|
||||
The 'return value' for the dialog, used two placed in configDialog.py,
|
||||
is the .result attribute set in the Ok and Cancel methods.
|
||||
"""
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
|
||||
class GetCfgSectionNameDialog(Toplevel):
|
||||
def __init__(self, parent, title, message, used_names, _htest=False):
|
||||
"""
|
||||
message - string, informational message to display
|
||||
used_names - string collection, names already in use for validity check
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Cancel)
|
||||
self.parent = parent
|
||||
self.message = message
|
||||
self.used_names = used_names
|
||||
self.create_widgets()
|
||||
self.withdraw() #hide while setting geometry
|
||||
self.update_idletasks()
|
||||
#needs to be done here so that the winfo_reqwidth is valid
|
||||
self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
|
||||
self.geometry(
|
||||
"+%d+%d" % (
|
||||
parent.winfo_rootx() +
|
||||
(parent.winfo_width()/2 - self.winfo_reqwidth()/2),
|
||||
parent.winfo_rooty() +
|
||||
((parent.winfo_height()/2 - self.winfo_reqheight()/2)
|
||||
if not _htest else 100)
|
||||
) ) #centre dialog over parent (or below htest box)
|
||||
self.deiconify() #geometry set, unhide
|
||||
self.wait_window()
|
||||
|
||||
def create_widgets(self):
|
||||
self.name = StringVar(self.parent)
|
||||
self.fontSize = StringVar(self.parent)
|
||||
self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
|
||||
self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT,
|
||||
padx=5, pady=5, text=self.message) #,aspect=200)
|
||||
entryName = Entry(self.frameMain, textvariable=self.name, width=30)
|
||||
entryName.focus_set()
|
||||
self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH)
|
||||
entryName.pack(padx=5, pady=5)
|
||||
|
||||
frameButtons = Frame(self, pady=2)
|
||||
frameButtons.pack(side=BOTTOM)
|
||||
self.buttonOk = Button(frameButtons, text='Ok',
|
||||
width=8, command=self.Ok)
|
||||
self.buttonOk.pack(side=LEFT, padx=5)
|
||||
self.buttonCancel = Button(frameButtons, text='Cancel',
|
||||
width=8, command=self.Cancel)
|
||||
self.buttonCancel.pack(side=RIGHT, padx=5)
|
||||
|
||||
def name_ok(self):
|
||||
''' After stripping entered name, check that it is a sensible
|
||||
ConfigParser file section name. Return it if it is, '' if not.
|
||||
'''
|
||||
name = self.name.get().strip()
|
||||
if not name: #no name specified
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='No name specified.', parent=self)
|
||||
elif len(name)>30: #name too long
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='Name too long. It should be no more than '+
|
||||
'30 characters.', parent=self)
|
||||
name = ''
|
||||
elif name in self.used_names:
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='This name is already in use.', parent=self)
|
||||
name = ''
|
||||
return name
|
||||
|
||||
def Ok(self, event=None):
|
||||
name = self.name_ok()
|
||||
if name:
|
||||
self.result = name
|
||||
self.destroy()
|
||||
|
||||
def Cancel(self, event=None):
|
||||
self.result = ''
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
|
||||
|
||||
from idlelib.idle_test.htest import run
|
||||
run(GetCfgSectionNameDialog)
|
||||
57
Darwin/lib/python3.5/idlelib/dynOptionMenuWidget.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
"""
|
||||
OptionMenu widget modified to allow dynamic menu reconfiguration
|
||||
and setting of highlightthickness
|
||||
"""
|
||||
import copy
|
||||
from tkinter import OptionMenu, _setit, StringVar, Button
|
||||
|
||||
class DynOptionMenu(OptionMenu):
|
||||
"""
|
||||
unlike OptionMenu, our kwargs can include highlightthickness
|
||||
"""
|
||||
def __init__(self, master, variable, value, *values, **kwargs):
|
||||
# TODO copy value instead of whole dict
|
||||
kwargsCopy=copy.copy(kwargs)
|
||||
if 'highlightthickness' in list(kwargs.keys()):
|
||||
del(kwargs['highlightthickness'])
|
||||
OptionMenu.__init__(self, master, variable, value, *values, **kwargs)
|
||||
self.config(highlightthickness=kwargsCopy.get('highlightthickness'))
|
||||
#self.menu=self['menu']
|
||||
self.variable=variable
|
||||
self.command=kwargs.get('command')
|
||||
|
||||
def SetMenu(self,valueList,value=None):
|
||||
"""
|
||||
clear and reload the menu with a new set of options.
|
||||
valueList - list of new options
|
||||
value - initial value to set the optionmenu's menubutton to
|
||||
"""
|
||||
self['menu'].delete(0,'end')
|
||||
for item in valueList:
|
||||
self['menu'].add_command(label=item,
|
||||
command=_setit(self.variable,item,self.command))
|
||||
if value:
|
||||
self.variable.set(value)
|
||||
|
||||
def _dyn_option_menu(parent): # htest #
|
||||
from tkinter import Toplevel
|
||||
|
||||
top = Toplevel()
|
||||
top.title("Tets dynamic option menu")
|
||||
top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
|
||||
parent.winfo_rooty() + 150))
|
||||
top.focus_set()
|
||||
|
||||
var = StringVar(top)
|
||||
var.set("Old option set") #Set the default value
|
||||
dyn = DynOptionMenu(top,var, "old1","old2","old3","old4")
|
||||
dyn.pack()
|
||||
|
||||
def update():
|
||||
dyn.SetMenu(["new1","new2","new3","new4"], value="new option set")
|
||||
button = Button(top, text="Change option set", command=update)
|
||||
button.pack()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_dyn_option_menu)
|
||||
83
Darwin/lib/python3.5/idlelib/extend.txt
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
Writing an IDLE extension
|
||||
=========================
|
||||
|
||||
An IDLE extension can define new key bindings and menu entries for IDLE
|
||||
edit windows. There is a simple mechanism to load extensions when IDLE
|
||||
starts up and to attach them to each edit window. (It is also possible
|
||||
to make other changes to IDLE, but this must be done by editing the IDLE
|
||||
source code.)
|
||||
|
||||
The list of extensions loaded at startup time is configured by editing
|
||||
the file config-extensions.def. See below for details.
|
||||
|
||||
An IDLE extension is defined by a class. Methods of the class define
|
||||
actions that are invoked by event bindings or menu entries. Class (or
|
||||
instance) variables define the bindings and menu additions; these are
|
||||
automatically applied by IDLE when the extension is linked to an edit
|
||||
window.
|
||||
|
||||
An IDLE extension class is instantiated with a single argument,
|
||||
`editwin', an EditorWindow instance. The extension cannot assume much
|
||||
about this argument, but it is guaranteed to have the following instance
|
||||
variables:
|
||||
|
||||
text a Text instance (a widget)
|
||||
io an IOBinding instance (more about this later)
|
||||
flist the FileList instance (shared by all edit windows)
|
||||
|
||||
(There are a few more, but they are rarely useful.)
|
||||
|
||||
The extension class must not directly bind Window Manager (e.g. X) events.
|
||||
Rather, it must define one or more virtual events, e.g. <<zoom-height>>, and
|
||||
corresponding methods, e.g. zoom_height_event(). The virtual events will be
|
||||
bound to the corresponding methods, and Window Manager events can then be bound
|
||||
to the virtual events. (This indirection is done so that the key bindings can
|
||||
easily be changed, and so that other sources of virtual events can exist, such
|
||||
as menu entries.)
|
||||
|
||||
An extension can define menu entries. This is done with a class or instance
|
||||
variable named menudefs; it should be a list of pairs, where each pair is a
|
||||
menu name (lowercase) and a list of menu entries. Each menu entry is either
|
||||
None (to insert a separator entry) or a pair of strings (menu_label,
|
||||
virtual_event). Here, menu_label is the label of the menu entry, and
|
||||
virtual_event is the virtual event to be generated when the entry is selected.
|
||||
An underscore in the menu label is removed; the character following the
|
||||
underscore is displayed underlined, to indicate the shortcut character (for
|
||||
Windows).
|
||||
|
||||
At the moment, extensions cannot define whole new menus; they must define
|
||||
entries in existing menus. Some menus are not present on some windows; such
|
||||
entry definitions are then ignored, but key bindings are still applied. (This
|
||||
should probably be refined in the future.)
|
||||
|
||||
Extensions are not required to define menu entries for all the events they
|
||||
implement. (They are also not required to create keybindings, but in that
|
||||
case there must be empty bindings in cofig-extensions.def)
|
||||
|
||||
Here is a complete example:
|
||||
|
||||
class ZoomHeight:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
None, # Separator
|
||||
('_Zoom Height', '<<zoom-height>>'),
|
||||
])
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def zoom_height_event(self, event):
|
||||
"...Do what you want here..."
|
||||
|
||||
The final piece of the puzzle is the file "config-extensions.def", which is
|
||||
used to configure the loading of extensions and to establish key (or, more
|
||||
generally, event) bindings to the virtual events defined in the extensions.
|
||||
|
||||
See the comments at the top of config-extensions.def for information. It's
|
||||
currently necessary to manually modify that file to change IDLE's extension
|
||||
loading or extension key bindings.
|
||||
|
||||
For further information on binding refer to the Tkinter Resources web page at
|
||||
python.org and to the Tk Command "bind" man page.
|
||||
368
Darwin/lib/python3.5/idlelib/help.txt
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
[See the end of this file for ** TIPS ** on using IDLE !!]
|
||||
|
||||
IDLE is the Python IDE built with the tkinter GUI toolkit.
|
||||
|
||||
IDLE has the following features:
|
||||
-coded in 100% pure Python, using the tkinter GUI toolkit
|
||||
-cross-platform: works on Windows, Unix, and OS X
|
||||
-multi-window text editor with multiple undo, Python colorizing, smart indent,
|
||||
call tips, and many other features
|
||||
-Python shell window (a.k.a interactive interpreter)
|
||||
-debugger (not complete, but you can set breakpoints, view and step)
|
||||
|
||||
Menus:
|
||||
|
||||
IDLE has two window types the Shell window and the Editor window. It is
|
||||
possible to have multiple editor windows simultaneously. IDLE's
|
||||
menus dynamically change based on which window is currently selected. Each menu
|
||||
documented below indicates which window type it is associated with.
|
||||
|
||||
File Menu (Shell and Editor):
|
||||
|
||||
New File -- Create a new file editing window
|
||||
Open... -- Open an existing file
|
||||
Open Module... -- Open an existing module (searches sys.path)
|
||||
Recent Files... -- Open a list of recent files
|
||||
Class Browser -- Show classes and methods in current file
|
||||
Path Browser -- Show sys.path directories, modules, classes,
|
||||
and methods
|
||||
---
|
||||
Save -- Save current window to the associated file (unsaved
|
||||
windows have a * before and after the window title)
|
||||
|
||||
Save As... -- Save current window to new file, which becomes
|
||||
the associated file
|
||||
Save Copy As... -- Save current window to different file
|
||||
without changing the associated file
|
||||
---
|
||||
Print Window -- Print the current window
|
||||
---
|
||||
Close -- Close current window (asks to save if unsaved)
|
||||
Exit -- Close all windows, quit (asks to save if unsaved)
|
||||
|
||||
Edit Menu (Shell and Editor):
|
||||
|
||||
Undo -- Undo last change to current window
|
||||
(a maximum of 1000 changes may be undone)
|
||||
Redo -- Redo last undone change to current window
|
||||
---
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
Select All -- Select the entire contents of the edit buffer
|
||||
---
|
||||
Find... -- Open a search dialog box with many options
|
||||
Find Again -- Repeat last search
|
||||
Find Selection -- Search for the string in the selection
|
||||
Find in Files... -- Open a search dialog box for searching files
|
||||
Replace... -- Open a search-and-replace dialog box
|
||||
Go to Line -- Ask for a line number and show that line
|
||||
Expand Word -- Expand the word you have typed to match another
|
||||
word in the same buffer; repeat to get a
|
||||
different expansion
|
||||
Show Calltip -- After an unclosed parenthesis for a function, open
|
||||
a small window with function parameter hints
|
||||
Show Parens -- Highlight the surrounding parenthesis
|
||||
Show Completions -- Open a scroll window allowing selection keywords
|
||||
and attributes. (see '*TIPS*', below)
|
||||
|
||||
Format Menu (Editor window only):
|
||||
|
||||
Indent Region -- Shift selected lines right by the indent width
|
||||
(default 4 spaces)
|
||||
Dedent Region -- Shift selected lines left by the indent width
|
||||
(default 4 spaces)
|
||||
Comment Out Region -- Insert ## in front of selected lines
|
||||
Uncomment Region -- Remove leading # or ## from selected lines
|
||||
Tabify Region -- Turns *leading* stretches of spaces into tabs.
|
||||
(Note: We recommend using 4 space blocks to indent Python code.)
|
||||
Untabify Region -- Turn *all* tabs into the corrent number of spaces
|
||||
Toggle tabs -- Open a dialog to switch between indenting with
|
||||
spaces and tabs.
|
||||
New Indent Width... -- Open a dialog to change indent width. The
|
||||
accepted default by the Python community is 4
|
||||
spaces.
|
||||
Format Paragraph -- Reformat the current blank-line-separated
|
||||
paragraph. All lines in the paragraph will be
|
||||
formatted to less than 80 columns.
|
||||
---
|
||||
Strip trailing whitespace -- Removed any space characters after the end
|
||||
of the last non-space character
|
||||
|
||||
Run Menu (Editor window only):
|
||||
|
||||
Python Shell -- Open or wake up the Python shell window
|
||||
---
|
||||
Check Module -- Check the syntax of the module currently open in the
|
||||
Editor window. If the module has not been saved IDLE
|
||||
will prompt the user to save the code.
|
||||
Run Module -- Restart the shell to clean the environment, then
|
||||
execute the currently open module. If the module has
|
||||
not been saved IDLE will prompt the user to save the
|
||||
code.
|
||||
|
||||
Shell Menu (Shell window only):
|
||||
|
||||
View Last Restart -- Scroll the shell window to the last Shell restart
|
||||
Restart Shell -- Restart the shell to clean the environment
|
||||
|
||||
Debug Menu (Shell window only):
|
||||
|
||||
Go to File/Line -- Look around the insert point for a filename
|
||||
and line number, open the file, and show the line.
|
||||
Useful to view the source lines referenced in an
|
||||
exception traceback. Available in the context
|
||||
menu of the Shell window.
|
||||
Debugger (toggle) -- This feature is not complete and considered
|
||||
experimental. Run commands in the shell under the
|
||||
debugger.
|
||||
Stack Viewer -- Show the stack traceback of the last exception
|
||||
Auto-open Stack Viewer (toggle) -- Toggle automatically opening the
|
||||
stack viewer on unhandled
|
||||
exception
|
||||
|
||||
Options Menu (Shell and Editor):
|
||||
|
||||
Configure IDLE -- Open a configuration dialog. Fonts, indentation,
|
||||
keybindings, and color themes may be altered.
|
||||
Startup Preferences may be set, and additional Help
|
||||
sources can be specified. On OS X, open the
|
||||
configuration dialog by selecting Preferences
|
||||
in the application menu.
|
||||
|
||||
---
|
||||
Code Context (toggle) -- Open a pane at the top of the edit window
|
||||
which shows the block context of the section
|
||||
of code which is scrolling off the top or the
|
||||
window. This is not present in the Shell
|
||||
window only the Editor window.
|
||||
|
||||
Window Menu (Shell and Editor):
|
||||
|
||||
Zoom Height -- Toggles the window between normal size (40x80 initial
|
||||
setting) and maximum height. The initial size is in the Configure
|
||||
IDLE dialog under the general tab.
|
||||
---
|
||||
The rest of this menu lists the names of all open windows;
|
||||
select one to bring it to the foreground (deiconifying it if
|
||||
necessary).
|
||||
|
||||
Help Menu:
|
||||
|
||||
About IDLE -- Version, copyright, license, credits
|
||||
---
|
||||
IDLE Help -- Display this file which is a help file for IDLE
|
||||
detailing the menu options, basic editing and navigation,
|
||||
and other tips.
|
||||
Python Docs -- Access local Python documentation, if
|
||||
installed. Or will start a web browser and open
|
||||
docs.python.org showing the latest Python documentation.
|
||||
---
|
||||
Additional help sources may be added here with the Configure IDLE
|
||||
dialog under the General tab.
|
||||
|
||||
Editor context menu (Right-click / Control-click on OS X in Edit window):
|
||||
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
Set Breakpoint -- Sets a breakpoint. Breakpoints are only enabled
|
||||
when the debugger is open.
|
||||
Clear Breakpoint -- Clears the breakpoint on that line
|
||||
|
||||
Shell context menu (Right-click / Control-click on OS X in Shell window):
|
||||
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
---
|
||||
Go to file/line -- Same as in Debug menu
|
||||
|
||||
|
||||
** TIPS **
|
||||
==========
|
||||
|
||||
Additional Help Sources:
|
||||
|
||||
Windows users can Google on zopeshelf.chm to access Zope help files in
|
||||
the Windows help format. The Additional Help Sources feature of the
|
||||
configuration GUI supports .chm, along with any other filetypes
|
||||
supported by your browser. Supply a Menu Item title, and enter the
|
||||
location in the Help File Path slot of the New Help Source dialog. Use
|
||||
http:// and/or www. to identify external URLs, or download the file and
|
||||
browse for its path on your machine using the Browse button.
|
||||
|
||||
All users can access the extensive sources of help, including
|
||||
tutorials, available at docs.python.org. Selected URLs can be added
|
||||
or removed from the Help menu at any time using Configure IDLE.
|
||||
|
||||
Basic editing and navigation:
|
||||
|
||||
Backspace deletes char to the left; DEL deletes char to the right.
|
||||
Control-backspace deletes word left, Control-DEL deletes word right.
|
||||
Arrow keys and Page Up/Down move around.
|
||||
Control-left/right Arrow moves by words in a strange but useful way.
|
||||
Home/End go to begin/end of line.
|
||||
Control-Home/End go to begin/end of file.
|
||||
Some useful Emacs bindings are inherited from Tcl/Tk:
|
||||
Control-a beginning of line
|
||||
Control-e end of line
|
||||
Control-k kill line (but doesn't put it in clipboard)
|
||||
Control-l center window around the insertion point
|
||||
Standard keybindings (like Control-c to copy and Control-v to
|
||||
paste) may work. Keybindings are selected in the Configure IDLE
|
||||
dialog.
|
||||
|
||||
Automatic indentation:
|
||||
|
||||
After a block-opening statement, the next line is indented by 4 spaces
|
||||
(in the Python Shell window by one tab). After certain keywords
|
||||
(break, return etc.) the next line is dedented. In leading
|
||||
indentation, Backspace deletes up to 4 spaces if they are there. Tab
|
||||
inserts spaces (in the Python Shell window one tab), number depends on
|
||||
Indent Width. Currently tabs are restricted to four spaces due
|
||||
to Tcl/Tk limitations.
|
||||
|
||||
See also the indent/dedent region commands in the edit menu.
|
||||
|
||||
Completions:
|
||||
|
||||
Completions are supplied for functions, classes, and attributes of
|
||||
classes, both built-in and user-defined. Completions are also provided
|
||||
for filenames.
|
||||
|
||||
The AutoCompleteWindow (ACW) will open after a predefined delay
|
||||
(default is two seconds) after a '.' or (in a string) an os.sep is
|
||||
typed. If after one of those characters (plus zero or more other
|
||||
characters) a tab is typed the ACW will open immediately if a possible
|
||||
continuation is found.
|
||||
|
||||
If there is only one possible completion for the characters entered, a
|
||||
tab will supply that completion without opening the ACW.
|
||||
|
||||
'Show Completions' will force open a completions window, by default the
|
||||
Control-space keys will open a completions window. In an empty
|
||||
string, this will contain the files in the current directory. On a
|
||||
blank line, it will contain the built-in and user-defined functions and
|
||||
classes in the current name spaces, plus any modules imported. If some
|
||||
characters have been entered, the ACW will attempt to be more specific.
|
||||
|
||||
If string of characters is typed, the ACW selection will jump to the
|
||||
entry most closely matching those characters. Entering a tab will cause
|
||||
the longest non-ambiguous match to be entered in the Edit window or
|
||||
Shell. Two tabs in a row will supply the current ACW selection, as
|
||||
will return or a double click. Cursor keys, Page Up/Down, mouse
|
||||
selection, and the scroll wheel all operate on the ACW.
|
||||
|
||||
"Hidden" attributes can be accessed by typing the beginning of hidden
|
||||
name after a '.', e.g. '_'. This allows access to modules with
|
||||
'__all__' set, or to class-private attributes.
|
||||
|
||||
Completions and the 'Expand Word' facility can save a lot of typing!
|
||||
|
||||
Completions are currently limited to those in the namespaces. Names in
|
||||
an Editor window which are not via __main__ or sys.modules will not be
|
||||
found. Run the module once with your imports to correct this
|
||||
situation. Note that IDLE itself places quite a few modules in
|
||||
sys.modules, so much can be found by default, e.g. the re module.
|
||||
|
||||
If you don't like the ACW popping up unbidden, simply make the delay
|
||||
longer or disable the extension. Or another option is the delay could
|
||||
be set to zero. Another alternative to preventing ACW popups is to
|
||||
disable the call tips extension.
|
||||
|
||||
Python Shell window:
|
||||
|
||||
Control-c interrupts executing command.
|
||||
Control-d sends end-of-file; closes window if typed at >>> prompt.
|
||||
Alt-/ expand word is also useful to reduce typing.
|
||||
|
||||
Command history:
|
||||
|
||||
Alt-p retrieves previous command matching what you have typed. On OS X
|
||||
use Control-p.
|
||||
Alt-n retrieves next. On OS X use Control-n.
|
||||
Return while cursor is on a previous command retrieves that command.
|
||||
|
||||
Syntax colors:
|
||||
|
||||
The coloring is applied in a background "thread", so you may
|
||||
occasionally see uncolorized text. To change the color
|
||||
scheme, use the Configure IDLE / Highlighting dialog.
|
||||
|
||||
Python default syntax colors:
|
||||
|
||||
Keywords orange
|
||||
Builtins royal purple
|
||||
Strings green
|
||||
Comments red
|
||||
Definitions blue
|
||||
|
||||
Shell default colors:
|
||||
|
||||
Console output brown
|
||||
stdout blue
|
||||
stderr red
|
||||
stdin black
|
||||
|
||||
Other preferences:
|
||||
|
||||
The font preferences, highlighting, keys, and general preferences can
|
||||
be changed via the Configure IDLE menu option. Be sure to note that
|
||||
keys can be user defined, IDLE ships with four built in key sets. In
|
||||
addition a user can create a custom key set in the Configure IDLE
|
||||
dialog under the keys tab.
|
||||
|
||||
Command line usage:
|
||||
|
||||
Enter idle -h at the command prompt to get a usage message.
|
||||
|
||||
idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
|
||||
|
||||
-c command run this command
|
||||
-d enable debugger
|
||||
-e edit mode; arguments are files to be edited
|
||||
-s run $IDLESTARTUP or $PYTHONSTARTUP first
|
||||
-t title set title of shell window
|
||||
|
||||
If there are arguments:
|
||||
1. If -e is used, arguments are files opened for editing and sys.argv
|
||||
reflects the arguments passed to IDLE itself.
|
||||
2. Otherwise, if -c is used, all arguments are placed in
|
||||
sys.argv[1:...], with sys.argv[0] set to -c.
|
||||
3. Otherwise, if neither -e nor -c is used, the first argument is a
|
||||
script which is executed with the remaining arguments in
|
||||
sys.argv[1:...] and sys.argv[0] set to the script name. If the
|
||||
script name is -, no script is executed but an interactive Python
|
||||
session is started; the arguments are still available in sys.argv.
|
||||
|
||||
Running without a subprocess: (DEPRECATED in Python 3.4 see Issue 16123)
|
||||
|
||||
If IDLE is started with the -n command line switch it will run in a
|
||||
single process and will not create the subprocess which runs the RPC
|
||||
Python execution server. This can be useful if Python cannot create
|
||||
the subprocess or the RPC socket interface on your platform. However,
|
||||
in this mode user code is not isolated from IDLE itself. Also, the
|
||||
environment is not restarted when Run/Run Module (F5) is selected. If
|
||||
your code has been modified, you must reload() the affected modules and
|
||||
re-import any specific items (e.g. from foo import baz) if the changes
|
||||
are to take effect. For these reasons, it is preferable to run IDLE
|
||||
with the default subprocess if at all possible.
|
||||
|
||||
Extensions:
|
||||
|
||||
IDLE contains an extension facility. See the beginning of
|
||||
config-extensions.def in the idlelib directory for further information.
|
||||
The default extensions are currently:
|
||||
|
||||
FormatParagraph
|
||||
AutoExpand
|
||||
ZoomHeight
|
||||
ScriptBinding
|
||||
CallTips
|
||||
ParenMatch
|
||||
AutoComplete
|
||||
CodeContext
|
||||
4
Darwin/lib/python3.5/idlelib/idle.bat
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
@echo off
|
||||
rem Start IDLE using the appropriate Python interpreter
|
||||
set CURRDIR=%~dp0
|
||||
start "IDLE" "%CURRDIR%..\..\pythonw.exe" "%CURRDIR%idle.pyw" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||
11
Darwin/lib/python3.5/idlelib/idle.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import os.path
|
||||
import sys
|
||||
|
||||
# If we are working on a development version of IDLE, we need to prepend the
|
||||
# parent of this idlelib dir to sys.path. Otherwise, importing idlelib gets
|
||||
# the version installed with the Python used to call this module:
|
||||
idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, idlelib_dir)
|
||||
|
||||
import idlelib.PyShell
|
||||
idlelib.PyShell.main()
|
||||
17
Darwin/lib/python3.5/idlelib/idle.pyw
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
try:
|
||||
import idlelib.PyShell
|
||||
except ImportError:
|
||||
# IDLE is not installed, but maybe PyShell is on sys.path:
|
||||
from . import PyShell
|
||||
import os
|
||||
idledir = os.path.dirname(os.path.abspath(PyShell.__file__))
|
||||
if idledir != os.getcwd():
|
||||
# We're not in the IDLE directory, help the subprocess find run.py
|
||||
pypath = os.environ.get('PYTHONPATH', '')
|
||||
if pypath:
|
||||
os.environ['PYTHONPATH'] = pypath + ':' + idledir
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = idledir
|
||||
PyShell.main()
|
||||
else:
|
||||
idlelib.PyShell.main()
|
||||
143
Darwin/lib/python3.5/idlelib/idle_test/README.txt
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
README FOR IDLE TESTS IN IDLELIB.IDLE_TEST
|
||||
|
||||
0. Quick Start
|
||||
|
||||
Automated unit tests were added in 2.7 for Python 2.x and 3.3 for Python 3.x.
|
||||
To run the tests from a command line:
|
||||
|
||||
python -m test.test_idle
|
||||
|
||||
Human-mediated tests were added later in 2.7 and in 3.4.
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
|
||||
|
||||
1. Test Files
|
||||
|
||||
The idle directory, idlelib, has over 60 xyz.py files. The idle_test
|
||||
subdirectory should contain a test_xyz.py for each, where 'xyz' is lowercased
|
||||
even if xyz.py is not. Here is a possible template, with the blanks after after
|
||||
'.' and 'as', and before and after '_' to be filled in.
|
||||
|
||||
import unittest
|
||||
from test.support import requires
|
||||
import idlelib. as
|
||||
|
||||
class _Test(unittest.TestCase):
|
||||
|
||||
def test_(self):
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
||||
Add the following at the end of xyy.py, with the appropriate name added after
|
||||
'test_'. Some files already have something like this for htest. If so, insert
|
||||
the import and unittest.main lines before the htest lines.
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False)
|
||||
|
||||
|
||||
|
||||
2. GUI Tests
|
||||
|
||||
When run as part of the Python test suite, Idle gui tests need to run
|
||||
test.support.requires('gui') (test.test_support in 2.7). A test is a gui test
|
||||
if it creates a Tk root or master object either directly or indirectly by
|
||||
instantiating a tkinter or idle class. For the benefit of test processes that
|
||||
either have no graphical environment available or are not allowed to use it, gui
|
||||
tests must be 'guarded' by "requires('gui')" in a setUp function or method.
|
||||
This will typically be setUpClass.
|
||||
|
||||
To avoid interfering with other gui tests, all gui objects must be destroyed and
|
||||
deleted by the end of the test. Widgets, such as a Tk root, created in a setUpX
|
||||
function, should be destroyed in the corresponding tearDownX. Module and class
|
||||
widget attributes should also be deleted..
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
|
||||
Requires('gui') causes the test(s) it guards to be skipped if any of
|
||||
a few conditions are met:
|
||||
|
||||
- The tests are being run by regrtest.py, and it was started without enabling
|
||||
the "gui" resource with the "-u" command line option.
|
||||
|
||||
- The tests are being run on Windows by a service that is not allowed to
|
||||
interact with the graphical environment.
|
||||
|
||||
- The tests are being run on Mac OSX in a process that cannot make a window
|
||||
manager connection.
|
||||
|
||||
- tkinter.Tk cannot be successfully instantiated for some reason.
|
||||
|
||||
- test.support.use_resources has been set by something other than
|
||||
regrtest.py and does not contain "gui".
|
||||
|
||||
Tests of non-gui operations should avoid creating tk widgets. Incidental uses of
|
||||
tk variables and messageboxes can be replaced by the mock classes in
|
||||
idle_test/mock_tk.py. The mock text handles some uses of the tk Text widget.
|
||||
|
||||
|
||||
3. Running Unit Tests
|
||||
|
||||
Assume that xyz.py and test_xyz.py both end with a unittest.main() call.
|
||||
Running either from an Idle editor runs all tests in the test_xyz file with the
|
||||
version of Python running Idle. Test output appears in the Shell window. The
|
||||
'verbosity=2' option lists all test methods in the file, which is appropriate
|
||||
when developing tests. The 'exit=False' option is needed in xyx.py files when an
|
||||
htest follows.
|
||||
|
||||
The following command lines also run all test methods, including
|
||||
gui tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle' start
|
||||
Idle and so cannot run tests.)
|
||||
|
||||
python -m idlelib.xyz
|
||||
python -m idlelib.idle_test.test_xyz
|
||||
|
||||
The following runs all idle_test/test_*.py tests interactively.
|
||||
|
||||
>>> import unittest
|
||||
>>> unittest.main('idlelib.idle_test', verbosity=2)
|
||||
|
||||
The following run all Idle tests at a command line. Option '-v' is the same as
|
||||
'verbosity=2'. (For 2.7, replace 'test' in the second line with
|
||||
'test.regrtest'.)
|
||||
|
||||
python -m unittest -v idlelib.idle_test
|
||||
python -m test -v -ugui test_idle
|
||||
python -m test.test_idle
|
||||
|
||||
The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests,
|
||||
which is also imported into test.test_idle. Normally, neither file should be
|
||||
changed when working on individual test modules. The third command runs
|
||||
unittest indirectly through regrtest. The same happens when the entire test
|
||||
suite is run with 'python -m test'. So that command must work for buildbots
|
||||
to stay green. Idle tests must not disturb the environment in a way that
|
||||
makes other tests fail (issue 18081).
|
||||
|
||||
To run an individual Testcase or test method, extend the dotted name given to
|
||||
unittest on the command line.
|
||||
|
||||
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
|
||||
|
||||
|
||||
4. Human-mediated Tests
|
||||
|
||||
Human-mediated tests are widget tests that cannot be automated but need human
|
||||
verification. They are contained in idlelib/idle_test/htest.py, which has
|
||||
instructions. (Some modules need an auxiliary function, identified with # htest
|
||||
# on the header line.) The set is about complete, though some tests need
|
||||
improvement. To run all htests, run the htest file from an editor or from the
|
||||
command line with:
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
9
Darwin/lib/python3.5/idlelib/idle_test/__init__.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from os.path import dirname
|
||||
|
||||
def load_tests(loader, standard_tests, pattern):
|
||||
this_dir = dirname(__file__)
|
||||
top_dir = dirname(dirname(this_dir))
|
||||
package_tests = loader.discover(start_dir=this_dir, pattern='test*.py',
|
||||
top_level_dir=top_dir)
|
||||
standard_tests.addTests(package_tests)
|
||||
return standard_tests
|
||||
407
Darwin/lib/python3.5/idlelib/idle_test/htest.py
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
'''Run human tests of Idle's window, dialog, and popup widgets.
|
||||
|
||||
run(*tests)
|
||||
Create a master Tk window. Within that, run each callable in tests
|
||||
after finding the matching test spec in this file. If tests is empty,
|
||||
run an htest for each spec dict in this file after finding the matching
|
||||
callable in the module named in the spec. Close the window to skip or
|
||||
end the test.
|
||||
|
||||
In a tested module, let X be a global name bound to a callable (class
|
||||
or function) whose .__name__ attrubute is also X (the usual situation).
|
||||
The first parameter of X must be 'parent'. When called, the parent
|
||||
argument will be the root window. X must create a child Toplevel
|
||||
window (or subclass thereof). The Toplevel may be a test widget or
|
||||
dialog, in which case the callable is the corresonding class. Or the
|
||||
Toplevel may contain the widget to be tested or set up a context in
|
||||
which a test widget is invoked. In this latter case, the callable is a
|
||||
wrapper function that sets up the Toplevel and other objects. Wrapper
|
||||
function names, such as _editor_window', should start with '_'.
|
||||
|
||||
|
||||
End the module with
|
||||
|
||||
if __name__ == '__main__':
|
||||
<unittest, if there is one>
|
||||
from idlelib.idle_test.htest import run
|
||||
run(X)
|
||||
|
||||
To have wrapper functions and test invocation code ignored by coveragepy
|
||||
reports, put '# htest #' on the def statement header line.
|
||||
|
||||
def _wrapper(parent): # htest #
|
||||
|
||||
Also make sure that the 'if __name__' line matches the above. Then have
|
||||
make sure that .coveragerc includes the following.
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
.*# htest #
|
||||
if __name__ == .__main__.:
|
||||
|
||||
(The "." instead of "'" is intentional and necessary.)
|
||||
|
||||
|
||||
To run any X, this file must contain a matching instance of the
|
||||
following template, with X.__name__ prepended to '_spec'.
|
||||
When all tests are run, the prefix is use to get X.
|
||||
|
||||
_spec = {
|
||||
'file': '',
|
||||
'kwds': {'title': ''},
|
||||
'msg': ""
|
||||
}
|
||||
|
||||
file (no .py): run() imports file.py.
|
||||
kwds: augmented with {'parent':root} and passed to X as **kwds.
|
||||
title: an example kwd; some widgets need this, delete if not.
|
||||
msg: master window hints about testing the widget.
|
||||
|
||||
|
||||
Modules and classes not being tested at the moment:
|
||||
PyShell.PyShellEditorWindow
|
||||
Debugger.Debugger
|
||||
AutoCompleteWindow.AutoCompleteWindow
|
||||
OutputWindow.OutputWindow (indirectly being tested with grep test)
|
||||
'''
|
||||
|
||||
from importlib import import_module
|
||||
from idlelib.macosxSupport import _initializeTkVariantTests
|
||||
import tkinter as tk
|
||||
|
||||
AboutDialog_spec = {
|
||||
'file': 'aboutDialog',
|
||||
'kwds': {'title': 'aboutDialog test',
|
||||
'_htest': True,
|
||||
},
|
||||
'msg': "Test every button. Ensure Python, TK and IDLE versions "
|
||||
"are correctly displayed.\n [Close] to exit.",
|
||||
}
|
||||
|
||||
_calltip_window_spec = {
|
||||
'file': 'CallTipWindow',
|
||||
'kwds': {},
|
||||
'msg': "Typing '(' should display a calltip.\n"
|
||||
"Typing ') should hide the calltip.\n"
|
||||
}
|
||||
|
||||
_class_browser_spec = {
|
||||
'file': 'ClassBrowser',
|
||||
'kwds': {},
|
||||
'msg': "Inspect names of module, class(with superclass if "
|
||||
"applicable), methods and functions.\nToggle nested items.\n"
|
||||
"Double clicking on items prints a traceback for an exception "
|
||||
"that is ignored."
|
||||
}
|
||||
ConfigExtensionsDialog_spec = {
|
||||
'file': 'configDialog',
|
||||
'kwds': {'title': 'Test Extension Configuration',
|
||||
'_htest': True,},
|
||||
'msg': "IDLE extensions dialog.\n"
|
||||
"\n[Ok] to close the dialog.[Apply] to apply the settings and "
|
||||
"and [Cancel] to revert all changes.\nRe-run the test to ensure "
|
||||
"changes made have persisted."
|
||||
}
|
||||
|
||||
_color_delegator_spec = {
|
||||
'file': 'ColorDelegator',
|
||||
'kwds': {},
|
||||
'msg': "The text is sample Python code.\n"
|
||||
"Ensure components like comments, keywords, builtins,\n"
|
||||
"string, definitions, and break are correctly colored.\n"
|
||||
"The default color scheme is in idlelib/config-highlight.def"
|
||||
}
|
||||
|
||||
ConfigDialog_spec = {
|
||||
'file': 'configDialog',
|
||||
'kwds': {'title': 'ConfigDialogTest',
|
||||
'_htest': True,},
|
||||
'msg': "IDLE preferences dialog.\n"
|
||||
"In the 'Fonts/Tabs' tab, changing font face, should update the "
|
||||
"font face of the text in the area below it.\nIn the "
|
||||
"'Highlighting' tab, try different color schemes. Clicking "
|
||||
"items in the sample program should update the choices above it."
|
||||
"\nIn the 'Keys' and 'General' tab, test settings of interest."
|
||||
"\n[Ok] to close the dialog.[Apply] to apply the settings and "
|
||||
"and [Cancel] to revert all changes.\nRe-run the test to ensure "
|
||||
"changes made have persisted."
|
||||
}
|
||||
|
||||
# TODO Improve message
|
||||
_dyn_option_menu_spec = {
|
||||
'file': 'dynOptionMenuWidget',
|
||||
'kwds': {},
|
||||
'msg': "Select one of the many options in the 'old option set'.\n"
|
||||
"Click the button to change the option set.\n"
|
||||
"Select one of the many options in the 'new option set'."
|
||||
}
|
||||
|
||||
# TODO edit wrapper
|
||||
_editor_window_spec = {
|
||||
'file': 'EditorWindow',
|
||||
'kwds': {},
|
||||
'msg': "Test editor functions of interest.\n"
|
||||
"Best to close editor first."
|
||||
}
|
||||
|
||||
GetCfgSectionNameDialog_spec = {
|
||||
'file': 'configSectionNameDialog',
|
||||
'kwds': {'title':'Get Name',
|
||||
'message':'Enter something',
|
||||
'used_names': {'abc'},
|
||||
'_htest': True},
|
||||
'msg': "After the text entered with [Ok] is stripped, <nothing>, "
|
||||
"'abc', or more that 30 chars are errors.\n"
|
||||
"Close 'Get Name' with a valid entry (printed to Shell), "
|
||||
"[Cancel], or [X]",
|
||||
}
|
||||
|
||||
GetHelpSourceDialog_spec = {
|
||||
'file': 'configHelpSourceEdit',
|
||||
'kwds': {'title': 'Get helpsource',
|
||||
'_htest': True},
|
||||
'msg': "Enter menu item name and help file path\n "
|
||||
"<nothing> and more than 30 chars are invalid menu item names.\n"
|
||||
"<nothing>, file does not exist are invalid path items.\n"
|
||||
"Test for incomplete web address for help file path.\n"
|
||||
"A valid entry will be printed to shell with [0k].\n"
|
||||
"[Cancel] will print None to shell",
|
||||
}
|
||||
|
||||
# Update once issue21519 is resolved.
|
||||
GetKeysDialog_spec = {
|
||||
'file': 'keybindingDialog',
|
||||
'kwds': {'title': 'Test keybindings',
|
||||
'action': 'find-again',
|
||||
'currentKeySequences': [''] ,
|
||||
'_htest': True,
|
||||
},
|
||||
'msg': "Test for different key modifier sequences.\n"
|
||||
"<nothing> is invalid.\n"
|
||||
"No modifier key is invalid.\n"
|
||||
"Shift key with [a-z],[0-9], function key, move key, tab, space"
|
||||
"is invalid.\nNo validity checking if advanced key binding "
|
||||
"entry is used."
|
||||
}
|
||||
|
||||
_grep_dialog_spec = {
|
||||
'file': 'GrepDialog',
|
||||
'kwds': {},
|
||||
'msg': "Click the 'Show GrepDialog' button.\n"
|
||||
"Test the various 'Find-in-files' functions.\n"
|
||||
"The results should be displayed in a new '*Output*' window.\n"
|
||||
"'Right-click'->'Goto file/line' anywhere in the search results "
|
||||
"should open that file \nin a new EditorWindow."
|
||||
}
|
||||
|
||||
_help_dialog_spec = {
|
||||
'file': 'EditorWindow',
|
||||
'kwds': {},
|
||||
'msg': "If the help text displays, this works.\n"
|
||||
"Text is selectable. Window is scrollable."
|
||||
}
|
||||
|
||||
_io_binding_spec = {
|
||||
'file': 'IOBinding',
|
||||
'kwds': {},
|
||||
'msg': "Test the following bindings\n"
|
||||
"<Control-o> to display open window from file dialog.\n"
|
||||
"<Control-s> to save the file\n"
|
||||
}
|
||||
|
||||
_multi_call_spec = {
|
||||
'file': 'MultiCall',
|
||||
'kwds': {},
|
||||
'msg': "The following actions should trigger a print to console or IDLE"
|
||||
" Shell.\nEntering and leaving the text area, key entry, "
|
||||
"<Control-Key>,\n<Alt-Key-a>, <Control-Key-a>, "
|
||||
"<Alt-Control-Key-a>, \n<Control-Button-1>, <Alt-Button-1> and "
|
||||
"focusing out of the window\nare sequences to be tested."
|
||||
}
|
||||
|
||||
_multistatus_bar_spec = {
|
||||
'file': 'MultiStatusBar',
|
||||
'kwds': {},
|
||||
'msg': "Ensure presence of multi-status bar below text area.\n"
|
||||
"Click 'Update Status' to change the multi-status text"
|
||||
}
|
||||
|
||||
_object_browser_spec = {
|
||||
'file': 'ObjectBrowser',
|
||||
'kwds': {},
|
||||
'msg': "Double click on items upto the lowest level.\n"
|
||||
"Attributes of the objects and related information "
|
||||
"will be displayed side-by-side at each level."
|
||||
}
|
||||
|
||||
_path_browser_spec = {
|
||||
'file': 'PathBrowser',
|
||||
'kwds': {},
|
||||
'msg': "Test for correct display of all paths in sys.path.\n"
|
||||
"Toggle nested items upto the lowest level.\n"
|
||||
"Double clicking on an item prints a traceback\n"
|
||||
"for an exception that is ignored."
|
||||
}
|
||||
|
||||
_percolator_spec = {
|
||||
'file': 'Percolator',
|
||||
'kwds': {},
|
||||
'msg': "There are two tracers which can be toggled using a checkbox.\n"
|
||||
"Toggling a tracer 'on' by checking it should print tracer"
|
||||
"output to the console or to the IDLE shell.\n"
|
||||
"If both the tracers are 'on', the output from the tracer which "
|
||||
"was switched 'on' later, should be printed first\n"
|
||||
"Test for actions like text entry, and removal."
|
||||
}
|
||||
|
||||
_replace_dialog_spec = {
|
||||
'file': 'ReplaceDialog',
|
||||
'kwds': {},
|
||||
'msg': "Click the 'Replace' button.\n"
|
||||
"Test various replace options in the 'Replace dialog'.\n"
|
||||
"Click [Close] or [X] to close the 'Replace Dialog'."
|
||||
}
|
||||
|
||||
_search_dialog_spec = {
|
||||
'file': 'SearchDialog',
|
||||
'kwds': {},
|
||||
'msg': "Click the 'Search' button.\n"
|
||||
"Test various search options in the 'Search dialog'.\n"
|
||||
"Click [Close] or [X] to close the 'Search Dialog'."
|
||||
}
|
||||
|
||||
_scrolled_list_spec = {
|
||||
'file': 'ScrolledList',
|
||||
'kwds': {},
|
||||
'msg': "You should see a scrollable list of items\n"
|
||||
"Selecting (clicking) or double clicking an item "
|
||||
"prints the name to the console or Idle shell.\n"
|
||||
"Right clicking an item will display a popup."
|
||||
}
|
||||
|
||||
_stack_viewer_spec = {
|
||||
'file': 'StackViewer',
|
||||
'kwds': {},
|
||||
'msg': "A stacktrace for a NameError exception.\n"
|
||||
"Expand 'idlelib ...' and '<locals>'.\n"
|
||||
"Check that exc_value, exc_tb, and exc_type are correct.\n"
|
||||
}
|
||||
|
||||
_tabbed_pages_spec = {
|
||||
'file': 'tabbedpages',
|
||||
'kwds': {},
|
||||
'msg': "Toggle between the two tabs 'foo' and 'bar'\n"
|
||||
"Add a tab by entering a suitable name for it.\n"
|
||||
"Remove an existing tab by entering its name.\n"
|
||||
"Remove all existing tabs.\n"
|
||||
"<nothing> is an invalid add page and remove page name.\n"
|
||||
}
|
||||
|
||||
TextViewer_spec = {
|
||||
'file': 'textView',
|
||||
'kwds': {'title': 'Test textView',
|
||||
'text':'The quick brown fox jumps over the lazy dog.\n'*35,
|
||||
'_htest': True},
|
||||
'msg': "Test for read-only property of text.\n"
|
||||
"Text is selectable. Window is scrollable.",
|
||||
}
|
||||
|
||||
_tooltip_spec = {
|
||||
'file': 'ToolTip',
|
||||
'kwds': {},
|
||||
'msg': "Place mouse cursor over both the buttons\n"
|
||||
"A tooltip should appear with some text."
|
||||
}
|
||||
|
||||
_tree_widget_spec = {
|
||||
'file': 'TreeWidget',
|
||||
'kwds': {},
|
||||
'msg': "The canvas is scrollable.\n"
|
||||
"Click on folders upto to the lowest level."
|
||||
}
|
||||
|
||||
_undo_delegator_spec = {
|
||||
'file': 'UndoDelegator',
|
||||
'kwds': {},
|
||||
'msg': "Click [Undo] to undo any action.\n"
|
||||
"Click [Redo] to redo any action.\n"
|
||||
"Click [Dump] to dump the current state "
|
||||
"by printing to the console or the IDLE shell.\n"
|
||||
}
|
||||
|
||||
_widget_redirector_spec = {
|
||||
'file': 'WidgetRedirector',
|
||||
'kwds': {},
|
||||
'msg': "Every text insert should be printed to the console."
|
||||
"or the IDLE shell."
|
||||
}
|
||||
|
||||
def run(*tests):
|
||||
root = tk.Tk()
|
||||
root.title('IDLE htest')
|
||||
root.resizable(0, 0)
|
||||
_initializeTkVariantTests(root)
|
||||
|
||||
# a scrollable Label like constant width text widget.
|
||||
frameLabel = tk.Frame(root, padx=10)
|
||||
frameLabel.pack()
|
||||
text = tk.Text(frameLabel, wrap='word')
|
||||
text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70)
|
||||
scrollbar = tk.Scrollbar(frameLabel, command=text.yview)
|
||||
text.config(yscrollcommand=scrollbar.set)
|
||||
scrollbar.pack(side='right', fill='y', expand=False)
|
||||
text.pack(side='left', fill='both', expand=True)
|
||||
|
||||
test_list = [] # List of tuples of the form (spec, callable widget)
|
||||
if tests:
|
||||
for test in tests:
|
||||
test_spec = globals()[test.__name__ + '_spec']
|
||||
test_spec['name'] = test.__name__
|
||||
test_list.append((test_spec, test))
|
||||
else:
|
||||
for k, d in globals().items():
|
||||
if k.endswith('_spec'):
|
||||
test_name = k[:-5]
|
||||
test_spec = d
|
||||
test_spec['name'] = test_name
|
||||
mod = import_module('idlelib.' + test_spec['file'])
|
||||
test = getattr(mod, test_name)
|
||||
test_list.append((test_spec, test))
|
||||
|
||||
test_name = tk.StringVar('')
|
||||
callable_object = None
|
||||
test_kwds = None
|
||||
|
||||
def next():
|
||||
|
||||
nonlocal test_name, callable_object, test_kwds
|
||||
if len(test_list) == 1:
|
||||
next_button.pack_forget()
|
||||
test_spec, callable_object = test_list.pop()
|
||||
test_kwds = test_spec['kwds']
|
||||
test_kwds['parent'] = root
|
||||
test_name.set('Test ' + test_spec['name'])
|
||||
|
||||
text.configure(state='normal') # enable text editing
|
||||
text.delete('1.0','end')
|
||||
text.insert("1.0",test_spec['msg'])
|
||||
text.configure(state='disabled') # preserve read-only property
|
||||
|
||||
def run_test():
|
||||
widget = callable_object(**test_kwds)
|
||||
try:
|
||||
print(widget.result)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
button = tk.Button(root, textvariable=test_name, command=run_test)
|
||||
button.pack()
|
||||
next_button = tk.Button(root, text="Next", command=next)
|
||||
next_button.pack()
|
||||
|
||||
next()
|
||||
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
55
Darwin/lib/python3.5/idlelib/idle_test/mock_idle.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
'''Mock classes that imitate idlelib modules or classes.
|
||||
|
||||
Attributes and methods will be added as needed for tests.
|
||||
'''
|
||||
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
|
||||
class Func:
|
||||
'''Mock function captures args and returns result set by test.
|
||||
|
||||
Attributes:
|
||||
self.called - records call even if no args, kwds passed.
|
||||
self.result - set by init, returned by call.
|
||||
self.args - captures positional arguments.
|
||||
self.kwds - captures keyword arguments.
|
||||
|
||||
Most common use will probably be to mock methods.
|
||||
Mock_tk.Var and Mbox_func are special variants of this.
|
||||
'''
|
||||
def __init__(self, result=None):
|
||||
self.called = False
|
||||
self.result = result
|
||||
self.args = None
|
||||
self.kwds = None
|
||||
def __call__(self, *args, **kwds):
|
||||
self.called = True
|
||||
self.args = args
|
||||
self.kwds = kwds
|
||||
if isinstance(self.result, BaseException):
|
||||
raise self.result
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
class Editor:
|
||||
'''Minimally imitate EditorWindow.EditorWindow class.
|
||||
'''
|
||||
def __init__(self, flist=None, filename=None, key=None, root=None):
|
||||
self.text = Text()
|
||||
self.undo = UndoDelegator()
|
||||
|
||||
def get_selection_indices(self):
|
||||
first = self.text.index('1.0')
|
||||
last = self.text.index('end')
|
||||
return first, last
|
||||
|
||||
|
||||
class UndoDelegator:
|
||||
'''Minimally imitate UndoDelegator,UndoDelegator class.
|
||||
'''
|
||||
# A real undo block is only needed for user interaction.
|
||||
def undo_block_start(*args):
|
||||
pass
|
||||
def undo_block_stop(*args):
|
||||
pass
|
||||
298
Darwin/lib/python3.5/idlelib/idle_test/mock_tk.py
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
"""Classes that replace tkinter gui objects used by an object being tested.
|
||||
|
||||
A gui object is anything with a master or parent parameter, which is
|
||||
typically required in spite of what the doc strings say.
|
||||
"""
|
||||
|
||||
class Event:
|
||||
'''Minimal mock with attributes for testing event handlers.
|
||||
|
||||
This is not a gui object, but is used as an argument for callbacks
|
||||
that access attributes of the event passed. If a callback ignores
|
||||
the event, other than the fact that is happened, pass 'event'.
|
||||
|
||||
Keyboard, mouse, window, and other sources generate Event instances.
|
||||
Event instances have the following attributes: serial (number of
|
||||
event), time (of event), type (of event as number), widget (in which
|
||||
event occurred), and x,y (position of mouse). There are other
|
||||
attributes for specific events, such as keycode for key events.
|
||||
tkinter.Event.__doc__ has more but is still not complete.
|
||||
'''
|
||||
def __init__(self, **kwds):
|
||||
"Create event with attributes needed for test"
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
class Var:
|
||||
"Use for String/Int/BooleanVar: incomplete"
|
||||
def __init__(self, master=None, value=None, name=None):
|
||||
self.master = master
|
||||
self.value = value
|
||||
self.name = name
|
||||
def set(self, value):
|
||||
self.value = value
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
class Mbox_func:
|
||||
"""Generic mock for messagebox functions, which all have the same signature.
|
||||
|
||||
Instead of displaying a message box, the mock's call method saves the
|
||||
arguments as instance attributes, which test functions can then examime.
|
||||
The test can set the result returned to ask function
|
||||
"""
|
||||
def __init__(self, result=None):
|
||||
self.result = result # Return None for all show funcs
|
||||
def __call__(self, title, message, *args, **kwds):
|
||||
# Save all args for possible examination by tester
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.args = args
|
||||
self.kwds = kwds
|
||||
return self.result # Set by tester for ask functions
|
||||
|
||||
class Mbox:
|
||||
"""Mock for tkinter.messagebox with an Mbox_func for each function.
|
||||
|
||||
This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x.
|
||||
Example usage in test_module.py for testing functions in module.py:
|
||||
---
|
||||
from idlelib.idle_test.mock_tk import Mbox
|
||||
import module
|
||||
|
||||
orig_mbox = module.tkMessageBox
|
||||
showerror = Mbox.showerror # example, for attribute access in test methods
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
module.tkMessageBox = Mbox
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
module.tkMessageBox = orig_mbox
|
||||
---
|
||||
For 'ask' functions, set func.result return value before calling the method
|
||||
that uses the message function. When tkMessageBox functions are the
|
||||
only gui alls in a method, this replacement makes the method gui-free,
|
||||
"""
|
||||
askokcancel = Mbox_func() # True or False
|
||||
askquestion = Mbox_func() # 'yes' or 'no'
|
||||
askretrycancel = Mbox_func() # True or False
|
||||
askyesno = Mbox_func() # True or False
|
||||
askyesnocancel = Mbox_func() # True, False, or None
|
||||
showerror = Mbox_func() # None
|
||||
showinfo = Mbox_func() # None
|
||||
showwarning = Mbox_func() # None
|
||||
|
||||
from _tkinter import TclError
|
||||
|
||||
class Text:
|
||||
"""A semi-functional non-gui replacement for tkinter.Text text editors.
|
||||
|
||||
The mock's data model is that a text is a list of \n-terminated lines.
|
||||
The mock adds an empty string at the beginning of the list so that the
|
||||
index of actual lines start at 1, as with Tk. The methods never see this.
|
||||
Tk initializes files with a terminal \n that cannot be deleted. It is
|
||||
invisible in the sense that one cannot move the cursor beyond it.
|
||||
|
||||
This class is only tested (and valid) with strings of ascii chars.
|
||||
For testing, we are not concerned with Tk Text's treatment of,
|
||||
for instance, 0-width characters or character + accent.
|
||||
"""
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
'''Initialize mock, non-gui, text-only Text widget.
|
||||
|
||||
At present, all args are ignored. Almost all affect visual behavior.
|
||||
There are just a few Text-only options that affect text behavior.
|
||||
'''
|
||||
self.data = ['', '\n']
|
||||
|
||||
def index(self, index):
|
||||
"Return string version of index decoded according to current text."
|
||||
return "%s.%s" % self._decode(index, endflag=1)
|
||||
|
||||
def _decode(self, index, endflag=0):
|
||||
"""Return a (line, char) tuple of int indexes into self.data.
|
||||
|
||||
This implements .index without converting the result back to a string.
|
||||
The result is constrained by the number of lines and linelengths of
|
||||
self.data. For many indexes, the result is initially (1, 0).
|
||||
|
||||
The input index may have any of several possible forms:
|
||||
* line.char float: converted to 'line.char' string;
|
||||
* 'line.char' string, where line and char are decimal integers;
|
||||
* 'line.char lineend', where lineend='lineend' (and char is ignored);
|
||||
* 'line.end', where end='end' (same as above);
|
||||
* 'insert', the positions before terminal \n;
|
||||
* 'end', whose meaning depends on the endflag passed to ._endex.
|
||||
* 'sel.first' or 'sel.last', where sel is a tag -- not implemented.
|
||||
"""
|
||||
if isinstance(index, (float, bytes)):
|
||||
index = str(index)
|
||||
try:
|
||||
index=index.lower()
|
||||
except AttributeError:
|
||||
raise TclError('bad text index "%s"' % index) from None
|
||||
|
||||
lastline = len(self.data) - 1 # same as number of text lines
|
||||
if index == 'insert':
|
||||
return lastline, len(self.data[lastline]) - 1
|
||||
elif index == 'end':
|
||||
return self._endex(endflag)
|
||||
|
||||
line, char = index.split('.')
|
||||
line = int(line)
|
||||
|
||||
# Out of bounds line becomes first or last ('end') index
|
||||
if line < 1:
|
||||
return 1, 0
|
||||
elif line > lastline:
|
||||
return self._endex(endflag)
|
||||
|
||||
linelength = len(self.data[line]) -1 # position before/at \n
|
||||
if char.endswith(' lineend') or char == 'end':
|
||||
return line, linelength
|
||||
# Tk requires that ignored chars before ' lineend' be valid int
|
||||
|
||||
# Out of bounds char becomes first or last index of line
|
||||
char = int(char)
|
||||
if char < 0:
|
||||
char = 0
|
||||
elif char > linelength:
|
||||
char = linelength
|
||||
return line, char
|
||||
|
||||
def _endex(self, endflag):
|
||||
'''Return position for 'end' or line overflow corresponding to endflag.
|
||||
|
||||
-1: position before terminal \n; for .insert(), .delete
|
||||
0: position after terminal \n; for .get, .delete index 1
|
||||
1: same viewed as beginning of non-existent next line (for .index)
|
||||
'''
|
||||
n = len(self.data)
|
||||
if endflag == 1:
|
||||
return n, 0
|
||||
else:
|
||||
n -= 1
|
||||
return n, len(self.data[n]) + endflag
|
||||
|
||||
|
||||
def insert(self, index, chars):
|
||||
"Insert chars before the character at index."
|
||||
|
||||
if not chars: # ''.splitlines() is [], not ['']
|
||||
return
|
||||
chars = chars.splitlines(True)
|
||||
if chars[-1][-1] == '\n':
|
||||
chars.append('')
|
||||
line, char = self._decode(index, -1)
|
||||
before = self.data[line][:char]
|
||||
after = self.data[line][char:]
|
||||
self.data[line] = before + chars[0]
|
||||
self.data[line+1:line+1] = chars[1:]
|
||||
self.data[line+len(chars)-1] += after
|
||||
|
||||
|
||||
def get(self, index1, index2=None):
|
||||
"Return slice from index1 to index2 (default is 'index1+1')."
|
||||
|
||||
startline, startchar = self._decode(index1)
|
||||
if index2 is None:
|
||||
endline, endchar = startline, startchar+1
|
||||
else:
|
||||
endline, endchar = self._decode(index2)
|
||||
|
||||
if startline == endline:
|
||||
return self.data[startline][startchar:endchar]
|
||||
else:
|
||||
lines = [self.data[startline][startchar:]]
|
||||
for i in range(startline+1, endline):
|
||||
lines.append(self.data[i])
|
||||
lines.append(self.data[endline][:endchar])
|
||||
return ''.join(lines)
|
||||
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
'''Delete slice from index1 to index2 (default is 'index1+1').
|
||||
|
||||
Adjust default index2 ('index+1) for line ends.
|
||||
Do not delete the terminal \n at the very end of self.data ([-1][-1]).
|
||||
'''
|
||||
startline, startchar = self._decode(index1, -1)
|
||||
if index2 is None:
|
||||
if startchar < len(self.data[startline])-1:
|
||||
# not deleting \n
|
||||
endline, endchar = startline, startchar+1
|
||||
elif startline < len(self.data) - 1:
|
||||
# deleting non-terminal \n, convert 'index1+1 to start of next line
|
||||
endline, endchar = startline+1, 0
|
||||
else:
|
||||
# do not delete terminal \n if index1 == 'insert'
|
||||
return
|
||||
else:
|
||||
endline, endchar = self._decode(index2, -1)
|
||||
# restricting end position to insert position excludes terminal \n
|
||||
|
||||
if startline == endline and startchar < endchar:
|
||||
self.data[startline] = self.data[startline][:startchar] + \
|
||||
self.data[startline][endchar:]
|
||||
elif startline < endline:
|
||||
self.data[startline] = self.data[startline][:startchar] + \
|
||||
self.data[endline][endchar:]
|
||||
startline += 1
|
||||
for i in range(startline, endline+1):
|
||||
del self.data[startline]
|
||||
|
||||
def compare(self, index1, op, index2):
|
||||
line1, char1 = self._decode(index1)
|
||||
line2, char2 = self._decode(index2)
|
||||
if op == '<':
|
||||
return line1 < line2 or line1 == line2 and char1 < char2
|
||||
elif op == '<=':
|
||||
return line1 < line2 or line1 == line2 and char1 <= char2
|
||||
elif op == '>':
|
||||
return line1 > line2 or line1 == line2 and char1 > char2
|
||||
elif op == '>=':
|
||||
return line1 > line2 or line1 == line2 and char1 >= char2
|
||||
elif op == '==':
|
||||
return line1 == line2 and char1 == char2
|
||||
elif op == '!=':
|
||||
return line1 != line2 or char1 != char2
|
||||
else:
|
||||
raise TclError('''bad comparison operator "%s":'''
|
||||
'''must be <, <=, ==, >=, >, or !=''' % op)
|
||||
|
||||
# The following Text methods normally do something and return None.
|
||||
# Whether doing nothing is sufficient for a test will depend on the test.
|
||||
|
||||
def mark_set(self, name, index):
|
||||
"Set mark *name* before the character at index."
|
||||
pass
|
||||
|
||||
def mark_unset(self, *markNames):
|
||||
"Delete all marks in markNames."
|
||||
|
||||
def tag_remove(self, tagName, index1, index2=None):
|
||||
"Remove tag tagName from all characters between index1 and index2."
|
||||
pass
|
||||
|
||||
# The following Text methods affect the graphics screen and return None.
|
||||
# Doing nothing should always be sufficient for tests.
|
||||
|
||||
def scan_dragto(self, x, y):
|
||||
"Adjust the view of the text according to scan_mark"
|
||||
|
||||
def scan_mark(self, x, y):
|
||||
"Remember the current X, Y coordinates."
|
||||
|
||||
def see(self, index):
|
||||
"Scroll screen to make the character at INDEX is visible."
|
||||
pass
|
||||
|
||||
# The following is a Misc method inherited by Text.
|
||||
# It should properly go in a Misc mock, but is included here for now.
|
||||
|
||||
def bind(sequence=None, func=None, add=None):
|
||||
"Bind to this widget at event sequence a call to function func."
|
||||
pass
|
||||
143
Darwin/lib/python3.5/idlelib/idle_test/test_autocomplete.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
|
||||
import idlelib.AutoComplete as ac
|
||||
import idlelib.AutoCompleteWindow as acw
|
||||
import idlelib.macosxSupport as mac
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Event
|
||||
|
||||
class AutoCompleteWindow:
|
||||
def complete():
|
||||
return
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, root, text):
|
||||
self.root = root
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.context_use_ps1 = True
|
||||
|
||||
|
||||
class AutoCompleteTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
mac.setupApp(cls.root, None)
|
||||
cls.text = Text(cls.root)
|
||||
cls.editor = DummyEditwin(cls.root, cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.text
|
||||
del cls.editor
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.editor.text.delete('1.0', 'end')
|
||||
self.autocomplete = ac.AutoComplete(self.editor)
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.autocomplete.editwin, self.editor)
|
||||
|
||||
def test_make_autocomplete_window(self):
|
||||
testwin = self.autocomplete._make_autocomplete_window()
|
||||
self.assertIsInstance(testwin, acw.AutoCompleteWindow)
|
||||
|
||||
def test_remove_autocomplete_window(self):
|
||||
self.autocomplete.autocompletewindow = (
|
||||
self.autocomplete._make_autocomplete_window())
|
||||
self.autocomplete._remove_autocomplete_window()
|
||||
self.assertIsNone(self.autocomplete.autocompletewindow)
|
||||
|
||||
def test_force_open_completions_event(self):
|
||||
# Test that force_open_completions_event calls _open_completions
|
||||
o_cs = Func()
|
||||
self.autocomplete.open_completions = o_cs
|
||||
self.autocomplete.force_open_completions_event('event')
|
||||
self.assertEqual(o_cs.args, (True, False, True))
|
||||
|
||||
def test_try_open_completions_event(self):
|
||||
Equal = self.assertEqual
|
||||
autocomplete = self.autocomplete
|
||||
trycompletions = self.autocomplete.try_open_completions_event
|
||||
o_c_l = Func()
|
||||
autocomplete._open_completions_later = o_c_l
|
||||
|
||||
# _open_completions_later should not be called with no text in editor
|
||||
trycompletions('event')
|
||||
Equal(o_c_l.args, None)
|
||||
|
||||
# _open_completions_later should be called with COMPLETE_ATTRIBUTES (1)
|
||||
self.text.insert('1.0', 're.')
|
||||
trycompletions('event')
|
||||
Equal(o_c_l.args, (False, False, False, 1))
|
||||
|
||||
# _open_completions_later should be called with COMPLETE_FILES (2)
|
||||
self.text.delete('1.0', 'end')
|
||||
self.text.insert('1.0', '"./Lib/')
|
||||
trycompletions('event')
|
||||
Equal(o_c_l.args, (False, False, False, 2))
|
||||
|
||||
def test_autocomplete_event(self):
|
||||
Equal = self.assertEqual
|
||||
autocomplete = self.autocomplete
|
||||
|
||||
# Test that the autocomplete event is ignored if user is pressing a
|
||||
# modifier key in addition to the tab key
|
||||
ev = Event(mc_state=True)
|
||||
self.assertIsNone(autocomplete.autocomplete_event(ev))
|
||||
del ev.mc_state
|
||||
|
||||
# If autocomplete window is open, complete() method is called
|
||||
self.text.insert('1.0', 're.')
|
||||
# This must call autocomplete._make_autocomplete_window()
|
||||
Equal(self.autocomplete.autocomplete_event(ev), 'break')
|
||||
|
||||
# If autocomplete window is not active or does not exist,
|
||||
# open_completions is called. Return depends on its return.
|
||||
autocomplete._remove_autocomplete_window()
|
||||
o_cs = Func() # .result = None
|
||||
autocomplete.open_completions = o_cs
|
||||
Equal(self.autocomplete.autocomplete_event(ev), None)
|
||||
Equal(o_cs.args, (False, True, True))
|
||||
o_cs.result = True
|
||||
Equal(self.autocomplete.autocomplete_event(ev), 'break')
|
||||
Equal(o_cs.args, (False, True, True))
|
||||
|
||||
def test_open_completions_later(self):
|
||||
# Test that autocomplete._delayed_completion_id is set
|
||||
pass
|
||||
|
||||
def test_delayed_open_completions(self):
|
||||
# Test that autocomplete._delayed_completion_id set to None and that
|
||||
# open_completions only called if insertion index is the same as
|
||||
# _delayed_completion_index
|
||||
pass
|
||||
|
||||
def test_open_completions(self):
|
||||
# Test completions of files and attributes as well as non-completion
|
||||
# of errors
|
||||
pass
|
||||
|
||||
def test_fetch_completions(self):
|
||||
# Test that fetch_completions returns 2 lists:
|
||||
# For attribute completion, a large list containing all variables, and
|
||||
# a small list containing non-private variables.
|
||||
# For file completion, a large list containing all files in the path,
|
||||
# and a small list containing files that do not start with '.'
|
||||
pass
|
||||
|
||||
def test_get_entity(self):
|
||||
# Test that a name is in the namespace of sys.modules and
|
||||
# __main__.__dict__
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
141
Darwin/lib/python3.5/idlelib/idle_test/test_autoexpand.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
"""Unit tests for idlelib.AutoExpand"""
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Text, Tk
|
||||
#from idlelib.idle_test.mock_tk import Text
|
||||
from idlelib.AutoExpand import AutoExpand
|
||||
|
||||
|
||||
class Dummy_Editwin:
|
||||
# AutoExpand.__init__ only needs .text
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
class AutoExpandTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
if 'tkinter' in str(Text):
|
||||
requires('gui')
|
||||
cls.tk = Tk()
|
||||
cls.text = Text(cls.tk)
|
||||
else:
|
||||
cls.text = Text()
|
||||
cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if hasattr(cls, 'tk'):
|
||||
cls.tk.destroy()
|
||||
del cls.tk
|
||||
del cls.text, cls.auto_expand
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_get_prevword(self):
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
equal = self.assertEqual
|
||||
|
||||
equal(previous(), '')
|
||||
|
||||
text.insert('insert', 't')
|
||||
equal(previous(), 't')
|
||||
|
||||
text.insert('insert', 'his')
|
||||
equal(previous(), 'this')
|
||||
|
||||
text.insert('insert', ' ')
|
||||
equal(previous(), '')
|
||||
|
||||
text.insert('insert', 'is')
|
||||
equal(previous(), 'is')
|
||||
|
||||
text.insert('insert', '\nsample\nstring')
|
||||
equal(previous(), 'string')
|
||||
|
||||
text.delete('3.0', 'insert')
|
||||
equal(previous(), '')
|
||||
|
||||
text.delete('1.0', 'end')
|
||||
equal(previous(), '')
|
||||
|
||||
def test_before_only(self):
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
self.text.insert('insert', 'ab ac bx ad ab a')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ad')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_after_only(self):
|
||||
# Also add punctuation 'noise' that should be ignored.
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya')
|
||||
text.mark_set('insert', '1.1')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'ad')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_both_before_after(self):
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
text.insert('insert', 'ab xy yz\n')
|
||||
text.insert('insert', 'a ac by ac')
|
||||
|
||||
text.mark_set('insert', '2.1')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ac', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_other_expand_cases(self):
|
||||
text = self.text
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
# no expansion candidate found
|
||||
equal(self.auto_expand.getwords(), [])
|
||||
equal(expand('event'), 'break')
|
||||
|
||||
text.insert('insert', 'bx cy dz a')
|
||||
equal(self.auto_expand.getwords(), [])
|
||||
|
||||
# reset state by successfully expanding once
|
||||
# move cursor to another position and expand again
|
||||
text.insert('insert', 'ac xy a ac ad a')
|
||||
text.mark_set('insert', '1.7')
|
||||
expand('event')
|
||||
initial_state = self.auto_expand.state
|
||||
text.mark_set('insert', '1.end')
|
||||
expand('event')
|
||||
new_state = self.auto_expand.state
|
||||
self.assertNotEqual(initial_state, new_state)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
177
Darwin/lib/python3.5/idlelib/idle_test/test_calltips.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import unittest
|
||||
import idlelib.CallTips as ct
|
||||
import textwrap
|
||||
import types
|
||||
|
||||
default_tip = ct._default_callable_argspec
|
||||
|
||||
# Test Class TC is used in multiple get_argspec test methods
|
||||
class TC():
|
||||
'doc'
|
||||
tip = "(ai=None, *b)"
|
||||
def __init__(self, ai=None, *b): 'doc'
|
||||
__init__.tip = "(self, ai=None, *b)"
|
||||
def t1(self): 'doc'
|
||||
t1.tip = "(self)"
|
||||
def t2(self, ai, b=None): 'doc'
|
||||
t2.tip = "(self, ai, b=None)"
|
||||
def t3(self, ai, *args): 'doc'
|
||||
t3.tip = "(self, ai, *args)"
|
||||
def t4(self, *args): 'doc'
|
||||
t4.tip = "(self, *args)"
|
||||
def t5(self, ai, b=None, *args, **kw): 'doc'
|
||||
t5.tip = "(self, ai, b=None, *args, **kw)"
|
||||
def t6(no, self): 'doc'
|
||||
t6.tip = "(no, self)"
|
||||
def __call__(self, ci): 'doc'
|
||||
__call__.tip = "(self, ci)"
|
||||
# attaching .tip to wrapped methods does not work
|
||||
@classmethod
|
||||
def cm(cls, a): 'doc'
|
||||
@staticmethod
|
||||
def sm(b): 'doc'
|
||||
|
||||
tc = TC()
|
||||
|
||||
signature = ct.get_argspec # 2.7 and 3.x use different functions
|
||||
class Get_signatureTest(unittest.TestCase):
|
||||
# The signature function must return a string, even if blank.
|
||||
# Test a variety of objects to be sure that none cause it to raise
|
||||
# (quite aside from getting as correct an answer as possible).
|
||||
# The tests of builtins may break if inspect or the docstrings change,
|
||||
# but a red buildbot is better than a user crash (as has happened).
|
||||
# For a simple mismatch, change the expected output to the actual.
|
||||
|
||||
def test_builtins(self):
|
||||
|
||||
# Python class that inherits builtin methods
|
||||
class List(list): "List() doc"
|
||||
# Simulate builtin with no docstring for default tip test
|
||||
class SB: __call__ = None
|
||||
|
||||
def gtest(obj, out):
|
||||
self.assertEqual(signature(obj), out)
|
||||
|
||||
if List.__doc__ is not None:
|
||||
gtest(List, List.__doc__)
|
||||
gtest(list.__new__,
|
||||
'Create and return a new object. See help(type) for accurate signature.')
|
||||
gtest(list.__init__,
|
||||
'Initialize self. See help(type(self)) for accurate signature.')
|
||||
append_doc = "L.append(object) -> None -- append object to end"
|
||||
gtest(list.append, append_doc)
|
||||
gtest([].append, append_doc)
|
||||
gtest(List.append, append_doc)
|
||||
|
||||
gtest(types.MethodType, "method(function, instance)")
|
||||
gtest(SB(), default_tip)
|
||||
|
||||
def test_signature_wrap(self):
|
||||
if textwrap.TextWrapper.__doc__ is not None:
|
||||
self.assertEqual(signature(textwrap.TextWrapper), '''\
|
||||
(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
|
||||
replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
|
||||
drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
|
||||
placeholder=' [...]')''')
|
||||
|
||||
def test_docline_truncation(self):
|
||||
def f(): pass
|
||||
f.__doc__ = 'a'*300
|
||||
self.assertEqual(signature(f), '()\n' + 'a' * (ct._MAX_COLS-3) + '...')
|
||||
|
||||
def test_multiline_docstring(self):
|
||||
# Test fewer lines than max.
|
||||
self.assertEqual(signature(list),
|
||||
"list() -> new empty list\n"
|
||||
"list(iterable) -> new list initialized from iterable's items")
|
||||
|
||||
# Test max lines
|
||||
self.assertEqual(signature(bytes), '''\
|
||||
bytes(iterable_of_ints) -> bytes
|
||||
bytes(string, encoding[, errors]) -> bytes
|
||||
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
|
||||
bytes(int) -> bytes object of size given by the parameter initialized with null bytes
|
||||
bytes() -> empty bytes object''')
|
||||
|
||||
# Test more than max lines
|
||||
def f(): pass
|
||||
f.__doc__ = 'a\n' * 15
|
||||
self.assertEqual(signature(f), '()' + '\na' * ct._MAX_LINES)
|
||||
|
||||
def test_functions(self):
|
||||
def t1(): 'doc'
|
||||
t1.tip = "()"
|
||||
def t2(a, b=None): 'doc'
|
||||
t2.tip = "(a, b=None)"
|
||||
def t3(a, *args): 'doc'
|
||||
t3.tip = "(a, *args)"
|
||||
def t4(*args): 'doc'
|
||||
t4.tip = "(*args)"
|
||||
def t5(a, b=None, *args, **kw): 'doc'
|
||||
t5.tip = "(a, b=None, *args, **kw)"
|
||||
|
||||
doc = '\ndoc' if t1.__doc__ is not None else ''
|
||||
for func in (t1, t2, t3, t4, t5, TC):
|
||||
self.assertEqual(signature(func), func.tip + doc)
|
||||
|
||||
def test_methods(self):
|
||||
doc = '\ndoc' if TC.__doc__ is not None else ''
|
||||
for meth in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.__call__):
|
||||
self.assertEqual(signature(meth), meth.tip + doc)
|
||||
self.assertEqual(signature(TC.cm), "(a)" + doc)
|
||||
self.assertEqual(signature(TC.sm), "(b)" + doc)
|
||||
|
||||
def test_bound_methods(self):
|
||||
# test that first parameter is correctly removed from argspec
|
||||
doc = '\ndoc' if TC.__doc__ is not None else ''
|
||||
for meth, mtip in ((tc.t1, "()"), (tc.t4, "(*args)"), (tc.t6, "(self)"),
|
||||
(tc.__call__, '(ci)'), (tc, '(ci)'), (TC.cm, "(a)"),):
|
||||
self.assertEqual(signature(meth), mtip + doc)
|
||||
|
||||
def test_starred_parameter(self):
|
||||
# test that starred first parameter is *not* removed from argspec
|
||||
class C:
|
||||
def m1(*args): pass
|
||||
def m2(**kwds): pass
|
||||
c = C()
|
||||
for meth, mtip in ((C.m1, '(*args)'), (c.m1, "(*args)"),
|
||||
(C.m2, "(**kwds)"), (c.m2, "(**kwds)"),):
|
||||
self.assertEqual(signature(meth), mtip)
|
||||
|
||||
def test_non_ascii_name(self):
|
||||
# test that re works to delete a first parameter name that
|
||||
# includes non-ascii chars, such as various forms of A.
|
||||
uni = "(A\u0391\u0410\u05d0\u0627\u0905\u1e00\u3042, a)"
|
||||
assert ct._first_param.sub('', uni) == '(a)'
|
||||
|
||||
def test_no_docstring(self):
|
||||
def nd(s):
|
||||
pass
|
||||
TC.nd = nd
|
||||
self.assertEqual(signature(nd), "(s)")
|
||||
self.assertEqual(signature(TC.nd), "(s)")
|
||||
self.assertEqual(signature(tc.nd), "()")
|
||||
|
||||
def test_attribute_exception(self):
|
||||
class NoCall:
|
||||
def __getattr__(self, name):
|
||||
raise BaseException
|
||||
class Call(NoCall):
|
||||
def __call__(self, ci):
|
||||
pass
|
||||
for meth, mtip in ((NoCall, default_tip), (Call, default_tip),
|
||||
(NoCall(), ''), (Call(), '(ci)')):
|
||||
self.assertEqual(signature(meth), mtip)
|
||||
|
||||
def test_non_callables(self):
|
||||
for obj in (0, 0.0, '0', b'0', [], {}):
|
||||
self.assertEqual(signature(obj), '')
|
||||
|
||||
class Get_entityTest(unittest.TestCase):
|
||||
def test_bad_entity(self):
|
||||
self.assertIsNone(ct.get_entity('1/0'))
|
||||
def test_good_entity(self):
|
||||
self.assertIs(ct.get_entity('int'), int)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
75
Darwin/lib/python3.5/idlelib/idle_test/test_config_name.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
"""Unit tests for idlelib.configSectionNameDialog"""
|
||||
import unittest
|
||||
from idlelib.idle_test.mock_tk import Var, Mbox
|
||||
from idlelib import configSectionNameDialog as name_dialog_module
|
||||
|
||||
name_dialog = name_dialog_module.GetCfgSectionNameDialog
|
||||
|
||||
class Dummy_name_dialog:
|
||||
# Mock for testing the following methods of name_dialog
|
||||
name_ok = name_dialog.name_ok
|
||||
Ok = name_dialog.Ok
|
||||
Cancel = name_dialog.Cancel
|
||||
# Attributes, constant or variable, needed for tests
|
||||
used_names = ['used']
|
||||
name = Var()
|
||||
result = None
|
||||
destroyed = False
|
||||
def destroy(self):
|
||||
self.destroyed = True
|
||||
|
||||
# name_ok calls Mbox.showerror if name is not ok
|
||||
orig_mbox = name_dialog_module.tkMessageBox
|
||||
showerror = Mbox.showerror
|
||||
|
||||
class ConfigNameTest(unittest.TestCase):
|
||||
dialog = Dummy_name_dialog()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
name_dialog_module.tkMessageBox = Mbox
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
name_dialog_module.tkMessageBox = orig_mbox
|
||||
|
||||
def test_blank_name(self):
|
||||
self.dialog.name.set(' ')
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('No', showerror.message)
|
||||
|
||||
def test_used_name(self):
|
||||
self.dialog.name.set('used')
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('use', showerror.message)
|
||||
|
||||
def test_long_name(self):
|
||||
self.dialog.name.set('good'*8)
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('too long', showerror.message)
|
||||
|
||||
def test_good_name(self):
|
||||
self.dialog.name.set(' good ')
|
||||
showerror.title = 'No Error' # should not be called
|
||||
self.assertEqual(self.dialog.name_ok(), 'good')
|
||||
self.assertEqual(showerror.title, 'No Error')
|
||||
|
||||
def test_ok(self):
|
||||
self.dialog.destroyed = False
|
||||
self.dialog.name.set('good')
|
||||
self.dialog.Ok()
|
||||
self.assertEqual(self.dialog.result, 'good')
|
||||
self.assertTrue(self.dialog.destroyed)
|
||||
|
||||
def test_cancel(self):
|
||||
self.dialog.destroyed = False
|
||||
self.dialog.Cancel()
|
||||
self.assertEqual(self.dialog.result, '')
|
||||
self.assertTrue(self.dialog.destroyed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
32
Darwin/lib/python3.5/idlelib/idle_test/test_configdialog.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'''Unittests for idlelib/configHandler.py
|
||||
|
||||
Coverage: 46% just by creating dialog. The other half is change code.
|
||||
|
||||
'''
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from idlelib.configDialog import ConfigDialog
|
||||
from idlelib.macosxSupport import _initializeTkVariantTests
|
||||
|
||||
|
||||
class ConfigDialogTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
_initializeTkVariantTests(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_dialog(self):
|
||||
d=ConfigDialog(self.root, 'Test', _utest=True)
|
||||
d.destroy()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
37
Darwin/lib/python3.5/idlelib/idle_test/test_delegator.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import unittest
|
||||
from idlelib.Delegator import Delegator
|
||||
|
||||
class DelegatorTest(unittest.TestCase):
|
||||
|
||||
def test_mydel(self):
|
||||
# test a simple use scenario
|
||||
|
||||
# initialize
|
||||
mydel = Delegator(int)
|
||||
self.assertIs(mydel.delegate, int)
|
||||
self.assertEqual(mydel._Delegator__cache, set())
|
||||
|
||||
# add an attribute:
|
||||
self.assertRaises(AttributeError, mydel.__getattr__, 'xyz')
|
||||
bl = mydel.bit_length
|
||||
self.assertIs(bl, int.bit_length)
|
||||
self.assertIs(mydel.__dict__['bit_length'], int.bit_length)
|
||||
self.assertEqual(mydel._Delegator__cache, {'bit_length'})
|
||||
|
||||
# add a second attribute
|
||||
mydel.numerator
|
||||
self.assertEqual(mydel._Delegator__cache, {'bit_length', 'numerator'})
|
||||
|
||||
# delete the second (which, however, leaves it in the name cache)
|
||||
del mydel.numerator
|
||||
self.assertNotIn('numerator', mydel.__dict__)
|
||||
self.assertIn('numerator', mydel._Delegator__cache)
|
||||
|
||||
# reset by calling .setdelegate, which calls .resetcache
|
||||
mydel.setdelegate(float)
|
||||
self.assertIs(mydel.delegate, float)
|
||||
self.assertNotIn('bit_length', mydel.__dict__)
|
||||
self.assertEqual(mydel._Delegator__cache, set())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
16
Darwin/lib/python3.5/idlelib/idle_test/test_editor.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import unittest
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
from test.support import requires
|
||||
|
||||
class Editor_func_test(unittest.TestCase):
|
||||
def test_filename_to_unicode(self):
|
||||
func = EditorWindow._filename_to_unicode
|
||||
class dummy(): filesystemencoding = 'utf-8'
|
||||
pairs = (('abc', 'abc'), ('a\U00011111c', 'a\ufffdc'),
|
||||
(b'abc', 'abc'), (b'a\xf0\x91\x84\x91c', 'a\ufffdc'))
|
||||
for inp, out in pairs:
|
||||
self.assertEqual(func(dummy, inp), out)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
377
Darwin/lib/python3.5/idlelib/idle_test/test_formatparagraph.py
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
# Test the functions and main class method of FormatParagraph.py
|
||||
import unittest
|
||||
from idlelib import FormatParagraph as fp
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
from tkinter import Tk, Text
|
||||
from test.support import requires
|
||||
|
||||
|
||||
class Is_Get_Test(unittest.TestCase):
|
||||
"""Test the is_ and get_ functions"""
|
||||
test_comment = '# This is a comment'
|
||||
test_nocomment = 'This is not a comment'
|
||||
trailingws_comment = '# This is a comment '
|
||||
leadingws_comment = ' # This is a comment'
|
||||
leadingws_nocomment = ' This is not a comment'
|
||||
|
||||
def test_is_all_white(self):
|
||||
self.assertTrue(fp.is_all_white(''))
|
||||
self.assertTrue(fp.is_all_white('\t\n\r\f\v'))
|
||||
self.assertFalse(fp.is_all_white(self.test_comment))
|
||||
|
||||
def test_get_indent(self):
|
||||
Equal = self.assertEqual
|
||||
Equal(fp.get_indent(self.test_comment), '')
|
||||
Equal(fp.get_indent(self.trailingws_comment), '')
|
||||
Equal(fp.get_indent(self.leadingws_comment), ' ')
|
||||
Equal(fp.get_indent(self.leadingws_nocomment), ' ')
|
||||
|
||||
def test_get_comment_header(self):
|
||||
Equal = self.assertEqual
|
||||
# Test comment strings
|
||||
Equal(fp.get_comment_header(self.test_comment), '#')
|
||||
Equal(fp.get_comment_header(self.trailingws_comment), '#')
|
||||
Equal(fp.get_comment_header(self.leadingws_comment), ' #')
|
||||
# Test non-comment strings
|
||||
Equal(fp.get_comment_header(self.leadingws_nocomment), ' ')
|
||||
Equal(fp.get_comment_header(self.test_nocomment), '')
|
||||
|
||||
|
||||
class FindTest(unittest.TestCase):
|
||||
"""Test the find_paragraph function in FormatParagraph.
|
||||
|
||||
Using the runcase() function, find_paragraph() is called with 'mark' set at
|
||||
multiple indexes before and inside the test paragraph.
|
||||
|
||||
It appears that code with the same indentation as a quoted string is grouped
|
||||
as part of the same paragraph, which is probably incorrect behavior.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
cls.text = Text()
|
||||
|
||||
def runcase(self, inserttext, stopline, expected):
|
||||
# Check that find_paragraph returns the expected paragraph when
|
||||
# the mark index is set to beginning, middle, end of each line
|
||||
# up to but not including the stop line
|
||||
text = self.text
|
||||
text.insert('1.0', inserttext)
|
||||
for line in range(1, stopline):
|
||||
linelength = int(text.index("%d.end" % line).split('.')[1])
|
||||
for col in (0, linelength//2, linelength):
|
||||
tempindex = "%d.%d" % (line, col)
|
||||
self.assertEqual(fp.find_paragraph(text, tempindex), expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_find_comment(self):
|
||||
comment = (
|
||||
"# Comment block with no blank lines before\n"
|
||||
"# Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('1.0', '3.0', '#', comment[0:58]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment block with whitespace line before and after\n"
|
||||
"# Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 4, ('2.0', '4.0', '#', comment[1:70]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Indented comment block with whitespace before and after\n"
|
||||
" # Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 4, ('2.0', '4.0', ' #', comment[1:82]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Single line comment\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:23]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Single line comment with leading whitespace\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:51]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment immediately followed by code\n"
|
||||
"x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:40]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Indented comment immediately followed by code\n"
|
||||
"x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:53]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment immediately followed by indented code\n"
|
||||
" x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:49]))
|
||||
|
||||
def test_find_paragraph(self):
|
||||
teststring = (
|
||||
'"""String with no blank lines before\n'
|
||||
'String line\n'
|
||||
'"""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 4, ('1.0', '4.0', '', teststring[0:53]))
|
||||
|
||||
teststring = (
|
||||
"\n"
|
||||
'"""String with whitespace line before and after\n'
|
||||
'String line.\n'
|
||||
'"""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 5, ('2.0', '5.0', '', teststring[1:66]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
' """Indented string with whitespace before and after\n'
|
||||
' Comment string.\n'
|
||||
' """\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 5, ('2.0', '5.0', ' ', teststring[1:85]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
'"""Single line string."""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 3, ('2.0', '3.0', '', teststring[1:27]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
' """Single line string with leading whitespace."""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 3, ('2.0', '3.0', ' ', teststring[1:55]))
|
||||
|
||||
|
||||
class ReformatFunctionTest(unittest.TestCase):
|
||||
"""Test the reformat_paragraph function without the editor window."""
|
||||
|
||||
def test_reformat_paragrah(self):
|
||||
Equal = self.assertEqual
|
||||
reform = fp.reformat_paragraph
|
||||
hw = "O hello world"
|
||||
Equal(reform(' ', 1), ' ')
|
||||
Equal(reform("Hello world", 20), "Hello world")
|
||||
|
||||
# Test without leading newline
|
||||
Equal(reform(hw, 1), "O\nhello\nworld")
|
||||
Equal(reform(hw, 6), "O\nhello\nworld")
|
||||
Equal(reform(hw, 7), "O hello\nworld")
|
||||
Equal(reform(hw, 12), "O hello\nworld")
|
||||
Equal(reform(hw, 13), "O hello world")
|
||||
|
||||
# Test with leading newline
|
||||
hw = "\nO hello world"
|
||||
Equal(reform(hw, 1), "\nO\nhello\nworld")
|
||||
Equal(reform(hw, 6), "\nO\nhello\nworld")
|
||||
Equal(reform(hw, 7), "\nO hello\nworld")
|
||||
Equal(reform(hw, 12), "\nO hello\nworld")
|
||||
Equal(reform(hw, 13), "\nO hello world")
|
||||
|
||||
|
||||
class ReformatCommentTest(unittest.TestCase):
|
||||
"""Test the reformat_comment function without the editor window."""
|
||||
|
||||
def test_reformat_comment(self):
|
||||
Equal = self.assertEqual
|
||||
|
||||
# reformat_comment formats to a minimum of 20 characters
|
||||
test_string = (
|
||||
" \"\"\"this is a test of a reformat for a triple quoted string"
|
||||
" will it reformat to less than 70 characters for me?\"\"\"")
|
||||
result = fp.reformat_comment(test_string, 70, " ")
|
||||
expected = (
|
||||
" \"\"\"this is a test of a reformat for a triple quoted string will it\n"
|
||||
" reformat to less than 70 characters for me?\"\"\"")
|
||||
Equal(result, expected)
|
||||
|
||||
test_comment = (
|
||||
"# this is a test of a reformat for a triple quoted string will "
|
||||
"it reformat to less than 70 characters for me?")
|
||||
result = fp.reformat_comment(test_comment, 70, "#")
|
||||
expected = (
|
||||
"# this is a test of a reformat for a triple quoted string will it\n"
|
||||
"# reformat to less than 70 characters for me?")
|
||||
Equal(result, expected)
|
||||
|
||||
|
||||
class FormatClassTest(unittest.TestCase):
|
||||
def test_init_close(self):
|
||||
instance = fp.FormatParagraph('editor')
|
||||
self.assertEqual(instance.editwin, 'editor')
|
||||
instance.close()
|
||||
self.assertEqual(instance.editwin, None)
|
||||
|
||||
|
||||
# For testing format_paragraph_event, Initialize FormatParagraph with
|
||||
# a mock Editor with .text and .get_selection_indices. The text must
|
||||
# be a Text wrapper that adds two methods
|
||||
|
||||
# A real EditorWindow creates unneeded, time-consuming baggage and
|
||||
# sometimes emits shutdown warnings like this:
|
||||
# "warning: callback failed in WindowList <class '_tkinter.TclError'>
|
||||
# : invalid command name ".55131368.windows".
|
||||
# Calling EditorWindow._close in tearDownClass prevents this but causes
|
||||
# other problems (windows left open).
|
||||
|
||||
class TextWrapper:
|
||||
def __init__(self, master):
|
||||
self.text = Text(master=master)
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.text, name)
|
||||
def undo_block_start(self): pass
|
||||
def undo_block_stop(self): pass
|
||||
|
||||
class Editor:
|
||||
def __init__(self, root):
|
||||
self.text = TextWrapper(root)
|
||||
get_selection_indices = EditorWindow. get_selection_indices
|
||||
|
||||
class FormatEventTest(unittest.TestCase):
|
||||
"""Test the formatting of text inside a Text widget.
|
||||
|
||||
This is done with FormatParagraph.format.paragraph_event,
|
||||
which calls functions in the module as appropriate.
|
||||
"""
|
||||
test_string = (
|
||||
" '''this is a test of a reformat for a triple "
|
||||
"quoted string will it reformat to less than 70 "
|
||||
"characters for me?'''\n")
|
||||
multiline_test_string = (
|
||||
" '''The first line is under the max width.\n"
|
||||
" The second line's length is way over the max width. It goes "
|
||||
"on and on until it is over 100 characters long.\n"
|
||||
" Same thing with the third line. It is also way over the max "
|
||||
"width, but FormatParagraph will fix it.\n"
|
||||
" '''\n")
|
||||
multiline_test_comment = (
|
||||
"# The first line is under the max width.\n"
|
||||
"# The second line's length is way over the max width. It goes on "
|
||||
"and on until it is over 100 characters long.\n"
|
||||
"# Same thing with the third line. It is also way over the max "
|
||||
"width, but FormatParagraph will fix it.\n"
|
||||
"# The fourth line is short like the first line.")
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
editor = Editor(root=cls.root)
|
||||
cls.text = editor.text.text # Test code does not need the wrapper.
|
||||
cls.formatter = fp.FormatParagraph(editor).format_paragraph_event
|
||||
# Sets the insert mark just after the re-wrapped and inserted text.
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
del cls.text
|
||||
del cls.formatter
|
||||
|
||||
def test_short_line(self):
|
||||
self.text.insert('1.0', "Short line\n")
|
||||
self.formatter("Dummy")
|
||||
self.assertEqual(self.text.get('1.0', 'insert'), "Short line\n" )
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_long_line(self):
|
||||
text = self.text
|
||||
|
||||
# Set cursor ('insert' mark) to '1.0', within text.
|
||||
text.insert('1.0', self.test_string)
|
||||
text.mark_set('insert', '1.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
# find function includes \n
|
||||
expected = (
|
||||
" '''this is a test of a reformat for a triple quoted string will it\n"
|
||||
" reformat to less than 70 characters for me?'''\n") # yes
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# Select from 1.11 to line end.
|
||||
text.insert('1.0', self.test_string)
|
||||
text.tag_add('sel', '1.11', '1.end')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
# selection excludes \n
|
||||
expected = (
|
||||
" '''this is a test of a reformat for a triple quoted string will it reformat\n"
|
||||
" to less than 70 characters for me?'''") # no
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_multiple_lines(self):
|
||||
text = self.text
|
||||
# Select 2 long lines.
|
||||
text.insert('1.0', self.multiline_test_string)
|
||||
text.tag_add('sel', '2.0', '4.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('2.0', 'insert')
|
||||
expected = (
|
||||
" The second line's length is way over the max width. It goes on and\n"
|
||||
" on until it is over 100 characters long. Same thing with the third\n"
|
||||
" line. It is also way over the max width, but FormatParagraph will\n"
|
||||
" fix it.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_comment_block(self):
|
||||
text = self.text
|
||||
|
||||
# Set cursor ('insert') to '1.0', within block.
|
||||
text.insert('1.0', self.multiline_test_comment)
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
expected = (
|
||||
"# The first line is under the max width. The second line's length is\n"
|
||||
"# way over the max width. It goes on and on until it is over 100\n"
|
||||
"# characters long. Same thing with the third line. It is also way over\n"
|
||||
"# the max width, but FormatParagraph will fix it. The fourth line is\n"
|
||||
"# short like the first line.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# Select line 2, verify line 1 unaffected.
|
||||
text.insert('1.0', self.multiline_test_comment)
|
||||
text.tag_add('sel', '2.0', '3.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
expected = (
|
||||
"# The first line is under the max width.\n"
|
||||
"# The second line's length is way over the max width. It goes on and\n"
|
||||
"# on until it is over 100 characters long.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# The following block worked with EditorWindow but fails with the mock.
|
||||
# Lines 2 and 3 get pasted together even though the previous block left
|
||||
# the previous line alone. More investigation is needed.
|
||||
## # Select lines 3 and 4
|
||||
## text.insert('1.0', self.multiline_test_comment)
|
||||
## text.tag_add('sel', '3.0', '5.0')
|
||||
## self.formatter('ParameterDoesNothing')
|
||||
## result = text.get('3.0', 'insert')
|
||||
## expected = (
|
||||
##"# Same thing with the third line. It is also way over the max width,\n"
|
||||
##"# but FormatParagraph will fix it. The fourth line is short like the\n"
|
||||
##"# first line.\n")
|
||||
## self.assertEqual(result, expected)
|
||||
## text.delete('1.0', 'end')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
80
Darwin/lib/python3.5/idlelib/idle_test/test_grep.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
""" !Changing this line will break Test_findfile.test_found!
|
||||
Non-gui unit tests for idlelib.GrepDialog methods.
|
||||
dummy_command calls grep_it calls findfiles.
|
||||
An exception raised in one method will fail callers.
|
||||
Otherwise, tests are mostly independent.
|
||||
*** Currently only test grep_it.
|
||||
"""
|
||||
import unittest
|
||||
from test.support import captured_stdout
|
||||
from idlelib.idle_test.mock_tk import Var
|
||||
from idlelib.GrepDialog import GrepDialog
|
||||
import re
|
||||
|
||||
class Dummy_searchengine:
|
||||
'''GrepDialog.__init__ calls parent SearchDiabolBase which attaches the
|
||||
passed in SearchEngine instance as attribute 'engine'. Only a few of the
|
||||
many possible self.engine.x attributes are needed here.
|
||||
'''
|
||||
def getpat(self):
|
||||
return self._pat
|
||||
|
||||
searchengine = Dummy_searchengine()
|
||||
|
||||
class Dummy_grep:
|
||||
# Methods tested
|
||||
#default_command = GrepDialog.default_command
|
||||
grep_it = GrepDialog.grep_it
|
||||
findfiles = GrepDialog.findfiles
|
||||
# Other stuff needed
|
||||
recvar = Var(False)
|
||||
engine = searchengine
|
||||
def close(self): # gui method
|
||||
pass
|
||||
|
||||
grep = Dummy_grep()
|
||||
|
||||
class FindfilesTest(unittest.TestCase):
|
||||
# findfiles is really a function, not a method, could be iterator
|
||||
# test that filename return filename
|
||||
# test that idlelib has many .py files
|
||||
# test that recursive flag adds idle_test .py files
|
||||
pass
|
||||
|
||||
class Grep_itTest(unittest.TestCase):
|
||||
# Test captured reports with 0 and some hits.
|
||||
# Should test file names, but Windows reports have mixed / and \ separators
|
||||
# from incomplete replacement, so 'later'.
|
||||
|
||||
def report(self, pat):
|
||||
grep.engine._pat = pat
|
||||
with captured_stdout() as s:
|
||||
grep.grep_it(re.compile(pat), __file__)
|
||||
lines = s.getvalue().split('\n')
|
||||
lines.pop() # remove bogus '' after last \n
|
||||
return lines
|
||||
|
||||
def test_unfound(self):
|
||||
pat = 'xyz*'*7
|
||||
lines = self.report(pat)
|
||||
self.assertEqual(len(lines), 2)
|
||||
self.assertIn(pat, lines[0])
|
||||
self.assertEqual(lines[1], 'No hits.')
|
||||
|
||||
def test_found(self):
|
||||
|
||||
pat = '""" !Changing this line will break Test_findfile.test_found!'
|
||||
lines = self.report(pat)
|
||||
self.assertEqual(len(lines), 5)
|
||||
self.assertIn(pat, lines[0])
|
||||
self.assertIn('py: 1:', lines[1]) # line number 1
|
||||
self.assertIn('2', lines[3]) # hits found 2
|
||||
self.assertTrue(lines[4].startswith('(Hint:'))
|
||||
|
||||
class Default_commandTest(unittest.TestCase):
|
||||
# To write this, mode OutputWindow import to top of GrepDialog
|
||||
# so it can be replaced by captured_stdout in class setup/teardown.
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
273
Darwin/lib/python3.5/idlelib/idle_test/test_hyperparser.py
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
"""Unittest for idlelib.HyperParser"""
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
from idlelib.HyperParser import HyperParser
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.context_use_ps1 = True
|
||||
self.num_context_lines = 50, 500, 1000
|
||||
|
||||
_build_char_in_string_func = EditorWindow._build_char_in_string_func
|
||||
is_char_in_string = EditorWindow.is_char_in_string
|
||||
|
||||
|
||||
class HyperParserTest(unittest.TestCase):
|
||||
code = (
|
||||
'"""This is a module docstring"""\n'
|
||||
'# this line is a comment\n'
|
||||
'x = "this is a string"\n'
|
||||
"y = 'this is also a string'\n"
|
||||
'l = [i for i in range(10)]\n'
|
||||
'm = [py*py for # comment\n'
|
||||
' py in l]\n'
|
||||
'x.__len__\n'
|
||||
"z = ((r'asdf')+('a')))\n"
|
||||
'[x for x in\n'
|
||||
'for = False\n'
|
||||
'cliché = "this is a string with unicode, what a cliché"'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editwin = DummyEditwin(cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.editwin
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.insert('insert', self.code)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.editwin.context_use_ps1 = True
|
||||
|
||||
def get_parser(self, index):
|
||||
"""
|
||||
Return a parser object with index at 'index'
|
||||
"""
|
||||
return HyperParser(self.editwin, index)
|
||||
|
||||
def test_init(self):
|
||||
"""
|
||||
test corner cases in the init method
|
||||
"""
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.text.tag_add('console', '1.0', '1.end')
|
||||
p = self.get_parser('1.5')
|
||||
self.assertIn('precedes', str(ve.exception))
|
||||
|
||||
# test without ps1
|
||||
self.editwin.context_use_ps1 = False
|
||||
|
||||
# number of lines lesser than 50
|
||||
p = self.get_parser('end')
|
||||
self.assertEqual(p.rawtext, self.text.get('1.0', 'end'))
|
||||
|
||||
# number of lines greater than 50
|
||||
self.text.insert('end', self.text.get('1.0', 'end')*4)
|
||||
p = self.get_parser('54.5')
|
||||
|
||||
def test_is_in_string(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('1.0')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('1.4')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('2.3')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('3.3')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('3.7')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('4.6')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('12.54')
|
||||
self.assertTrue(p.is_in_string())
|
||||
|
||||
def test_is_in_code(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('1.0')
|
||||
self.assertTrue(p.is_in_code())
|
||||
p = get('1.1')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('2.5')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('3.4')
|
||||
self.assertTrue(p.is_in_code())
|
||||
p = get('3.6')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('4.14')
|
||||
self.assertFalse(p.is_in_code())
|
||||
|
||||
def test_get_surrounding_bracket(self):
|
||||
get = self.get_parser
|
||||
|
||||
def without_mustclose(parser):
|
||||
# a utility function to get surrounding bracket
|
||||
# with mustclose=False
|
||||
return parser.get_surrounding_brackets(mustclose=False)
|
||||
|
||||
def with_mustclose(parser):
|
||||
# a utility function to get surrounding bracket
|
||||
# with mustclose=True
|
||||
return parser.get_surrounding_brackets(mustclose=True)
|
||||
|
||||
p = get('3.2')
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
self.assertIsNone(without_mustclose(p))
|
||||
|
||||
p = get('5.6')
|
||||
self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25'))
|
||||
self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
|
||||
|
||||
p = get('5.23')
|
||||
self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24'))
|
||||
self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
|
||||
|
||||
p = get('6.15')
|
||||
self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end'))
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
|
||||
p = get('9.end')
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
self.assertIsNone(without_mustclose(p))
|
||||
|
||||
def test_get_expression(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('4.2')
|
||||
self.assertEqual(p.get_expression(), 'y ')
|
||||
|
||||
p = get('4.7')
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
p.get_expression()
|
||||
self.assertIn('is inside a code', str(ve.exception))
|
||||
|
||||
p = get('5.25')
|
||||
self.assertEqual(p.get_expression(), 'range(10)')
|
||||
|
||||
p = get('6.7')
|
||||
self.assertEqual(p.get_expression(), 'py')
|
||||
|
||||
p = get('6.8')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('7.9')
|
||||
self.assertEqual(p.get_expression(), 'py')
|
||||
|
||||
p = get('8.end')
|
||||
self.assertEqual(p.get_expression(), 'x.__len__')
|
||||
|
||||
p = get('9.13')
|
||||
self.assertEqual(p.get_expression(), "r'asdf'")
|
||||
|
||||
p = get('9.17')
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
p.get_expression()
|
||||
self.assertIn('is inside a code', str(ve.exception))
|
||||
|
||||
p = get('10.0')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('10.6')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('10.11')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('11.3')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('11.11')
|
||||
self.assertEqual(p.get_expression(), 'False')
|
||||
|
||||
p = get('12.6')
|
||||
self.assertEqual(p.get_expression(), 'cliché')
|
||||
|
||||
def test_eat_identifier(self):
|
||||
def is_valid_id(candidate):
|
||||
result = HyperParser._eat_identifier(candidate, 0, len(candidate))
|
||||
if result == len(candidate):
|
||||
return True
|
||||
elif result == 0:
|
||||
return False
|
||||
else:
|
||||
err_msg = "Unexpected result: {} (expected 0 or {}".format(
|
||||
result, len(candidate)
|
||||
)
|
||||
raise Exception(err_msg)
|
||||
|
||||
# invalid first character which is valid elsewhere in an identifier
|
||||
self.assertFalse(is_valid_id('2notid'))
|
||||
|
||||
# ASCII-only valid identifiers
|
||||
self.assertTrue(is_valid_id('valid_id'))
|
||||
self.assertTrue(is_valid_id('_valid_id'))
|
||||
self.assertTrue(is_valid_id('valid_id_'))
|
||||
self.assertTrue(is_valid_id('_2valid_id'))
|
||||
|
||||
# keywords which should be "eaten"
|
||||
self.assertTrue(is_valid_id('True'))
|
||||
self.assertTrue(is_valid_id('False'))
|
||||
self.assertTrue(is_valid_id('None'))
|
||||
|
||||
# keywords which should not be "eaten"
|
||||
self.assertFalse(is_valid_id('for'))
|
||||
self.assertFalse(is_valid_id('import'))
|
||||
self.assertFalse(is_valid_id('return'))
|
||||
|
||||
# valid unicode identifiers
|
||||
self.assertTrue(is_valid_id('cliche'))
|
||||
self.assertTrue(is_valid_id('cliché'))
|
||||
self.assertTrue(is_valid_id('a٢'))
|
||||
|
||||
# invalid unicode identifiers
|
||||
self.assertFalse(is_valid_id('2a'))
|
||||
self.assertFalse(is_valid_id('٢a'))
|
||||
self.assertFalse(is_valid_id('a²'))
|
||||
|
||||
# valid identifier after "punctuation"
|
||||
self.assertEqual(HyperParser._eat_identifier('+ var', 0, 5), len('var'))
|
||||
self.assertEqual(HyperParser._eat_identifier('+var', 0, 4), len('var'))
|
||||
self.assertEqual(HyperParser._eat_identifier('.var', 0, 4), len('var'))
|
||||
|
||||
# invalid identifiers
|
||||
self.assertFalse(is_valid_id('+'))
|
||||
self.assertFalse(is_valid_id(' '))
|
||||
self.assertFalse(is_valid_id(':'))
|
||||
self.assertFalse(is_valid_id('?'))
|
||||
self.assertFalse(is_valid_id('^'))
|
||||
self.assertFalse(is_valid_id('\\'))
|
||||
self.assertFalse(is_valid_id('"'))
|
||||
self.assertFalse(is_valid_id('"a string"'))
|
||||
|
||||
def test_eat_identifier_various_lengths(self):
|
||||
eat_id = HyperParser._eat_identifier
|
||||
|
||||
for length in range(1, 21):
|
||||
self.assertEqual(eat_id('a' * length, 0, length), length)
|
||||
self.assertEqual(eat_id('é' * length, 0, length), length)
|
||||
self.assertEqual(eat_id('a' + '2' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' + '2' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' + 'a' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' * (length - 1) + 'a', 0, length), length)
|
||||
self.assertEqual(eat_id('+' * length, 0, length), 0)
|
||||
self.assertEqual(eat_id('2' + 'a' * (length - 1), 0, length), 0)
|
||||
self.assertEqual(eat_id('2' + 'é' * (length - 1), 0, length), 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
167
Darwin/lib/python3.5/idlelib/idle_test/test_idlehistory.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import unittest
|
||||
from test.support import requires
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import Text as tkText
|
||||
from idlelib.idle_test.mock_tk import Text as mkText
|
||||
from idlelib.IdleHistory import History
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
line1 = 'a = 7'
|
||||
line2 = 'b = a'
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
'''Tests History.__init__ and History.store with mock Text'''
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.text = mkText()
|
||||
cls.history = History(cls.text)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.history.history = []
|
||||
|
||||
def test_init(self):
|
||||
self.assertIs(self.history.text, self.text)
|
||||
self.assertEqual(self.history.history, [])
|
||||
self.assertIsNone(self.history.prefix)
|
||||
self.assertIsNone(self.history.pointer)
|
||||
self.assertEqual(self.history.cyclic,
|
||||
idleConf.GetOption("main", "History", "cyclic", 1, "bool"))
|
||||
|
||||
def test_store_short(self):
|
||||
self.history.store('a')
|
||||
self.assertEqual(self.history.history, [])
|
||||
self.history.store(' a ')
|
||||
self.assertEqual(self.history.history, [])
|
||||
|
||||
def test_store_dup(self):
|
||||
self.history.store(line1)
|
||||
self.assertEqual(self.history.history, [line1])
|
||||
self.history.store(line2)
|
||||
self.assertEqual(self.history.history, [line1, line2])
|
||||
self.history.store(line1)
|
||||
self.assertEqual(self.history.history, [line2, line1])
|
||||
|
||||
def test_store_reset(self):
|
||||
self.history.prefix = line1
|
||||
self.history.pointer = 0
|
||||
self.history.store(line2)
|
||||
self.assertIsNone(self.history.prefix)
|
||||
self.assertIsNone(self.history.pointer)
|
||||
|
||||
|
||||
class TextWrapper:
|
||||
def __init__(self, master):
|
||||
self.text = tkText(master=master)
|
||||
self._bell = False
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.text, name)
|
||||
def bell(self):
|
||||
self._bell = True
|
||||
|
||||
class FetchTest(unittest.TestCase):
|
||||
'''Test History.fetch with wrapped tk.Text.
|
||||
'''
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
|
||||
def setUp(self):
|
||||
self.text = text = TextWrapper(self.root)
|
||||
text.insert('1.0', ">>> ")
|
||||
text.mark_set('iomark', '1.4')
|
||||
text.mark_gravity('iomark', 'left')
|
||||
self.history = History(text)
|
||||
self.history.history = [line1, line2]
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def fetch_test(self, reverse, line, prefix, index, *, bell=False):
|
||||
# Perform one fetch as invoked by Alt-N or Alt-P
|
||||
# Test the result. The line test is the most important.
|
||||
# The last two are diagnostic of fetch internals.
|
||||
History = self.history
|
||||
History.fetch(reverse)
|
||||
|
||||
Equal = self.assertEqual
|
||||
Equal(self.text.get('iomark', 'end-1c'), line)
|
||||
Equal(self.text._bell, bell)
|
||||
if bell:
|
||||
self.text._bell = False
|
||||
Equal(History.prefix, prefix)
|
||||
Equal(History.pointer, index)
|
||||
Equal(self.text.compare("insert", '==', "end-1c"), 1)
|
||||
|
||||
def test_fetch_prev_cyclic(self):
|
||||
prefix = ''
|
||||
test = self.fetch_test
|
||||
test(True, line2, prefix, 1)
|
||||
test(True, line1, prefix, 0)
|
||||
test(True, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_next_cyclic(self):
|
||||
prefix = ''
|
||||
test = self.fetch_test
|
||||
test(False, line1, prefix, 0)
|
||||
test(False, line2, prefix, 1)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
|
||||
# Prefix 'a' tests skip line2, which starts with 'b'
|
||||
def test_fetch_prev_prefix(self):
|
||||
prefix = 'a'
|
||||
self.text.insert('iomark', prefix)
|
||||
self.fetch_test(True, line1, prefix, 0)
|
||||
self.fetch_test(True, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_next_prefix(self):
|
||||
prefix = 'a'
|
||||
self.text.insert('iomark', prefix)
|
||||
self.fetch_test(False, line1, prefix, 0)
|
||||
self.fetch_test(False, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_prev_noncyclic(self):
|
||||
prefix = ''
|
||||
self.history.cyclic = False
|
||||
test = self.fetch_test
|
||||
test(True, line2, prefix, 1)
|
||||
test(True, line1, prefix, 0)
|
||||
test(True, line1, prefix, 0, bell=True)
|
||||
|
||||
def test_fetch_next_noncyclic(self):
|
||||
prefix = ''
|
||||
self.history.cyclic = False
|
||||
test = self.fetch_test
|
||||
test(False, prefix, None, None, bell=True)
|
||||
test(True, line2, prefix, 1)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_cursor_move(self):
|
||||
# Move cursor after fetch
|
||||
self.history.fetch(reverse=True) # initialization
|
||||
self.text.mark_set('insert', 'iomark')
|
||||
self.fetch_test(True, line2, None, None, bell=True)
|
||||
|
||||
def test_fetch_edit(self):
|
||||
# Edit after fetch
|
||||
self.history.fetch(reverse=True) # initialization
|
||||
self.text.delete('iomark', 'insert', )
|
||||
self.text.insert('iomark', 'a =')
|
||||
self.fetch_test(True, line1, 'a =', 0) # prefix is reset
|
||||
|
||||
def test_history_prev_next(self):
|
||||
# Minimally test functions bound to events
|
||||
self.history.history_prev('dummy event')
|
||||
self.assertEqual(self.history.pointer, 1)
|
||||
self.history.history_next('dummy event')
|
||||
self.assertEqual(self.history.pointer, None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
233
Darwin/lib/python3.5/idlelib/idle_test/test_io.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
import unittest
|
||||
import io
|
||||
from idlelib.PyShell import PseudoInputFile, PseudoOutputFile
|
||||
|
||||
|
||||
class S(str):
|
||||
def __str__(self):
|
||||
return '%s:str' % type(self).__name__
|
||||
def __unicode__(self):
|
||||
return '%s:unicode' % type(self).__name__
|
||||
def __len__(self):
|
||||
return 3
|
||||
def __iter__(self):
|
||||
return iter('abc')
|
||||
def __getitem__(self, *args):
|
||||
return '%s:item' % type(self).__name__
|
||||
def __getslice__(self, *args):
|
||||
return '%s:slice' % type(self).__name__
|
||||
|
||||
class MockShell:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def write(self, *args):
|
||||
self.written.append(args)
|
||||
|
||||
def readline(self):
|
||||
return self.lines.pop()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
self.written = []
|
||||
|
||||
def push(self, lines):
|
||||
self.lines = list(lines)[::-1]
|
||||
|
||||
|
||||
class PseudeOutputFilesTest(unittest.TestCase):
|
||||
def test_misc(self):
|
||||
shell = MockShell()
|
||||
f = PseudoOutputFile(shell, 'stdout', 'utf-8')
|
||||
self.assertIsInstance(f, io.TextIOBase)
|
||||
self.assertEqual(f.encoding, 'utf-8')
|
||||
self.assertIsNone(f.errors)
|
||||
self.assertIsNone(f.newlines)
|
||||
self.assertEqual(f.name, '<stdout>')
|
||||
self.assertFalse(f.closed)
|
||||
self.assertTrue(f.isatty())
|
||||
self.assertFalse(f.readable())
|
||||
self.assertTrue(f.writable())
|
||||
self.assertFalse(f.seekable())
|
||||
|
||||
def test_unsupported(self):
|
||||
shell = MockShell()
|
||||
f = PseudoOutputFile(shell, 'stdout', 'utf-8')
|
||||
self.assertRaises(OSError, f.fileno)
|
||||
self.assertRaises(OSError, f.tell)
|
||||
self.assertRaises(OSError, f.seek, 0)
|
||||
self.assertRaises(OSError, f.read, 0)
|
||||
self.assertRaises(OSError, f.readline, 0)
|
||||
|
||||
def test_write(self):
|
||||
shell = MockShell()
|
||||
f = PseudoOutputFile(shell, 'stdout', 'utf-8')
|
||||
f.write('test')
|
||||
self.assertEqual(shell.written, [('test', 'stdout')])
|
||||
shell.reset()
|
||||
f.write('t\xe8st')
|
||||
self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
|
||||
shell.reset()
|
||||
|
||||
f.write(S('t\xe8st'))
|
||||
self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
|
||||
self.assertEqual(type(shell.written[0][0]), str)
|
||||
shell.reset()
|
||||
|
||||
self.assertRaises(TypeError, f.write)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, b'test')
|
||||
self.assertRaises(TypeError, f.write, 123)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, 'test', 'spam')
|
||||
self.assertEqual(shell.written, [])
|
||||
|
||||
def test_writelines(self):
|
||||
shell = MockShell()
|
||||
f = PseudoOutputFile(shell, 'stdout', 'utf-8')
|
||||
f.writelines([])
|
||||
self.assertEqual(shell.written, [])
|
||||
shell.reset()
|
||||
f.writelines(['one\n', 'two'])
|
||||
self.assertEqual(shell.written,
|
||||
[('one\n', 'stdout'), ('two', 'stdout')])
|
||||
shell.reset()
|
||||
f.writelines(['on\xe8\n', 'tw\xf2'])
|
||||
self.assertEqual(shell.written,
|
||||
[('on\xe8\n', 'stdout'), ('tw\xf2', 'stdout')])
|
||||
shell.reset()
|
||||
|
||||
f.writelines([S('t\xe8st')])
|
||||
self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
|
||||
self.assertEqual(type(shell.written[0][0]), str)
|
||||
shell.reset()
|
||||
|
||||
self.assertRaises(TypeError, f.writelines)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, 123)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, [b'test'])
|
||||
self.assertRaises(TypeError, f.writelines, [123])
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, [], [])
|
||||
self.assertEqual(shell.written, [])
|
||||
|
||||
def test_close(self):
|
||||
shell = MockShell()
|
||||
f = PseudoOutputFile(shell, 'stdout', 'utf-8')
|
||||
self.assertFalse(f.closed)
|
||||
f.write('test')
|
||||
f.close()
|
||||
self.assertTrue(f.closed)
|
||||
self.assertRaises(ValueError, f.write, 'x')
|
||||
self.assertEqual(shell.written, [('test', 'stdout')])
|
||||
f.close()
|
||||
self.assertRaises(TypeError, f.close, 1)
|
||||
|
||||
|
||||
class PseudeInputFilesTest(unittest.TestCase):
|
||||
def test_misc(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
self.assertIsInstance(f, io.TextIOBase)
|
||||
self.assertEqual(f.encoding, 'utf-8')
|
||||
self.assertIsNone(f.errors)
|
||||
self.assertIsNone(f.newlines)
|
||||
self.assertEqual(f.name, '<stdin>')
|
||||
self.assertFalse(f.closed)
|
||||
self.assertTrue(f.isatty())
|
||||
self.assertTrue(f.readable())
|
||||
self.assertFalse(f.writable())
|
||||
self.assertFalse(f.seekable())
|
||||
|
||||
def test_unsupported(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
self.assertRaises(OSError, f.fileno)
|
||||
self.assertRaises(OSError, f.tell)
|
||||
self.assertRaises(OSError, f.seek, 0)
|
||||
self.assertRaises(OSError, f.write, 'x')
|
||||
self.assertRaises(OSError, f.writelines, ['x'])
|
||||
|
||||
def test_read(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(-1), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(None), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', 'three\n', ''])
|
||||
self.assertEqual(f.read(2), 'on')
|
||||
self.assertEqual(f.read(3), 'e\nt')
|
||||
self.assertEqual(f.read(10), 'wo\nthree\n')
|
||||
|
||||
shell.push(['one\n', 'two\n'])
|
||||
self.assertEqual(f.read(0), '')
|
||||
self.assertRaises(TypeError, f.read, 1.5)
|
||||
self.assertRaises(TypeError, f.read, '1')
|
||||
self.assertRaises(TypeError, f.read, 1, 1)
|
||||
|
||||
def test_readline(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
shell.push(['one\n', 'two\n', 'three\n', 'four\n'])
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
self.assertEqual(f.readline(-1), 'two\n')
|
||||
self.assertEqual(f.readline(None), 'three\n')
|
||||
shell.push(['one\ntwo\n'])
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
self.assertEqual(f.readline(), 'two\n')
|
||||
shell.push(['one', 'two', 'three'])
|
||||
self.assertEqual(f.readline(), 'one')
|
||||
self.assertEqual(f.readline(), 'two')
|
||||
shell.push(['one\n', 'two\n', 'three\n'])
|
||||
self.assertEqual(f.readline(2), 'on')
|
||||
self.assertEqual(f.readline(1), 'e')
|
||||
self.assertEqual(f.readline(1), '\n')
|
||||
self.assertEqual(f.readline(10), 'two\n')
|
||||
|
||||
shell.push(['one\n', 'two\n'])
|
||||
self.assertEqual(f.readline(0), '')
|
||||
self.assertRaises(TypeError, f.readlines, 1.5)
|
||||
self.assertRaises(TypeError, f.readlines, '1')
|
||||
self.assertRaises(TypeError, f.readlines, 1, 1)
|
||||
|
||||
def test_readlines(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(-1), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(None), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(0), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(3), ['one\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(4), ['one\n', 'two\n'])
|
||||
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertRaises(TypeError, f.readlines, 1.5)
|
||||
self.assertRaises(TypeError, f.readlines, '1')
|
||||
self.assertRaises(TypeError, f.readlines, 1, 1)
|
||||
|
||||
def test_close(self):
|
||||
shell = MockShell()
|
||||
f = PseudoInputFile(shell, 'stdin', 'utf-8')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
f.close()
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(f.readline(), 'two\n')
|
||||
self.assertRaises(TypeError, f.close, 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
109
Darwin/lib/python3.5/idlelib/idle_test/test_parenmatch.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
"""Test idlelib.ParenMatch."""
|
||||
# This must currently be a gui test because ParenMatch methods use
|
||||
# several text methods not defined on idlelib.idle_test.mock_tk.Text.
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.ParenMatch import ParenMatch
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.context_use_ps1 = True
|
||||
|
||||
|
||||
class ParenMatchTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editwin = DummyEditwin(cls.text)
|
||||
cls.editwin.text_frame = Mock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.editwin
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_paren_expression(self):
|
||||
"""
|
||||
Test ParenMatch with 'expression' style.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.set_style('expression')
|
||||
|
||||
text.insert('insert', 'def foobar(a, b')
|
||||
pm.flash_paren_event('event')
|
||||
self.assertIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.15'))
|
||||
text.insert('insert', ')')
|
||||
pm.restore_event()
|
||||
self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertEqual(text.tag_prevrange('paren', 'end'), ())
|
||||
|
||||
# paren_closed_event can only be tested as below
|
||||
pm.paren_closed_event('event')
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.16'))
|
||||
|
||||
def test_paren_default(self):
|
||||
"""
|
||||
Test ParenMatch with 'default' style.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.set_style('default')
|
||||
|
||||
text.insert('insert', 'def foobar(a, b')
|
||||
pm.flash_paren_event('event')
|
||||
self.assertIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.11'))
|
||||
text.insert('insert', ')')
|
||||
pm.restore_event()
|
||||
self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertEqual(text.tag_prevrange('paren', 'end'), ())
|
||||
|
||||
def test_paren_corner(self):
|
||||
"""
|
||||
Test corner cases in flash_paren_event and paren_closed_event.
|
||||
|
||||
These cases force conditional expression and alternate paths.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
|
||||
text.insert('insert', '# this is a commen)')
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
text.insert('insert', '\ndef')
|
||||
self.assertIsNone(pm.flash_paren_event('event'))
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
text.insert('insert', ' a, *arg)')
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
def test_handle_restore_timer(self):
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.restore_event = Mock()
|
||||
pm.handle_restore_timer(0)
|
||||
self.assertTrue(pm.restore_event.called)
|
||||
pm.restore_event.reset_mock()
|
||||
pm.handle_restore_timer(1)
|
||||
self.assertFalse(pm.restore_event.called)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
27
Darwin/lib/python3.5/idlelib/idle_test/test_pathbrowser.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import idlelib
|
||||
from idlelib import PathBrowser
|
||||
|
||||
class PathBrowserTest(unittest.TestCase):
|
||||
|
||||
def test_DirBrowserTreeItem(self):
|
||||
# Issue16226 - make sure that getting a sublist works
|
||||
d = PathBrowser.DirBrowserTreeItem('')
|
||||
d.GetSubList()
|
||||
self.assertEqual('', d.GetText())
|
||||
|
||||
dir = os.path.split(os.path.abspath(idlelib.__file__))[0]
|
||||
self.assertEqual(d.ispackagedir(dir), True)
|
||||
self.assertEqual(d.ispackagedir(dir + '/Icons'), False)
|
||||
|
||||
def test_PathBrowserTreeItem(self):
|
||||
p = PathBrowser.PathBrowserTreeItem()
|
||||
self.assertEqual(p.GetText(), 'sys.path')
|
||||
sub = p.GetSubList()
|
||||
self.assertEqual(len(sub), len(sys.path))
|
||||
self.assertEqual(type(sub[0]), PathBrowser.DirBrowserTreeItem)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
49
Darwin/lib/python3.5/idlelib/idle_test/test_rstrip.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import unittest
|
||||
import idlelib.RstripExtension as rs
|
||||
from idlelib.idle_test.mock_idle import Editor
|
||||
|
||||
class rstripTest(unittest.TestCase):
|
||||
|
||||
def test_rstrip_line(self):
|
||||
editor = Editor()
|
||||
text = editor.text
|
||||
do_rstrip = rs.RstripExtension(editor).do_rstrip
|
||||
|
||||
do_rstrip()
|
||||
self.assertEqual(text.get('1.0', 'insert'), '')
|
||||
text.insert('1.0', ' ')
|
||||
do_rstrip()
|
||||
self.assertEqual(text.get('1.0', 'insert'), '')
|
||||
text.insert('1.0', ' \n')
|
||||
do_rstrip()
|
||||
self.assertEqual(text.get('1.0', 'insert'), '\n')
|
||||
|
||||
def test_rstrip_multiple(self):
|
||||
editor = Editor()
|
||||
# Uncomment following to verify that test passes with real widgets.
|
||||
## from idlelib.EditorWindow import EditorWindow as Editor
|
||||
## from tkinter import Tk
|
||||
## editor = Editor(root=Tk())
|
||||
text = editor.text
|
||||
do_rstrip = rs.RstripExtension(editor).do_rstrip
|
||||
|
||||
original = (
|
||||
"Line with an ending tab \n"
|
||||
"Line ending in 5 spaces \n"
|
||||
"Linewithnospaces\n"
|
||||
" indented line\n"
|
||||
" indented line with trailing space \n"
|
||||
" ")
|
||||
stripped = (
|
||||
"Line with an ending tab\n"
|
||||
"Line ending in 5 spaces\n"
|
||||
"Linewithnospaces\n"
|
||||
" indented line\n"
|
||||
" indented line with trailing space\n")
|
||||
|
||||
text.insert('1.0', original)
|
||||
do_rstrip()
|
||||
self.assertEqual(text.get('1.0', 'insert'), stripped)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||