summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/decimal.py2
-rw-r--r--Lib/distutils/command/build_ext.py15
-rw-r--r--Lib/encodings/__init__.py6
-rw-r--r--Lib/gzip.py18
-rw-r--r--Lib/heapq.py42
-rw-r--r--Lib/idlelib/AutoCompleteWindow.py63
-rw-r--r--Lib/idlelib/CallTips.py81
-rw-r--r--Lib/idlelib/CodeContext.py72
-rw-r--r--Lib/idlelib/IOBinding.py2
-rw-r--r--Lib/idlelib/NEWS.txt13
-rw-r--r--Lib/idlelib/PyShell.py54
-rw-r--r--Lib/idlelib/configHandler.py23
-rw-r--r--Lib/idlelib/help.txt77
-rw-r--r--Lib/imputil.py4
-rw-r--r--Lib/logging/__init__.py10
-rw-r--r--Lib/shutil.py4
-rw-r--r--Lib/stat.py13
-rw-r--r--Lib/subprocess.py58
-rw-r--r--Lib/tarfile.py9
-rw-r--r--Lib/test/test_datetime.py5
-rw-r--r--Lib/test/test_defaultdict.py7
-rw-r--r--Lib/test/test_descr.py2
-rw-r--r--Lib/test/test_dict.py8
-rw-r--r--Lib/test/test_gzip.py7
-rw-r--r--Lib/test/test_heapq.py25
-rw-r--r--Lib/test/test_itertools.py57
-rw-r--r--Lib/test/test_operator.py2
-rw-r--r--Lib/test/test_posix.py12
-rw-r--r--Lib/test/test_sax.py39
-rw-r--r--Lib/test/test_set.py20
-rw-r--r--Lib/test/test_tarfile.py56
-rw-r--r--Lib/test/test_zipfile.py65
-rw-r--r--Lib/trace.py2
-rw-r--r--Lib/xml/sax/saxutils.py35
-rw-r--r--Lib/zipfile.py94
35 files changed, 780 insertions, 222 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 198fd1a..74b23b6 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -487,7 +487,7 @@ def localcontext(ctx=None):
28
>>> with localcontext():
... ctx = getcontext()
- ... ctx.prec() += 2
+ ... ctx.prec += 2
... print(ctx.prec)
...
30
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index f79eee3..bbe5146 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -185,9 +185,7 @@ class build_ext (Command):
# for extensions under Cygwin and AtheOS Python's library directory must be
# appended to library_dirs
- if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \
- ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and
- sysconfig.get_config_var('Py_ENABLE_SHARED')):
+ if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos':
if string.find(sys.executable, sys.exec_prefix) != -1:
# building third party extensions
self.library_dirs.append(os.path.join(sys.prefix, "lib",
@@ -197,6 +195,17 @@ class build_ext (Command):
# building python standard extensions
self.library_dirs.append('.')
+ # for extensions under Linux with a shared Python library,
+ # Python's library directory must be appended to library_dirs
+ if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \
+ and sysconfig.get_config_var('Py_ENABLE_SHARED'):
+ if string.find(sys.executable, sys.exec_prefix) != -1:
+ # building third party extensions
+ self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
+ else:
+ # building python standard extensions
+ self.library_dirs.append('.')
+
# The argument parsing will result in self.define being a string, but
# it has to be a list of 2-tuples. All the preprocessor symbols
# specified by the 'define' option will be set to '1'. Multiple
diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py
index f1e7ecc..383f3b0 100644
--- a/Lib/encodings/__init__.py
+++ b/Lib/encodings/__init__.py
@@ -93,8 +93,10 @@ def search_function(encoding):
if not modname or '.' in modname:
continue
try:
- mod = __import__('encodings.' + modname,
- globals(), locals(), _import_tail)
+ # Import is absolute to prevent the possibly malicious import of a
+ # module with side-effects that is not in the 'encodings' package.
+ mod = __import__('encodings.' + modname, fromlist=_import_tail,
+ level=0)
except ImportError:
pass
else:
diff --git a/Lib/gzip.py b/Lib/gzip.py
index aac9956..4ff4883 100644
--- a/Lib/gzip.py
+++ b/Lib/gzip.py
@@ -106,7 +106,7 @@ class GzipFile:
self._new_member = True
self.extrabuf = ""
self.extrasize = 0
- self.filename = filename
+ self.name = filename
# Starts small, scales exponentially
self.min_readsize = 100
@@ -127,14 +127,20 @@ class GzipFile:
if self.mode == WRITE:
self._write_gzip_header()
+ @property
+ def filename(self):
+ import warnings
+ warnings.warn("use the name attribute", DeprecationWarning)
+ if self.mode == WRITE and self.name[-3:] != ".gz":
+ return self.name + ".gz"
+ return self.name
+
def __repr__(self):
s = repr(self.fileobj)
return '<gzip ' + s[1:-1] + ' ' + hex(id(self)) + '>'
def _init_write(self, filename):
- if filename[-3:] != '.gz':
- filename = filename + '.gz'
- self.filename = filename
+ self.name = filename
self.crc = zlib.crc32("")
self.size = 0
self.writebuf = []
@@ -143,7 +149,9 @@ class GzipFile:
def _write_gzip_header(self):
self.fileobj.write('\037\213') # magic header
self.fileobj.write('\010') # compression method
- fname = self.filename[:-3]
+ fname = self.name
+ if fname.endswith(".gz"):
+ fname = fname[:-3]
flags = 0
if fname:
flags = FNAME
diff --git a/Lib/heapq.py b/Lib/heapq.py
index 9ab4c99..0168065 100644
--- a/Lib/heapq.py
+++ b/Lib/heapq.py
@@ -126,8 +126,8 @@ Believe me, real good tape sorts were quite spectacular to watch!
From all times, sorting has always been a Great Art! :-)
"""
-__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'nlargest',
- 'nsmallest']
+__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'merge',
+ 'nlargest', 'nsmallest']
from itertools import islice, repeat, count, imap, izip, tee
from operator import itemgetter, neg
@@ -308,6 +308,41 @@ try:
except ImportError:
pass
+def merge(*iterables):
+ '''Merge multiple sorted inputs into a single sorted output.
+
+ Similar to sorted(itertools.chain(*iterables)) but returns an iterable,
+ does not pull the data into memory all at once, and assumes that each of
+ the input streams is already sorted (smallest to largest).
+
+ >>> list(merge([1,3,5,7], [0,2,4,8], [5,10,15,20], [], [25]))
+ [0, 1, 2, 3, 4, 5, 5, 7, 8, 10, 15, 20, 25]
+
+ '''
+ _heappop, _heapreplace, _StopIteration = heappop, heapreplace, StopIteration
+
+ h = []
+ h_append = h.append
+ for itnum, it in enumerate(map(iter, iterables)):
+ try:
+ next = it.next
+ h_append([next(), itnum, next])
+ except _StopIteration:
+ pass
+ heapify(h)
+
+ while 1:
+ try:
+ while 1:
+ v, itnum, next = s = h[0] # raises IndexError when h is empty
+ yield v
+ s[0] = next() # raises StopIteration when exhausted
+ _heapreplace(h, s) # restore heap condition
+ except _StopIteration:
+ _heappop(h) # remove empty iterator
+ except IndexError:
+ return
+
# Extend the implementations of nsmallest and nlargest to use a key= argument
_nsmallest = nsmallest
def nsmallest(n, iterable, key=None):
@@ -341,3 +376,6 @@ if __name__ == "__main__":
while heap:
sort.append(heappop(heap))
print(sort)
+
+ import doctest
+ doctest.testmod()
diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py
index 7f8adaf..56433cd 100644
--- a/Lib/idlelib/AutoCompleteWindow.py
+++ b/Lib/idlelib/AutoCompleteWindow.py
@@ -10,13 +10,14 @@ 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-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>")
+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 = "<ButtonRelease>"
+LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>"
WINCONFIG_SEQUENCE = "<Configure>"
-DOUBLECLICK_SEQUENCE = "<Double-ButtonRelease>"
+DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>"
class AutoCompleteWindow:
@@ -49,6 +50,8 @@ class AutoCompleteWindow:
# 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):
i = 0
@@ -118,11 +121,6 @@ class AutoCompleteWindow:
i = 0
while i < len(lts) and i < len(selstart) and lts[i] == selstart[i]:
i += 1
- previous_completion = self.completions[cursel - 1]
- while cursel > 0 and selstart[:i] <= previous_completion:
- i += 1
- if selstart == previous_completion:
- break # maybe we have a duplicate?
newstart = selstart[:i]
self._change_start(newstart)
@@ -206,7 +204,7 @@ class AutoCompleteWindow:
self.keyrelease_event)
self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE)
self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE,
- self.listupdate_event)
+ self.listselect_event)
self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event)
self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE,
self.doubleclick_event)
@@ -215,24 +213,34 @@ class AutoCompleteWindow:
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
- self.widget.see(self.startindex)
- x, y, cx, cy = self.widget.bbox(self.startindex)
- acw.wm_geometry("+%d+%d" % (x + self.widget.winfo_rootx(),
- y + self.widget.winfo_rooty() \
- -acw.winfo_height()))
-
+ 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 listupdate_event(self, event):
+ def listselect_event(self, event):
if not self.is_active():
return
self.userwantswindow = True
- self._selection_changed()
+ 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
@@ -248,7 +256,8 @@ class AutoCompleteWindow:
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==AutoComplete.COMPLETE_FILES and keysym in
("period", "minus"))) \
@@ -330,13 +339,21 @@ class AutoCompleteWindow:
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):
- # The user wants a completion, but it is handled by AutoComplete
- # (not AutoCompleteWindow), so ignore.
- self.userwantswindow = True
- return
+ 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")):
diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py
index 1ffbc16..b395f15 100644
--- a/Lib/idlelib/CallTips.py
+++ b/Lib/idlelib/CallTips.py
@@ -3,7 +3,9 @@
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 re
import sys
import types
@@ -89,6 +91,8 @@ class CallTips:
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
@@ -108,7 +112,7 @@ class CallTips:
namespace.update(__main__.__dict__)
try:
return eval(name, namespace)
- except:
+ except (NameError, AttributeError):
return None
def _find_constructor(class_ob):
@@ -124,39 +128,37 @@ def _find_constructor(class_ob):
def get_arg_text(ob):
"""Get a string describing the arguments for the given object"""
- argText = ""
+ arg_text = ""
if ob is not None:
- argOffset = 0
+ arg_offset = 0
if type(ob) in (types.ClassType, types.TypeType):
# Look for the highest __init__ in the class chain.
fob = _find_constructor(ob)
if fob is None:
fob = lambda: None
else:
- argOffset = 1
+ arg_offset = 1
elif type(ob)==types.MethodType:
# bit of a hack for methods - turn it into a function
# but we drop the "self" param.
fob = ob.im_func
- argOffset = 1
+ arg_offset = 1
else:
fob = ob
- # Try and build one for Python defined functions
+ # Try to build one for Python defined functions
if type(fob) in [types.FunctionType, types.LambdaType]:
- try:
- realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
- defaults = fob.func_defaults or []
- defaults = list(map(lambda name: "=%s" % repr(name), defaults))
- defaults = [""] * (len(realArgs)-len(defaults)) + defaults
- items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
- if fob.func_code.co_flags & 0x4:
- items.append("...")
- if fob.func_code.co_flags & 0x8:
- items.append("***")
- argText = ", ".join(items)
- argText = "(%s)" % argText
- except:
- pass
+ argcount = fob.func_code.co_argcount
+ real_args = fob.func_code.co_varnames[arg_offset:argcount]
+ defaults = fob.func_defaults or []
+ defaults = list(map(lambda name: "=%s" % repr(name), defaults))
+ defaults = [""] * (len(real_args) - len(defaults)) + defaults
+ items = map(lambda arg, dflt: arg + dflt, real_args, defaults)
+ if fob.func_code.co_flags & 0x4:
+ items.append("...")
+ if fob.func_code.co_flags & 0x8:
+ items.append("***")
+ arg_text = ", ".join(items)
+ arg_text = "(%s)" % re.sub("\.\d+", "<tuple>", arg_text)
# See if we can use the docstring
doc = getattr(ob, "__doc__", "")
if doc:
@@ -164,10 +166,10 @@ def get_arg_text(ob):
pos = doc.find("\n")
if pos < 0 or pos > 70:
pos = 70
- if argText:
- argText += "\n"
- argText += doc[:pos]
- return argText
+ if arg_text:
+ arg_text += "\n"
+ arg_text += doc[:pos]
+ return arg_text
#################################################
#
@@ -181,16 +183,18 @@ if __name__=='__main__':
def t4(*args): "(...)"
def t5(a, *args): "(a, ...)"
def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
+ def t7((a, b), c, (d, e)): "(<tuple>, c, <tuple>)"
- class TC:
- "(a=None, ...)"
- def __init__(self, a=None, *b): "(a=None, ...)"
+ class TC(object):
+ "(ai=None, ...)"
+ def __init__(self, ai=None, *b): "(ai=None, ...)"
def t1(self): "()"
- def t2(self, a, b=None): "(a, b=None)"
- def t3(self, a, *args): "(a, ...)"
+ def t2(self, ai, b=None): "(ai, b=None)"
+ def t3(self, ai, *args): "(ai, ...)"
def t4(self, *args): "(...)"
- def t5(self, a, *args): "(a, ...)"
- def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
+ def t5(self, ai, *args): "(ai, ...)"
+ def t6(self, ai, b=None, *args, **kw): "(ai, b=None, ..., ***)"
+ def t7(self, (ai, b), c, (d, e)): "(<tuple>, c, <tuple>)"
def test(tests):
ct = CallTips()
@@ -198,15 +202,20 @@ if __name__=='__main__':
for t in tests:
expected = t.__doc__ + "\n" + t.__doc__
name = t.__name__
- arg_text = ct.fetch_tip(name)
+ # exercise fetch_tip(), not just get_arg_text()
+ try:
+ qualified_name = "%s.%s" % (t.im_class.__name__, name)
+ except AttributeError:
+ qualified_name = name
+ arg_text = ct.fetch_tip(qualified_name)
if arg_text != expected:
failed.append(t)
- print("%s - expected %s, but got %s" % (t, expected,
- get_arg_text(entity)))
+ fmt = "%s - expected %s, but got %s"
+ print(fmt % (t.__name__, expected, get_arg_text(t)))
print("%d of %d tests failed" % (len(failed), len(tests)))
tc = TC()
- tests = (t1, t2, t3, t4, t5, t6,
- TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
+ tests = (t1, t2, t3, t4, t5, t6, t7,
+ TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6, tc.t7)
test(tests)
diff --git a/Lib/idlelib/CodeContext.py b/Lib/idlelib/CodeContext.py
index 28e99c5..420ec33 100644
--- a/Lib/idlelib/CodeContext.py
+++ b/Lib/idlelib/CodeContext.py
@@ -10,6 +10,7 @@ not open blocks are not shown in the context hints pane.
"""
import Tkinter
+from Tkconstants import TOP, LEFT, X, W, SUNKEN
from configHandler import idleConf
import re
from sys import maxint as INFINITY
@@ -24,7 +25,6 @@ getspacesfirstword =\
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",
@@ -54,66 +54,33 @@ class CodeContext:
def toggle_code_context_event(self, event=None):
if not self.label:
- # The following code attempts to figure out the required border
- # width and vertical padding required for the CodeContext widget
- # to be perfectly aligned with the text in the main Text widget.
- # This is done by retrieving the appropriate attributes from the
- # editwin.text and editwin.text_frame widgets.
+ # 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 int(str(<value>)), since some
- # values may be pixel objects, which can't simply be added added
- # to ints.
- #
- # This code is considered somewhat unstable since it relies on
- # some of Tk's inner workings. However its effect is merely
- # cosmetic; failure will only cause the CodeContext text to be
- # somewhat misaligned with the text in the main Text widget.
- #
- # To avoid possible errors, all references to the inner workings
- # of Tk are executed inside try/except blocks.
-
- widgets_for_width_calc = self.editwin.text, self.editwin.text_frame
-
- # calculate the required vertical padding
+ # 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_for_width_calc:
- try:
- # retrieve the "padx" attribte from widget's pack info
- padx += int(str( widget.pack_info()['padx'] ))
- except:
- pass
- try:
- # retrieve the widget's "padx" attribte
- padx += int(str( widget.cget('padx') ))
- except:
- pass
-
- # calculate the required border width
- border_width = 0
- for widget in widgets_for_width_calc:
- try:
- # retrieve the widget's "border" attribte
- border_width += int(str( widget.cget('border') ))
- except:
- pass
-
+ for widget in widgets:
+ padx += int(str( widget.pack_info()['padx'] ))
+ padx += int(str( widget.cget('padx') ))
+ # Calculate the required border width
+ border = 0
+ for widget in widgets:
+ border += int(str( widget.cget('border') ))
self.label = Tkinter.Label(self.editwin.top,
text="\n" * (self.context_depth - 1),
- anchor="w", justify="left",
+ anchor=W, justify=LEFT,
font=self.textfont,
bg=self.bgcolor, fg=self.fgcolor,
width=1, #don't request more than we get
- padx=padx, #line up with text widget
- border=border_width, #match border width
- relief="sunken",
- )
-
- # CodeContext's label widget is packed before and above the
- # text_frame widget, thus ensuring that it will appear directly
- # above it.
- self.label.pack(side="top", fill="x", expand=False,
+ 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
@@ -190,7 +157,6 @@ class CodeContext:
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:
diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py
index e23b40c..b433c9b 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/IOBinding.py
@@ -209,7 +209,7 @@ class IOBinding:
# gets set to "not modified" at every new prompt.
try:
interp = self.editwin.interp
- except:
+ except AttributeError:
interp = None
if not self.filename and self.get_saved() and not interp:
self.editwin.flist.open(filename, self.loadfile)
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 5f73a69..86802ff 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,6 +3,19 @@ What's New in IDLE 2.6a1?
*Release date: XX-XXX-200X*
+- 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
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 4c16db9..b16bbc0 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -706,34 +706,36 @@ class ModifiedInterpreter(InteractiveInterpreter):
debugger = self.debugger
try:
self.tkconsole.beginexecuting()
- try:
- if not debugger and self.rpcclt is not None:
- self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
- (code,), {})
- elif debugger:
- debugger.run(code, self.locals)
- else:
- exec(code, self.locals)
- except SystemExit:
- if not self.tkconsole.closing:
- if tkMessageBox.askyesno(
- "Exit?",
- "Do you want to exit altogether?",
- default="yes",
- master=self.tkconsole.text):
- raise
- else:
- self.showtraceback()
- else:
+ if not debugger and self.rpcclt is not None:
+ self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
+ (code,), {})
+ elif debugger:
+ debugger.run(code, self.locals)
+ else:
+ exec(code, self.locals)
+ except SystemExit:
+ if not self.tkconsole.closing:
+ if tkMessageBox.askyesno(
+ "Exit?",
+ "Do you want to exit altogether?",
+ default="yes",
+ master=self.tkconsole.text):
raise
- except:
- if use_subprocess:
- # When run w/o subprocess, both user and IDLE errors
- # are printed here; skip message in that case.
- print("IDLE internal error in runcode()", file=self.tkconsole.stderr)
+ else:
+ else:
+ raise
+ except:
+ if use_subprocess:
+ print("IDLE internal error in runcode()",
+ file=self.tkconsole.stderr)
self.showtraceback()
- if use_subprocess:
- self.tkconsole.endexecuting()
+ self.tkconsole.endexecuting()
+ else:
+ if self.tkconsole.canceled:
+ self.tkconsole.canceled = False
+ print("KeyboardInterrupt", file=self.tkconsole.stderr)
+ else:
+ self.showtraceback()
finally:
if not use_subprocess:
try:
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py
index 469d0e4..0a08de2 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/configHandler.py
@@ -39,22 +39,19 @@ class IdleConfParser(ConfigParser):
self.file=cfgFile
ConfigParser.__init__(self,defaults=cfgDefaults)
- def Get(self, section, option, type=None, default=None):
+ 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.
"""
+ if not self.has_option(section, option):
+ return default
if type=='bool':
- getVal=self.getboolean
+ return self.getboolean(section, option)
elif type=='int':
- getVal=self.getint
- else:
- getVal=self.get
- if self.has_option(section,option):
- #return getVal(section, option, raw, vars, default)
- return getVal(section, option)
+ return self.getint(section, option)
else:
- return default
+ return self.get(section, option, raw=raw)
def GetOptionList(self,section):
"""
@@ -219,7 +216,7 @@ class IdleConf:
return userDir
def GetOption(self, configType, section, option, default=None, type=None,
- warn_on_default=True):
+ warn_on_default=True, raw=False):
"""
Get an option value for given config type and given general
configuration section/option or return a default. If type is specified,
@@ -233,9 +230,11 @@ class IdleConf:
"""
if self.userCfg[configType].has_option(section,option):
- return self.userCfg[configType].Get(section, option, type=type)
+ return self.userCfg[configType].Get(section, option,
+ type=type, raw=raw)
elif self.defaultCfg[configType].has_option(section,option):
- return self.defaultCfg[configType].Get(section, option, type=type)
+ return self.defaultCfg[configType].Get(section, option,
+ type=type, raw=raw)
else: #returning default, print warning
if warn_on_default:
warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt
index 6d2ba2f..76cccf0 100644
--- a/Lib/idlelib/help.txt
+++ b/Lib/idlelib/help.txt
@@ -44,6 +44,10 @@ Edit Menu:
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
+ Show Calltip -- Open a small window with function param hints
+ Show Completions -- Open a scroll window allowing selection keywords
+ and attributes. (see '*TIPS*', below)
+ Show Parens -- Highlight the surrounding parenthesis
Expand Word -- Expand the word you have typed to match another
word in the same buffer; repeat to get a
different expansion
@@ -91,6 +95,7 @@ Options Menu:
Code Context -- 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.
+ (Not present in Shell window.)
Windows Menu:
@@ -138,8 +143,11 @@ Basic editing and navigation:
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 (Control-a, Control-e, Control-k, etc.)
- are inherited from Tcl/Tk.
+ 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 Windows bindings may work on that platform.
Keybindings are selected in the Settings Dialog, look there.
@@ -155,6 +163,52 @@ Automatic indentation:
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) you type a Tab 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. 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 scrollwheel 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 Edit 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. OTOH, you could make the delay zero.
+
+ You could also switch off the CallTips extension. (We will be adding
+ a delay to the call tip window.)
+
Python Shell window:
Control-c interrupts executing command.
@@ -165,7 +219,7 @@ Python Shell window:
Alt-p retrieves previous command matching what you have typed.
Alt-n retrieves next.
- (These are Control-p, Control-n on the Mac)
+ (These are Control-p, Control-n on the Mac)
Return while cursor is on a previous command retrieves that command.
Expand word is also useful to reduce typing.
@@ -196,7 +250,7 @@ Other preferences:
be changed using the Settings dialog.
Command line usage:
-
+
Enter idle -h at the command prompt to get a usage message.
Running without a subprocess:
@@ -211,3 +265,18 @@ Running without a subprocess:
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
diff --git a/Lib/imputil.py b/Lib/imputil.py
index 1472d9c..87c31fa 100644
--- a/Lib/imputil.py
+++ b/Lib/imputil.py
@@ -552,6 +552,10 @@ class _FilesystemImporter(Importer):
# This method is only used when we look for a module within a package.
assert parent
+ for submodule_path in parent.__path__:
+ code = self._import_pathname(_os_path_join(submodule_path, modname), fqname)
+ if code is not None:
+ return code
return self._import_pathname(_os_path_join(parent.__pkgdir__, modname),
fqname)
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 220ff09..7ff9f25 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2007 by Vinay Sajip. All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
@@ -21,7 +21,7 @@ comp.lang.python, and influenced by Apache's log4j system.
Should work under Python versions >= 1.5.2, except that source line
information is not available unless 'sys._getframe()' is.
-Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2007 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
@@ -41,8 +41,8 @@ except ImportError:
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
-__version__ = "0.5.0.1"
-__date__ = "09 January 2007"
+__version__ = "0.5.0.2"
+__date__ = "16 February 2007"
#---------------------------------------------------------------------------
# Miscellaneous module data
@@ -68,7 +68,7 @@ def currentframe():
except:
return sys.exc_traceback.tb_frame.f_back
-if hasattr(sys, '_getframe'): currentframe = sys._getframe
+if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# done filching
# _srcfile is only used in conjunction with sys._getframe().
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 7da5cac..5ed72b3 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -60,13 +60,15 @@ def copymode(src, dst):
os.chmod(dst, mode)
def copystat(src, dst):
- """Copy all stat info (mode bits, atime and mtime) from src to dst"""
+ """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
st = os.stat(src)
mode = stat.S_IMODE(st.st_mode)
if hasattr(os, 'utime'):
os.utime(dst, (st.st_atime, st.st_mtime))
if hasattr(os, 'chmod'):
os.chmod(dst, mode)
+ if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
+ os.chflags(dst, st.st_flags)
def copy(src, dst):
diff --git a/Lib/stat.py b/Lib/stat.py
index 70750d8..5f41687 100644
--- a/Lib/stat.py
+++ b/Lib/stat.py
@@ -84,3 +84,16 @@ S_IRWXO = 00007
S_IROTH = 00004
S_IWOTH = 00002
S_IXOTH = 00001
+
+# Names for file flags
+
+UF_NODUMP = 0x00000001
+UF_IMMUTABLE = 0x00000002
+UF_APPEND = 0x00000004
+UF_OPAQUE = 0x00000008
+UF_NOUNLINK = 0x00000010
+SF_ARCHIVED = 0x00010000
+SF_IMMUTABLE = 0x00020000
+SF_APPEND = 0x00040000
+SF_NOUNLINK = 0x00100000
+SF_SNAPSHOT = 0x00200000
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 759b59f1..e9c9a0e 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -593,14 +593,30 @@ class Popen(object):
c2pread, c2pwrite,
errread, errwrite)
- if p2cwrite:
+ # On Windows, you cannot just redirect one or two handles: You
+ # either have to redirect all three or none. If the subprocess
+ # user has only redirected one or two handles, we are
+ # automatically creating PIPEs for the rest. We should close
+ # these after the process is started. See bug #1124861.
+ if mswindows:
+ if stdin is None and p2cwrite is not None:
+ os.close(p2cwrite)
+ p2cwrite = None
+ if stdout is None and c2pread is not None:
+ os.close(c2pread)
+ c2pread = None
+ if stderr is None and errread is not None:
+ os.close(errread)
+ errread = None
+
+ if p2cwrite is not None:
self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
- if c2pread:
+ if c2pread is not None:
if universal_newlines:
self.stdout = os.fdopen(c2pread, 'rU', bufsize)
else:
self.stdout = os.fdopen(c2pread, 'rb', bufsize)
- if errread:
+ if errread is not None:
if universal_newlines:
self.stderr = os.fdopen(errread, 'rU', bufsize)
else:
@@ -669,7 +685,9 @@ class Popen(object):
if stdin is None:
p2cread = GetStdHandle(STD_INPUT_HANDLE)
- elif stdin == PIPE:
+ if p2cread is not None:
+ pass
+ elif stdin is None or stdin == PIPE:
p2cread, p2cwrite = CreatePipe(None, 0)
# Detach and turn into fd
p2cwrite = p2cwrite.Detach()
@@ -683,7 +701,9 @@ class Popen(object):
if stdout is None:
c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
- elif stdout == PIPE:
+ if c2pwrite is not None:
+ pass
+ elif stdout is None or stdout == PIPE:
c2pread, c2pwrite = CreatePipe(None, 0)
# Detach and turn into fd
c2pread = c2pread.Detach()
@@ -697,7 +717,9 @@ class Popen(object):
if stderr is None:
errwrite = GetStdHandle(STD_ERROR_HANDLE)
- elif stderr == PIPE:
+ if errwrite is not None:
+ pass
+ elif stderr is None or stderr == PIPE:
errread, errwrite = CreatePipe(None, 0)
# Detach and turn into fd
errread = errread.Detach()
@@ -987,29 +1009,29 @@ class Popen(object):
# Child
try:
# Close parent's pipe ends
- if p2cwrite:
+ if p2cwrite is not None:
os.close(p2cwrite)
- if c2pread:
+ if c2pread is not None:
os.close(c2pread)
- if errread:
+ if errread is not None:
os.close(errread)
os.close(errpipe_read)
# Dup fds for child
- if p2cread:
+ if p2cread is not None:
os.dup2(p2cread, 0)
- if c2pwrite:
+ if c2pwrite is not None:
os.dup2(c2pwrite, 1)
- if errwrite:
+ if errwrite is not None:
os.dup2(errwrite, 2)
# Close pipe fds. Make sure we don't close the same
# fd more than once, or standard fds.
- if p2cread and p2cread not in (0,):
+ if p2cread is not None and p2cread not in (0,):
os.close(p2cread)
- if c2pwrite and c2pwrite not in (p2cread, 1):
+ if c2pwrite is not None and c2pwrite not in (p2cread, 1):
os.close(c2pwrite)
- if errwrite and errwrite not in (p2cread, c2pwrite, 2):
+ if errwrite is not None and errwrite not in (p2cread, c2pwrite, 2):
os.close(errwrite)
# Close all other fds, if asked for
@@ -1042,11 +1064,11 @@ class Popen(object):
# Parent
os.close(errpipe_write)
- if p2cread and p2cwrite:
+ if p2cread is not None and p2cwrite is not None:
os.close(p2cread)
- if c2pwrite and c2pread:
+ if c2pwrite is not None and c2pread is not None:
os.close(c2pwrite)
- if errwrite and errread:
+ if errwrite is not None and errread is not None:
os.close(errwrite)
# Wait for exec to fail or succeed; possibly raising exception
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index feea433..146bbb7 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1062,6 +1062,10 @@ class TarFile(object):
self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode]
if not fileobj:
+ if self._mode == "a" and not os.path.exists(self.name):
+ # Create nonexistent files in append mode.
+ self._mode = "w"
+ self.mode = "wb"
fileobj = _open(self.name, self.mode)
self._extfileobj = False
else:
@@ -1095,7 +1099,8 @@ class TarFile(object):
self.fileobj.seek(0)
break
if tarinfo is None:
- self.fileobj.seek(- BLOCKSIZE, 1)
+ if self.offset > 0:
+ self.fileobj.seek(- BLOCKSIZE, 1)
break
if self._mode in "aw":
@@ -1122,7 +1127,7 @@ class TarFile(object):
'r:' open for reading exclusively uncompressed
'r:gz' open for reading with gzip compression
'r:bz2' open for reading with bzip2 compression
- 'a' or 'a:' open for appending
+ 'a' or 'a:' open for appending, creating the file if necessary
'w' or 'w:' open for writing without compression
'w:gz' open for writing with gzip compression
'w:bz2' open for writing with bzip2 compression
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index 70b00a4..6b4c667 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -1767,6 +1767,11 @@ class TestTime(HarmlessMixedComparison):
self.assertEqual(t.isoformat(), "00:00:00.100000")
self.assertEqual(t.isoformat(), str(t))
+ def test_1653736(self):
+ # verify it doesn't accept extra keyword arguments
+ t = self.theclass(second=1)
+ self.assertRaises(TypeError, t.isoformat, foo=3)
+
def test_strftime(self):
t = self.theclass(1, 2, 3, 4)
self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py
index 03900de..c8ae2be 100644
--- a/Lib/test/test_defaultdict.py
+++ b/Lib/test/test_defaultdict.py
@@ -47,6 +47,7 @@ class TestDefaultDict(unittest.TestCase):
self.assertEqual(err.args, (15,))
else:
self.fail("d2[15] didn't raise KeyError")
+ self.assertRaises(TypeError, defaultdict, 1)
def test_missing(self):
d1 = defaultdict()
@@ -60,10 +61,10 @@ class TestDefaultDict(unittest.TestCase):
self.assertEqual(repr(d1), "defaultdict(None, {})")
d1[11] = 41
self.assertEqual(repr(d1), "defaultdict(None, {11: 41})")
- d2 = defaultdict(0)
- self.assertEqual(d2.default_factory, 0)
+ d2 = defaultdict(int)
+ self.assertEqual(d2.default_factory, int)
d2[12] = 42
- self.assertEqual(repr(d2), "defaultdict(0, {12: 42})")
+ self.assertEqual(repr(d2), "defaultdict(<type 'int'>, {12: 42})")
def foo(): return 43
d3 = defaultdict(foo)
self.assert_(d3.default_factory is foo)
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index d369cf6..e8db29e 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -2093,7 +2093,7 @@ def inherits():
__slots__ = ['prec']
def __init__(self, value=0.0, prec=12):
self.prec = int(prec)
- float.__init__(value)
+ float.__init__(self, value)
def __repr__(self):
return "%.*g" % (self.prec, self)
vereq(repr(precfloat(1.1)), "1.1")
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index fb4483a..679181f 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -182,6 +182,14 @@ class DictTest(unittest.TestCase):
self.assertRaises(ValueError, {}.update, [(1, 2, 3)])
+ # SF #1615701: make d.update(m) honor __getitem__() and keys() in dict subclasses
+ class KeyUpperDict(dict):
+ def __getitem__(self, key):
+ return key.upper()
+ d.clear()
+ d.update(KeyUpperDict.fromkeys('abc'))
+ self.assertEqual(d, {'a':'A', 'b':'B', 'c':'C'})
+
def test_fromkeys(self):
self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
d = {}
diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py
index fbdbc30..124a469 100644
--- a/Lib/test/test_gzip.py
+++ b/Lib/test/test_gzip.py
@@ -153,6 +153,13 @@ class TestGzip(unittest.TestCase):
self.assertEqual(f.myfileobj.mode, 'rb')
f.close()
+ def test_1647484(self):
+ for mode in ('wb', 'rb'):
+ f = gzip.GzipFile(self.filename, mode)
+ self.assert_(hasattr(f, "name"))
+ self.assertEqual(f.name, self.filename)
+ f.close()
+
def test_main(verbose=None):
test_support.run_unittest(TestGzip)
diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py
index 934f7b6..156c835 100644
--- a/Lib/test/test_heapq.py
+++ b/Lib/test/test_heapq.py
@@ -1,6 +1,6 @@
"""Unittests for heapq."""
-from heapq import heappush, heappop, heapify, heapreplace, nlargest, nsmallest
+from heapq import heappush, heappop, heapify, heapreplace, merge, nlargest, nsmallest
import random
import unittest
from test import test_support
@@ -103,6 +103,29 @@ class TestHeap(unittest.TestCase):
heap_sorted = [heappop(heap) for i in range(size)]
self.assertEqual(heap_sorted, sorted(data))
+ def test_merge(self):
+ inputs = []
+ for i in xrange(random.randrange(5)):
+ row = sorted(random.randrange(1000) for j in range(random.randrange(10)))
+ inputs.append(row)
+ self.assertEqual(sorted(chain(*inputs)), list(merge(*inputs)))
+ self.assertEqual(list(merge()), [])
+
+ def test_merge_stability(self):
+ class Int(int):
+ pass
+ inputs = [[], [], [], []]
+ for i in range(20000):
+ stream = random.randrange(4)
+ x = random.randrange(500)
+ obj = Int(x)
+ obj.pair = (x, stream)
+ inputs[stream].append(obj)
+ for stream in inputs:
+ stream.sort()
+ result = [i.pair for i in merge(*inputs)]
+ self.assertEqual(result, sorted(result))
+
def test_nsmallest(self):
data = [(random.randrange(2000), i) for i in range(1000)]
for f in (None, lambda x: x[0] * 547 % 2000):
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index ac30495..bbebbd9 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -55,8 +55,7 @@ class TestBasicOps(unittest.TestCase):
self.assertEqual(take(2, lzip('abc',count(3))), [('a', 3), ('b', 4)])
self.assertRaises(TypeError, count, 2, 3)
self.assertRaises(TypeError, count, 'a')
- c = count(sys.maxint-2) # verify that rollover doesn't crash
- c.next(); c.next(); c.next(); c.next(); c.next()
+ self.assertRaises(OverflowError, list, islice(count(sys.maxint-5), 10))
c = count(3)
self.assertEqual(repr(c), 'count(3)')
c.next()
@@ -203,6 +202,51 @@ class TestBasicOps(unittest.TestCase):
ids = map(id, list(izip('abc', 'def')))
self.assertEqual(len(dict.fromkeys(ids)), len(ids))
+ def test_iziplongest(self):
+ for args in [
+ ['abc', range(6)],
+ [range(6), 'abc'],
+ [range(1000), range(2000,2100), range(3000,3050)],
+ [range(1000), range(0), range(3000,3050), range(1200), range(1500)],
+ [range(1000), range(0), range(3000,3050), range(1200), range(1500), range(0)],
+ ]:
+ target = map(None, *args)
+ self.assertEqual(list(izip_longest(*args)), target)
+ self.assertEqual(list(izip_longest(*args, **{})), target)
+ target = [tuple((e is None and 'X' or e) for e in t) for t in target] # Replace None fills with 'X'
+ self.assertEqual(list(izip_longest(*args, **dict(fillvalue='X'))), target)
+
+ self.assertEqual(take(3,izip_longest('abcdef', count())), list(zip('abcdef', range(3)))) # take 3 from infinite input
+
+ self.assertEqual(list(izip_longest()), list(zip()))
+ self.assertEqual(list(izip_longest([])), list(zip([])))
+ self.assertEqual(list(izip_longest('abcdef')), list(zip('abcdef')))
+
+ self.assertEqual(list(izip_longest('abc', 'defg', **{})), map(None, 'abc', 'defg')) # empty keyword dict
+ self.assertRaises(TypeError, izip_longest, 3)
+ self.assertRaises(TypeError, izip_longest, range(3), 3)
+
+ for stmt in [
+ "izip_longest('abc', fv=1)",
+ "izip_longest('abc', fillvalue=1, bogus_keyword=None)",
+ ]:
+ try:
+ eval(stmt, globals(), locals())
+ except TypeError:
+ pass
+ else:
+ self.fail('Did not raise Type in: ' + stmt)
+
+ # Check tuple re-use (implementation detail)
+ self.assertEqual([tuple(list(pair)) for pair in izip_longest('abc', 'def')],
+ list(zip('abc', 'def')))
+ self.assertEqual([pair for pair in izip_longest('abc', 'def')],
+ list(zip('abc', 'def')))
+ ids = map(id, izip_longest('abc', 'def'))
+ self.assertEqual(min(ids), max(ids))
+ ids = map(id, list(izip_longest('abc', 'def')))
+ self.assertEqual(len(dict.fromkeys(ids)), len(ids))
+
def test_repeat(self):
self.assertEqual(lzip(xrange(3),repeat('a')),
[(0, 'a'), (1, 'a'), (2, 'a')])
@@ -616,6 +660,15 @@ class TestVariousIteratorArgs(unittest.TestCase):
self.assertRaises(TypeError, izip, N(s))
self.assertRaises(ZeroDivisionError, list, izip(E(s)))
+ def test_iziplongest(self):
+ for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)):
+ for g in (G, I, Ig, S, L, R):
+ self.assertEqual(list(izip_longest(g(s))), list(zip(g(s))))
+ self.assertEqual(list(izip_longest(g(s), g(s))), list(zip(g(s), g(s))))
+ self.assertRaises(TypeError, izip_longest, X(s))
+ self.assertRaises(TypeError, izip_longest, N(s))
+ self.assertRaises(ZeroDivisionError, list, izip_longest(E(s)))
+
def test_imap(self):
for s in (range(10), range(0), range(100), (7,11), xrange(20,50,5)):
for g in (G, I, Ig, S, L, R):
diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py
index 2183508..e05d054 100644
--- a/Lib/test/test_operator.py
+++ b/Lib/test/test_operator.py
@@ -210,6 +210,8 @@ class OperatorTestCase(unittest.TestCase):
self.failUnless(operator.isSequenceType(xrange(10)))
self.failUnless(operator.isSequenceType('yeahbuddy'))
self.failIf(operator.isSequenceType(3))
+ class Dict(dict): pass
+ self.failIf(operator.isSequenceType(Dict()))
def test_lshift(self):
self.failUnlessRaises(TypeError, operator.lshift)
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index f98c723..7207de5 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -192,6 +192,18 @@ class PosixTester(unittest.TestCase):
posix.utime(test_support.TESTFN, (int(now), int(now)))
posix.utime(test_support.TESTFN, (now, now))
+ def test_chflags(self):
+ if hasattr(posix, 'chflags'):
+ st = os.stat(test_support.TESTFN)
+ if hasattr(st, 'st_flags'):
+ posix.chflags(test_support.TESTFN, st.st_flags)
+
+ def test_lchflags(self):
+ if hasattr(posix, 'lchflags'):
+ st = os.stat(test_support.TESTFN)
+ if hasattr(st, 'st_flags'):
+ posix.lchflags(test_support.TESTFN, st.st_flags)
+
def test_main():
test_support.run_unittest(PosixTester)
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
index fbe742d..5b071dd 100644
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -216,7 +216,44 @@ def test_xmlgen_ns():
('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' %
ns_uri)
-# ===== XMLFilterBase
+def test_1463026_1():
+ result = StringIO()
+ gen = XMLGenerator(result)
+
+ gen.startDocument()
+ gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'})
+ gen.endElementNS((None, 'a'), 'a')
+ gen.endDocument()
+
+ return result.getvalue() == start+'<a b="c"></a>'
+
+def test_1463026_2():
+ result = StringIO()
+ gen = XMLGenerator(result)
+
+ gen.startDocument()
+ gen.startPrefixMapping(None, 'qux')
+ gen.startElementNS(('qux', 'a'), 'a', {})
+ gen.endElementNS(('qux', 'a'), 'a')
+ gen.endPrefixMapping(None)
+ gen.endDocument()
+
+ return result.getvalue() == start+'<a xmlns="qux"></a>'
+
+def test_1463026_3():
+ result = StringIO()
+ gen = XMLGenerator(result)
+
+ gen.startDocument()
+ gen.startPrefixMapping('my', 'qux')
+ gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'})
+ gen.endElementNS(('qux', 'a'), 'a')
+ gen.endPrefixMapping('my')
+ gen.endDocument()
+
+ return result.getvalue() == start+'<my:a xmlns:my="qux" b="c"></my:a>'
+
+# ===== Xmlfilterbase
def test_filter_basic():
result = StringIO()
diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py
index 2a791e4..8a43109 100644
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -26,6 +26,14 @@ class ReprWrapper:
def __repr__(self):
return repr(self.value)
+class HashCountingInt(int):
+ 'int-like object that counts the number of times __hash__ is called'
+ def __init__(self, *args):
+ self.hash_count = 0
+ def __hash__(self):
+ self.hash_count += 1
+ return int.__hash__(self)
+
class TestJointOps(unittest.TestCase):
# Tests common to both set and frozenset
@@ -273,6 +281,18 @@ class TestJointOps(unittest.TestCase):
fo.close()
os.remove(test_support.TESTFN)
+ def test_do_not_rehash_dict_keys(self):
+ n = 10
+ d = dict.fromkeys(map(HashCountingInt, xrange(n)))
+ self.assertEqual(sum(elem.hash_count for elem in d), n)
+ s = self.thetype(d)
+ self.assertEqual(sum(elem.hash_count for elem in d), n)
+ s.difference(d)
+ self.assertEqual(sum(elem.hash_count for elem in d), n)
+ if hasattr(s, 'symmetric_difference_update'):
+ s.symmetric_difference_update(d)
+ self.assertEqual(sum(elem.hash_count for elem in d), n)
+
class TestSet(TestJointOps):
thetype = set
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 9ae913d..e9bf497 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -305,6 +305,61 @@ class WriteTest(BaseTest):
self.assertEqual(self.dst.getnames(), [], "added the archive to itself")
+class AppendTest(unittest.TestCase):
+ # Test append mode (cp. patch #1652681).
+
+ def setUp(self):
+ self.tarname = tmpname()
+ if os.path.exists(self.tarname):
+ os.remove(self.tarname)
+
+ def _add_testfile(self, fileobj=None):
+ tar = tarfile.open(self.tarname, "a", fileobj=fileobj)
+ tar.addfile(tarfile.TarInfo("bar"))
+ tar.close()
+
+ def _create_testtar(self):
+ src = tarfile.open(tarname())
+ t = src.getmember("0-REGTYPE")
+ t.name = "foo"
+ f = src.extractfile(t)
+ tar = tarfile.open(self.tarname, "w")
+ tar.addfile(t, f)
+ tar.close()
+
+ def _test(self, names=["bar"], fileobj=None):
+ tar = tarfile.open(self.tarname, fileobj=fileobj)
+ self.assert_(tar.getnames() == names)
+
+ def test_non_existing(self):
+ self._add_testfile()
+ self._test()
+
+ def test_empty(self):
+ open(self.tarname, "wb").close()
+ self._add_testfile()
+ self._test()
+
+ def test_empty_fileobj(self):
+ fobj = StringIO.StringIO()
+ self._add_testfile(fobj)
+ fobj.seek(0)
+ self._test(fileobj=fobj)
+
+ def test_fileobj(self):
+ self._create_testtar()
+ data = open(self.tarname, "rb").read()
+ fobj = StringIO.StringIO(data)
+ self._add_testfile(fobj)
+ fobj.seek(0)
+ self._test(names=["foo", "bar"], fileobj=fobj)
+
+ def test_existing(self):
+ self._create_testtar()
+ self._add_testfile()
+ self._test(names=["foo", "bar"])
+
+
class Write100Test(BaseTest):
# The name field in a tar header stores strings of at most 100 chars.
# If a string is shorter than 100 chars it has to be padded with '\0',
@@ -711,6 +766,7 @@ def test_main():
ReadAsteriskTest,
ReadStreamAsteriskTest,
WriteTest,
+ AppendTest,
Write100Test,
WriteSize0Test,
WriteStreamTest,
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
index 56c4939..d6f5534 100644
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -307,6 +307,28 @@ class PyZipFileTests(unittest.TestCase):
class OtherTests(unittest.TestCase):
+ def testCreateNonExistentFileForAppend(self):
+ if os.path.exists(TESTFN):
+ os.unlink(TESTFN)
+
+ filename = 'testfile.txt'
+ content = 'hello, world. this is some content.'
+
+ try:
+ zf = zipfile.ZipFile(TESTFN, 'a')
+ zf.writestr(filename, content)
+ zf.close()
+ except IOError:
+ self.fail('Could not append data to a non-existent zip file.')
+
+ self.assert_(os.path.exists(TESTFN))
+
+ zf = zipfile.ZipFile(TESTFN, 'r')
+ self.assertEqual(zf.read(filename), content)
+ zf.close()
+
+ os.unlink(TESTFN)
+
def testCloseErroneousFile(self):
# This test checks that the ZipFile constructor closes the file object
# it opens if there's an error in the file. If it doesn't, the traceback
@@ -349,8 +371,49 @@ class OtherTests(unittest.TestCase):
# and report that the first file in the archive was corrupt.
self.assertRaises(RuntimeError, zipf.testzip)
+
+class DecryptionTests(unittest.TestCase):
+ # This test checks that ZIP decryption works. Since the library does not
+ # support encryption at the moment, we use a pre-generated encrypted
+ # ZIP file
+
+ data = (
+ 'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
+ '\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
+ '\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
+ 'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
+ '\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
+ '\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
+ '\x00\x00L\x00\x00\x00\x00\x00' )
+
+ plain = 'zipfile.py encryption test'
+
+ def setUp(self):
+ fp = open(TESTFN, "wb")
+ fp.write(self.data)
+ fp.close()
+ self.zip = zipfile.ZipFile(TESTFN, "r")
+
+ def tearDown(self):
+ self.zip.close()
+ os.unlink(TESTFN)
+
+ def testNoPassword(self):
+ # Reading the encrypted file without password
+ # must generate a RunTime exception
+ self.assertRaises(RuntimeError, self.zip.read, "test.txt")
+
+ def testBadPassword(self):
+ self.zip.setpassword("perl")
+ self.assertRaises(RuntimeError, self.zip.read, "test.txt")
+
+ def testGoodPassword(self):
+ self.zip.setpassword("python")
+ self.assertEquals(self.zip.read("test.txt"), self.plain)
+
def test_main():
- run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, PyZipFileTests)
+ run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests,
+ PyZipFileTests, DecryptionTests)
#run_unittest(TestZip64InSmallFiles)
if __name__ == "__main__":
diff --git a/Lib/trace.py b/Lib/trace.py
index 0f89c15..a1f9ade 100644
--- a/Lib/trace.py
+++ b/Lib/trace.py
@@ -587,7 +587,7 @@ class Trace:
"""
if why == 'call':
code = frame.f_code
- filename = code.co_filename
+ filename = frame.f_globals.get('__file__', None)
if filename:
# XXX modname() doesn't work right for packages, so
# the ignore support won't work right for packages
diff --git a/Lib/xml/sax/saxutils.py b/Lib/xml/sax/saxutils.py
index b5b9ff3..2b0d204 100644
--- a/Lib/xml/sax/saxutils.py
+++ b/Lib/xml/sax/saxutils.py
@@ -100,6 +100,17 @@ class XMLGenerator(handler.ContentHandler):
else:
self._out.write(text.encode(self._encoding, _error_handling))
+ def _qname(self, name):
+ """Builds a qualified name from a (ns_url, localname) pair"""
+ if name[0]:
+ # The name is in a non-empty namespace
+ prefix = self._current_context[name[0]]
+ if prefix:
+ # If it is not the default namespace, prepend the prefix
+ return prefix + ":" + name[1]
+ # Return the unqualified name
+ return name[1]
+
# ContentHandler methods
def startDocument(self):
@@ -125,29 +136,21 @@ class XMLGenerator(handler.ContentHandler):
self._write('</%s>' % name)
def startElementNS(self, name, qname, attrs):
- if name[0] is None:
- # if the name was not namespace-scoped, use the unqualified part
- name = name[1]
- else:
- # else try to restore the original prefix from the namespace
- name = self._current_context[name[0]] + ":" + name[1]
- self._write('<' + name)
+ self._write('<' + self._qname(name))
- for pair in self._undeclared_ns_maps:
- self._write(' xmlns:%s="%s"' % pair)
+ for prefix, uri in self._undeclared_ns_maps:
+ if prefix:
+ self._out.write(' xmlns:%s="%s"' % (prefix, uri))
+ else:
+ self._out.write(' xmlns="%s"' % uri)
self._undeclared_ns_maps = []
for (name, value) in attrs.items():
- name = self._current_context[name[0]] + ":" + name[1]
- self._write(' %s=%s' % (name, quoteattr(value)))
+ self._write(' %s=%s' % (self._qname(name), quoteattr(value)))
self._write('>')
def endElementNS(self, name, qname):
- if name[0] is None:
- name = name[1]
- else:
- name = self._current_context[name[0]] + ":" + name[1]
- self._write('</%s>' % name)
+ self._write('</%s>' % self._qname(name))
def characters(self, content):
self._write(escape(content))
diff --git a/Lib/zipfile.py b/Lib/zipfile.py
index c6162ff..dc51168 100644
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -296,6 +296,65 @@ class ZipInfo (object):
extra = extra[ln+4:]
+class _ZipDecrypter:
+ """Class to handle decryption of files stored within a ZIP archive.
+
+ ZIP supports a password-based form of encryption. Even though known
+ plaintext attacks have been found against it, it is still useful
+ for low-level securicy.
+
+ Usage:
+ zd = _ZipDecrypter(mypwd)
+ plain_char = zd(cypher_char)
+ plain_text = map(zd, cypher_text)
+ """
+
+ def _GenerateCRCTable():
+ """Generate a CRC-32 table.
+
+ ZIP encryption uses the CRC32 one-byte primitive for scrambling some
+ internal keys. We noticed that a direct implementation is faster than
+ relying on binascii.crc32().
+ """
+ poly = 0xedb88320
+ table = [0] * 256
+ for i in range(256):
+ crc = i
+ for j in range(8):
+ if crc & 1:
+ crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly
+ else:
+ crc = ((crc >> 1) & 0x7FFFFFFF)
+ table[i] = crc
+ return table
+ crctable = _GenerateCRCTable()
+
+ def _crc32(self, ch, crc):
+ """Compute the CRC32 primitive on one byte."""
+ return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ord(ch)) & 0xff]
+
+ def __init__(self, pwd):
+ self.key0 = 305419896
+ self.key1 = 591751049
+ self.key2 = 878082192
+ for p in pwd:
+ self._UpdateKeys(p)
+
+ def _UpdateKeys(self, c):
+ self.key0 = self._crc32(c, self.key0)
+ self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295
+ self.key1 = (self.key1 * 134775813 + 1) & 4294967295
+ self.key2 = self._crc32(chr((self.key1 >> 24) & 255), self.key2)
+
+ def __call__(self, c):
+ """Decrypt a single character."""
+ c = ord(c)
+ k = self.key2 | 2
+ c = c ^ (((k * (k^1)) >> 8) & 255)
+ c = chr(c)
+ self._UpdateKeys(c)
+ return c
+
class ZipFile:
""" Class with methods to open, read, write, close, list zip files.
@@ -330,13 +389,21 @@ class ZipFile:
self.filelist = [] # List of ZipInfo instances for archive
self.compression = compression # Method of compression
self.mode = key = mode.replace('b', '')[0]
+ self.pwd = None
# Check if we were passed a file-like object
if isinstance(file, basestring):
self._filePassed = 0
self.filename = file
modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
- self.fp = open(file, modeDict[mode])
+ try:
+ self.fp = open(file, modeDict[mode])
+ except IOError:
+ if mode == 'a':
+ mode = key = 'w'
+ self.fp = open(file, modeDict[mode])
+ else:
+ raise
else:
self._filePassed = 1
self.fp = file
@@ -461,7 +528,11 @@ class ZipFile:
"""Return the instance of ZipInfo given 'name'."""
return self.NameToInfo[name]
- def read(self, name):
+ def setpassword(self, pwd):
+ """Set default password for encrypted files."""
+ self.pwd = pwd
+
+ def read(self, name, pwd=None):
"""Return file bytes (as a string) for name."""
if self.mode not in ("r", "a"):
raise RuntimeError, 'read() requires mode "r" or "a"'
@@ -469,6 +540,13 @@ class ZipFile:
raise RuntimeError, \
"Attempt to read ZIP archive that was already closed"
zinfo = self.getinfo(name)
+ is_encrypted = zinfo.flag_bits & 0x1
+ if is_encrypted:
+ if not pwd:
+ pwd = self.pwd
+ if not pwd:
+ raise RuntimeError, "File %s is encrypted, " \
+ "password required for extraction" % name
filepos = self.fp.tell()
self.fp.seek(zinfo.header_offset, 0)
@@ -489,6 +567,18 @@ class ZipFile:
zinfo.orig_filename, fname)
bytes = self.fp.read(zinfo.compress_size)
+ # Go with decryption
+ if is_encrypted:
+ zd = _ZipDecrypter(pwd)
+ # The first 12 bytes in the cypher stream is an encryption header
+ # used to strengthen the algorithm. The first 11 bytes are
+ # completely random, while the 12th contains the MSB of the CRC,
+ # and is used to check the correctness of the password.
+ h = map(zd, bytes[0:12])
+ if ord(h[11]) != ((zinfo.CRC>>24)&255):
+ raise RuntimeError, "Bad password for file %s" % name
+ bytes = "".join(map(zd, bytes[12:]))
+ # Go with decompression
self.fp.seek(filepos, 0)
if zinfo.compress_type == ZIP_STORED:
pass