merge 2.7.9 release branch
diff --git a/Doc/library/json.rst b/Doc/library/json.rst
index caee953..db9df0a 100644
--- a/Doc/library/json.rst
+++ b/Doc/library/json.rst
@@ -8,9 +8,11 @@
.. versionadded:: 2.6
`JSON (JavaScript Object Notation) <http://json.org>`_, specified by
-:rfc:`4627`, is a lightweight data interchange format based on a subset of
-`JavaScript <http://en.wikipedia.org/wiki/JavaScript>`_ syntax (`ECMA-262 3rd
-edition <http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf>`_).
+:rfc:`7159` (which obsoletes :rfc:`4627`) and by
+`ECMA-404 <http://www.ecma-international.org/publications/standards/Ecma-404.htm>`_,
+is a lightweight data interchange format inspired by
+`JavaScript <http://en.wikipedia.org/wiki/JavaScript>`_ object literal syntax
+(although it is not a strict subset of JavaScript [#rfc-errata]_ ).
:mod:`json` exposes an API familiar to users of the standard library
:mod:`marshal` and :mod:`pickle` modules.
@@ -485,18 +487,18 @@
mysocket.write(chunk)
-Standard Compliance
--------------------
+Standard Compliance and Interoperability
+----------------------------------------
-The JSON format is specified by :rfc:`4627`. This section details this
-module's level of compliance with the RFC. For simplicity,
-:class:`JSONEncoder` and :class:`JSONDecoder` subclasses, and parameters other
-than those explicitly mentioned, are not considered.
+The JSON format is specified by :rfc:`7159` and by
+`ECMA-404 <http://www.ecma-international.org/publications/standards/Ecma-404.htm>`_.
+This section details this module's level of compliance with the RFC.
+For simplicity, :class:`JSONEncoder` and :class:`JSONDecoder` subclasses, and
+parameters other than those explicitly mentioned, are not considered.
This module does not comply with the RFC in a strict fashion, implementing some
extensions that are valid JavaScript but not valid JSON. In particular:
-- Top-level non-object, non-array values are accepted and output;
- Infinite and NaN number values are accepted and output;
- Repeated names within an object are accepted, and only the value of the last
name-value pair is used.
@@ -508,48 +510,30 @@
Character Encodings
^^^^^^^^^^^^^^^^^^^
-The RFC recommends that JSON be represented using either UTF-8, UTF-16, or
-UTF-32, with UTF-8 being the default. Accordingly, this module uses UTF-8 as
-the default for its *encoding* parameter.
+The RFC requires that JSON be represented using either UTF-8, UTF-16, or
+UTF-32, with UTF-8 being the recommended default for maximum interoperability.
+Accordingly, this module uses UTF-8 as the default for its *encoding* parameter.
This module's deserializer only directly works with ASCII-compatible encodings;
UTF-16, UTF-32, and other ASCII-incompatible encodings require the use of
workarounds described in the documentation for the deserializer's *encoding*
parameter.
-The RFC also non-normatively describes a limited encoding detection technique
-for JSON texts; this module's deserializer does not implement this or any other
-kind of encoding detection.
-
As permitted, though not required, by the RFC, this module's serializer sets
*ensure_ascii=True* by default, thus escaping the output so that the resulting
strings only contain ASCII characters.
+The RFC prohibits adding a byte order mark (BOM) to the start of a JSON text,
+and this module's serializer does not add a BOM to its output.
+The RFC permits, but does not require, JSON deserializers to ignore an initial
+BOM in their input. This module's deserializer raises a :exc:`ValueError`
+when an initial BOM is present.
-Top-level Non-Object, Non-Array Values
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The RFC specifies that the top-level value of a JSON text must be either a
-JSON object or array (Python :class:`dict` or :class:`list`). This module's
-deserializer also accepts input texts consisting solely of a
-JSON null, boolean, number, or string value::
-
- >>> just_a_json_string = '"spam and eggs"' # Not by itself a valid JSON text
- >>> json.loads(just_a_json_string)
- u'spam and eggs'
-
-This module itself does not include a way to request that such input texts be
-regarded as illegal. Likewise, this module's serializer also accepts single
-Python :data:`None`, :class:`bool`, numeric, and :class:`str`
-values as input and will generate output texts consisting solely of a top-level
-JSON null, boolean, number, or string value without raising an exception::
-
- >>> neither_a_list_nor_a_dict = u"spam and eggs"
- >>> json.dumps(neither_a_list_nor_a_dict) # The result is not a valid JSON text
- '"spam and eggs"'
-
-This module's serializer does not itself include a way to enforce the
-aforementioned constraint.
+The RFC does not explicitly forbid JSON strings which contain byte sequences
+that don't correspond to valid Unicode characters (e.g. unpaired UTF-16
+surrogates), but it does note that they may cause interoperability problems.
+By default, this module accepts and outputs (when present in the original
+:class:`str`) codepoints for such sequences.
Infinite and NaN Number Values
@@ -579,7 +563,7 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The RFC specifies that the names within a JSON object should be unique, but
-does not specify how repeated names in JSON objects should be handled. By
+does not mandate how repeated names in JSON objects should be handled. By
default, this module does not raise an exception; instead, it ignores all but
the last name-value pair for a given name::
@@ -588,3 +572,48 @@
{u'x': 3}
The *object_pairs_hook* parameter can be used to alter this behavior.
+
+
+Top-level Non-Object, Non-Array Values
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The old version of JSON specified by the obsolete :rfc:`4627` required that
+the top-level value of a JSON text must be either a JSON object or array
+(Python :class:`dict` or :class:`list`), and could not be a JSON null,
+boolean, number, or string value. :rfc:`7159` removed that restriction, and
+this module does not and has never implemented that restriction in either its
+serializer or its deserializer.
+
+Regardless, for maximum interoperability, you may wish to voluntarily adhere
+to the restriction yourself.
+
+
+Implementation Limitations
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some JSON deserializer implementations may set limits on:
+
+* the size of accepted JSON texts
+* the maximum level of nesting of JSON objects and arrays
+* the range and precision of JSON numbers
+* the content and maximum length of JSON strings
+
+This module does not impose any such limits beyond those of the relevant
+Python datatypes themselves or the Python interpreter itself.
+
+When serializing to JSON, beware any such limitations in applications that may
+consume your JSON. In particular, it is common for JSON numbers to be
+deserialized into IEEE 754 double precision numbers and thus subject to that
+representation's range and precision limitations. This is especially relevant
+when serializing Python :class:`int` values of extremely large magnitude, or
+when serializing instances of "exotic" numerical types such as
+:class:`decimal.Decimal`.
+
+
+.. rubric:: Footnotes
+
+.. [#rfc-errata] As noted in `the errata for RFC 7159
+ <http://www.rfc-editor.org/errata_search.php?rfc=7159>`_,
+ JSON permits literal U+2028 (LINE SEPARATOR) and
+ U+2029 (PARAGRAPH SEPARATOR) characters in strings, whereas JavaScript
+ (as of ECMAScript Edition 5.1) does not.
diff --git a/Doc/library/pydoc.rst b/Doc/library/pydoc.rst
index 494a48d..57521f7 100644
--- a/Doc/library/pydoc.rst
+++ b/Doc/library/pydoc.rst
@@ -53,6 +53,10 @@
executed on that occasion. Use an ``if __name__ == '__main__':`` guard to
only execute code when a file is invoked as a script and not just imported.
+When printing output to the console, :program:`pydoc` attempts to paginate the
+output for easier reading. If the :envvar:`PAGER` environment variable is set,
+:program:`pydoc` will use its value as a pagination program.
+
Specifying a ``-w`` flag before the argument will cause HTML documentation
to be written out to a file in the current directory, instead of displaying text
on the console.
diff --git a/Lib/_abcoll.py b/Lib/_abcoll.py
index 3d567e3..0385627 100644
--- a/Lib/_abcoll.py
+++ b/Lib/_abcoll.py
@@ -548,23 +548,25 @@
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k, v in F.items(): D[k] = v
'''
- if len(args) > 2:
- raise TypeError("update() takes at most 2 positional "
- "arguments ({} given)".format(len(args)))
- elif not args:
- raise TypeError("update() takes at least 1 argument (0 given)")
+ if not args:
+ raise TypeError("descriptor 'update' of 'MutableMapping' object "
+ "needs an argument")
self = args[0]
- other = args[1] if len(args) >= 2 else ()
-
- if isinstance(other, Mapping):
- for key in other:
- self[key] = other[key]
- elif hasattr(other, "keys"):
- for key in other.keys():
- self[key] = other[key]
- else:
- for key, value in other:
- self[key] = value
+ args = args[1:]
+ if len(args) > 1:
+ raise TypeError('update expected at most 1 arguments, got %d' %
+ len(args))
+ if args:
+ other = args[0]
+ if isinstance(other, Mapping):
+ for key in other:
+ self[key] = other[key]
+ elif hasattr(other, "keys"):
+ for key in other.keys():
+ self[key] = other[key]
+ else:
+ for key, value in other:
+ self[key] = value
for key, value in kwds.items():
self[key] = value
diff --git a/Lib/bsddb/test/test_all.py b/Lib/bsddb/test/test_all.py
index caef1ac..004e357 100644
--- a/Lib/bsddb/test/test_all.py
+++ b/Lib/bsddb/test/test_all.py
@@ -412,9 +412,6 @@
def get_dbp(self) :
return self._db
- import string
- string.letters=[chr(i) for i in xrange(65,91)]
-
bsddb._db.DBEnv_orig = bsddb._db.DBEnv
bsddb._db.DB_orig = bsddb._db.DB
if bsddb.db.version() <= (4, 3) :
diff --git a/Lib/bsddb/test/test_basics.py b/Lib/bsddb/test/test_basics.py
index 3c57be4..1459d36 100644
--- a/Lib/bsddb/test/test_basics.py
+++ b/Lib/bsddb/test/test_basics.py
@@ -999,7 +999,7 @@
for x in "The quick brown fox jumped over the lazy dog".split():
d2.put(x, self.makeData(x))
- for x in string.letters:
+ for x in string.ascii_letters:
d3.put(x, x*70)
d1.sync()
@@ -1047,7 +1047,7 @@
if verbose:
print rec
rec = c3.next()
- self.assertEqual(count, len(string.letters))
+ self.assertEqual(count, len(string.ascii_letters))
c1.close()
diff --git a/Lib/bsddb/test/test_dbshelve.py b/Lib/bsddb/test/test_dbshelve.py
index c3701e1..e5609c5 100644
--- a/Lib/bsddb/test/test_dbshelve.py
+++ b/Lib/bsddb/test/test_dbshelve.py
@@ -59,7 +59,7 @@
return bytes(key, "iso8859-1") # 8 bits
def populateDB(self, d):
- for x in string.letters:
+ for x in string.ascii_letters:
d[self.mk('S' + x)] = 10 * x # add a string
d[self.mk('I' + x)] = ord(x) # add an integer
d[self.mk('L' + x)] = [x] * 10 # add a list
diff --git a/Lib/bsddb/test/test_get_none.py b/Lib/bsddb/test/test_get_none.py
index 8763b54..541044c 100644
--- a/Lib/bsddb/test/test_get_none.py
+++ b/Lib/bsddb/test/test_get_none.py
@@ -26,14 +26,14 @@
d.open(self.filename, db.DB_BTREE, db.DB_CREATE)
d.set_get_returns_none(1)
- for x in string.letters:
+ for x in string.ascii_letters:
d.put(x, x * 40)
data = d.get('bad key')
self.assertEqual(data, None)
- data = d.get(string.letters[0])
- self.assertEqual(data, string.letters[0]*40)
+ data = d.get(string.ascii_letters[0])
+ self.assertEqual(data, string.ascii_letters[0]*40)
count = 0
c = d.cursor()
@@ -43,7 +43,7 @@
rec = c.next()
self.assertEqual(rec, None)
- self.assertEqual(count, len(string.letters))
+ self.assertEqual(count, len(string.ascii_letters))
c.close()
d.close()
@@ -54,14 +54,14 @@
d.open(self.filename, db.DB_BTREE, db.DB_CREATE)
d.set_get_returns_none(0)
- for x in string.letters:
+ for x in string.ascii_letters:
d.put(x, x * 40)
self.assertRaises(db.DBNotFoundError, d.get, 'bad key')
self.assertRaises(KeyError, d.get, 'bad key')
- data = d.get(string.letters[0])
- self.assertEqual(data, string.letters[0]*40)
+ data = d.get(string.ascii_letters[0])
+ self.assertEqual(data, string.ascii_letters[0]*40)
count = 0
exceptionHappened = 0
@@ -77,7 +77,7 @@
self.assertNotEqual(rec, None)
self.assertTrue(exceptionHappened)
- self.assertEqual(count, len(string.letters))
+ self.assertEqual(count, len(string.ascii_letters))
c.close()
d.close()
diff --git a/Lib/bsddb/test/test_queue.py b/Lib/bsddb/test/test_queue.py
index d3a0c8b..5fa22ee 100644
--- a/Lib/bsddb/test/test_queue.py
+++ b/Lib/bsddb/test/test_queue.py
@@ -10,7 +10,6 @@
#----------------------------------------------------------------------
-@unittest.skip("fails on Windows; see issue 22943")
class SimpleQueueTestCase(unittest.TestCase):
def setUp(self):
self.filename = get_new_database_path()
@@ -37,17 +36,17 @@
print "before appends" + '-' * 30
pprint(d.stat())
- for x in string.letters:
+ for x in string.ascii_letters:
d.append(x * 40)
- self.assertEqual(len(d), len(string.letters))
+ self.assertEqual(len(d), len(string.ascii_letters))
d.put(100, "some more data")
d.put(101, "and some more ")
d.put(75, "out of order")
d.put(1, "replacement data")
- self.assertEqual(len(d), len(string.letters)+3)
+ self.assertEqual(len(d), len(string.ascii_letters)+3)
if verbose:
print "before close" + '-' * 30
@@ -108,17 +107,17 @@
print "before appends" + '-' * 30
pprint(d.stat())
- for x in string.letters:
+ for x in string.ascii_letters:
d.append(x * 40)
- self.assertEqual(len(d), len(string.letters))
+ self.assertEqual(len(d), len(string.ascii_letters))
d.put(100, "some more data")
d.put(101, "and some more ")
d.put(75, "out of order")
d.put(1, "replacement data")
- self.assertEqual(len(d), len(string.letters)+3)
+ self.assertEqual(len(d), len(string.ascii_letters)+3)
if verbose:
print "before close" + '-' * 30
diff --git a/Lib/bsddb/test/test_recno.py b/Lib/bsddb/test/test_recno.py
index fb6956a..b0e30de 100644
--- a/Lib/bsddb/test/test_recno.py
+++ b/Lib/bsddb/test/test_recno.py
@@ -4,12 +4,11 @@
import os, sys
import errno
from pprint import pprint
+import string
import unittest
from test_all import db, test_support, verbose, get_new_environment_path, get_new_database_path
-letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-
#----------------------------------------------------------------------
@@ -39,7 +38,7 @@
d.open(self.filename, db.DB_RECNO, db.DB_CREATE)
- for x in letters:
+ for x in string.ascii_letters:
recno = d.append(x * 60)
self.assertIsInstance(recno, int)
self.assertGreaterEqual(recno, 1)
@@ -270,7 +269,7 @@
d.set_re_pad(45) # ...test both int and char
d.open(self.filename, db.DB_RECNO, db.DB_CREATE)
- for x in letters:
+ for x in string.ascii_letters:
d.append(x * 35) # These will be padded
d.append('.' * 40) # this one will be exact
diff --git a/Lib/collections.py b/Lib/collections.py
index 8831f0b..1dcd233 100644
--- a/Lib/collections.py
+++ b/Lib/collections.py
@@ -35,12 +35,17 @@
# The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
- def __init__(self, *args, **kwds):
+ def __init__(*args, **kwds):
'''Initialize an ordered dictionary. The signature is the same as
regular dictionaries, but keyword arguments are not recommended because
their insertion order is arbitrary.
'''
+ if not args:
+ raise TypeError("descriptor '__init__' of 'OrderedDict' object "
+ "needs an argument")
+ self = args[0]
+ args = args[1:]
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
@@ -438,7 +443,7 @@
# http://code.activestate.com/recipes/259174/
# Knuth, TAOCP Vol. II section 4.6.3
- def __init__(self, iterable=None, **kwds):
+ def __init__(*args, **kwds):
'''Create a new, empty Counter object. And if given, count elements
from an input iterable. Or, initialize the count from another mapping
of elements to their counts.
@@ -449,8 +454,15 @@
>>> c = Counter(a=4, b=2) # a new counter from keyword args
'''
+ if not args:
+ raise TypeError("descriptor '__init__' of 'Counter' object "
+ "needs an argument")
+ self = args[0]
+ args = args[1:]
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
super(Counter, self).__init__()
- self.update(iterable, **kwds)
+ self.update(*args, **kwds)
def __missing__(self, key):
'The count of elements not in the Counter is zero.'
@@ -501,7 +513,7 @@
raise NotImplementedError(
'Counter.fromkeys() is undefined. Use Counter(iterable) instead.')
- def update(self, iterable=None, **kwds):
+ def update(*args, **kwds):
'''Like dict.update() but add counts instead of replacing them.
Source can be an iterable, a dictionary, or another Counter instance.
@@ -521,6 +533,14 @@
# contexts. Instead, we implement straight-addition. Both the inputs
# and outputs are allowed to contain zero and negative counts.
+ if not args:
+ raise TypeError("descriptor 'update' of 'Counter' object "
+ "needs an argument")
+ self = args[0]
+ args = args[1:]
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ iterable = args[0] if args else None
if iterable is not None:
if isinstance(iterable, Mapping):
if self:
@@ -536,7 +556,7 @@
if kwds:
self.update(kwds)
- def subtract(self, iterable=None, **kwds):
+ def subtract(*args, **kwds):
'''Like dict.update() but subtracts counts instead of replacing them.
Counts can be reduced below zero. Both the inputs and outputs are
allowed to contain zero and negative counts.
@@ -552,6 +572,14 @@
-1
'''
+ if not args:
+ raise TypeError("descriptor 'subtract' of 'Counter' object "
+ "needs an argument")
+ self = args[0]
+ args = args[1:]
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ iterable = args[0] if args else None
if iterable is not None:
self_get = self.get
if isinstance(iterable, Mapping):
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index 4a411bc..bbd06b9 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -187,7 +187,7 @@
def prf(msg, inner=inner, outer=outer):
# PBKDF2_HMAC uses the password as key. We can re-use the same
- # digest objects and and just update copies to skip initialization.
+ # digest objects and just update copies to skip initialization.
icpy = inner.copy()
ocpy = outer.copy()
icpy.update(msg)
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 7c27906..1224989 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -1070,18 +1070,22 @@
kwds["buffering"] = True;
response = self.response_class(*args, **kwds)
- response.begin()
- assert response.will_close != _UNKNOWN
- self.__state = _CS_IDLE
+ try:
+ response.begin()
+ assert response.will_close != _UNKNOWN
+ self.__state = _CS_IDLE
- if response.will_close:
- # this effectively passes the connection to the response
- self.close()
- else:
- # remember this, so we can tell when it is complete
- self.__response = response
+ if response.will_close:
+ # this effectively passes the connection to the response
+ self.close()
+ else:
+ # remember this, so we can tell when it is complete
+ self.__response = response
- return response
+ return response
+ except:
+ response.close()
+ raise
class HTTP:
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index dfa1f8e..0647d1e 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -17,7 +17,8 @@
of all available modules.
Run "pydoc -p <port>" to start an HTTP server on a given port on the
-local machine to generate documentation web pages.
+local machine to generate documentation web pages. Port number 0 can be
+used to get an arbitrary unused port.
For platforms without a command line, "pydoc -g" starts the HTTP server
and also pops up a little window for controlling it.
@@ -1445,7 +1446,13 @@
getchar = lambda: sys.stdin.readline()[:-1][:1]
try:
- r = inc = os.environ.get('LINES', 25) - 1
+ try:
+ h = int(os.environ.get('LINES', 0))
+ except ValueError:
+ h = 0
+ if h <= 1:
+ h = 25
+ r = inc = h - 1
sys.stdout.write(join(lines[:inc], '\n') + '\n')
while lines[r:]:
sys.stdout.write('-- more --')
@@ -2098,7 +2105,6 @@
def __init__(self, port, callback):
host = 'localhost'
self.address = (host, port)
- self.url = 'http://%s:%d/' % (host, port)
self.callback = callback
self.base.__init__(self, self.address, self.handler)
@@ -2111,6 +2117,7 @@
def server_activate(self):
self.base.server_activate(self)
+ self.url = 'http://%s:%d/' % (self.address[0], self.server_port)
if self.callback: self.callback(self)
DocServer.base = BaseHTTPServer.HTTPServer
@@ -2384,7 +2391,8 @@
Search for a keyword in the synopsis lines of all available modules.
%s -p <port>
- Start an HTTP server on the given port on the local machine.
+ Start an HTTP server on the given port on the local machine. Port
+ number 0 can be used to get an arbitrary unused port.
%s -g
Pop up a graphical interface for finding and serving documentation.
diff --git a/Lib/shutil.py b/Lib/shutil.py
index e12f791..e78a575 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -362,7 +362,7 @@
archive_name = base_name + '.tar' + compress_ext.get(compress, '')
archive_dir = os.path.dirname(archive_name)
- if not os.path.exists(archive_dir):
+ if archive_dir and not os.path.exists(archive_dir):
if logger is not None:
logger.info("creating %s", archive_dir)
if not dry_run:
@@ -426,7 +426,7 @@
zip_filename = base_name + ".zip"
archive_dir = os.path.dirname(base_name)
- if not os.path.exists(archive_dir):
+ if archive_dir and not os.path.exists(archive_dir):
if logger is not None:
logger.info("creating %s", archive_dir)
if not dry_run:
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 20e0667..cd27227 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -905,6 +905,28 @@
self.assertEqual(c.setdefault('e', 5), 5)
self.assertEqual(c['e'], 5)
+ def test_init(self):
+ self.assertEqual(list(Counter(self=42).items()), [('self', 42)])
+ self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)])
+ self.assertEqual(list(Counter(iterable=None).items()), [('iterable', None)])
+ self.assertRaises(TypeError, Counter, 42)
+ self.assertRaises(TypeError, Counter, (), ())
+ self.assertRaises(TypeError, Counter.__init__)
+
+ def test_update(self):
+ c = Counter()
+ c.update(self=42)
+ self.assertEqual(list(c.items()), [('self', 42)])
+ c = Counter()
+ c.update(iterable=42)
+ self.assertEqual(list(c.items()), [('iterable', 42)])
+ c = Counter()
+ c.update(iterable=None)
+ self.assertEqual(list(c.items()), [('iterable', None)])
+ self.assertRaises(TypeError, Counter().update, 42)
+ self.assertRaises(TypeError, Counter().update, {}, {})
+ self.assertRaises(TypeError, Counter.update)
+
def test_copying(self):
# Check that counters are copyable, deepcopyable, picklable, and
#have a repr/eval round-trip
@@ -1006,6 +1028,16 @@
c.subtract('aaaabbcce')
self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
+ c = Counter()
+ c.subtract(self=42)
+ self.assertEqual(list(c.items()), [('self', -42)])
+ c = Counter()
+ c.subtract(iterable=42)
+ self.assertEqual(list(c.items()), [('iterable', -42)])
+ self.assertRaises(TypeError, Counter().subtract, 42)
+ self.assertRaises(TypeError, Counter().subtract, {}, {})
+ self.assertRaises(TypeError, Counter.subtract)
+
class TestOrderedDict(unittest.TestCase):
def test_init(self):
@@ -1019,8 +1051,11 @@
c=3, e=5).items()), pairs) # mixed input
# make sure no positional args conflict with possible kwdargs
- self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__']).args,
- ['self'])
+ self.assertEqual(list(OrderedDict(self=42).items()), [('self', 42)])
+ self.assertEqual(list(OrderedDict(other=42).items()), [('other', 42)])
+ self.assertRaises(TypeError, OrderedDict, 42)
+ self.assertRaises(TypeError, OrderedDict, (), ())
+ self.assertRaises(TypeError, OrderedDict.__init__)
# Make sure that direct calls to __init__ do not clear previous contents
d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
@@ -1065,6 +1100,10 @@
self.assertEqual(list(d.items()),
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
+ self.assertRaises(TypeError, OrderedDict().update, 42)
+ self.assertRaises(TypeError, OrderedDict().update, (), ())
+ self.assertRaises(TypeError, OrderedDict.update)
+
def test_abc(self):
self.assertIsInstance(OrderedDict(), MutableMapping)
self.assertTrue(issubclass(OrderedDict, MutableMapping))
diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py
index 80d1803..a1a1fcf 100644
--- a/Lib/test/test_docxmlrpc.py
+++ b/Lib/test/test_docxmlrpc.py
@@ -10,7 +10,7 @@
PORT = None
def make_request_and_skipIf(condition, reason):
- # If we skip the test, we have to make a request because the
+ # If we skip the test, we have to make a request because
# the server created in setUp blocks expecting one to come in.
if not condition:
return lambda func: func
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 3e7a57e..a73a28b 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -25,6 +25,7 @@
self.text = text
self.fileclass = fileclass
self.data = ''
+ self.file_closed = False
self.host = host
self.port = port
@@ -34,7 +35,13 @@
def makefile(self, mode, bufsize=None):
if mode != 'r' and mode != 'rb':
raise httplib.UnimplementedFileMode()
- return self.fileclass(self.text)
+ # keep the file around so we can check how much was read from it
+ self.file = self.fileclass(self.text)
+ self.file.close = self.file_close #nerf close ()
+ return self.file
+
+ def file_close(self):
+ self.file_closed = True
def close(self):
pass
@@ -433,6 +440,22 @@
self.assertEqual(resp.read(), '')
self.assertTrue(resp.isclosed())
+ def test_error_leak(self):
+ # Test that the socket is not leaked if getresponse() fails
+ conn = httplib.HTTPConnection('example.com')
+ response = []
+ class Response(httplib.HTTPResponse):
+ def __init__(self, *pos, **kw):
+ response.append(self) # Avoid garbage collector closing the socket
+ httplib.HTTPResponse.__init__(self, *pos, **kw)
+ conn.response_class = Response
+ conn.sock = FakeSocket('') # Emulate server dropping connection
+ conn.request('GET', '/')
+ self.assertRaises(httplib.BadStatusLine, conn.getresponse)
+ self.assertTrue(response)
+ #self.assertTrue(response[0].closed)
+ self.assertTrue(conn.sock.file_closed)
+
class OfflineTest(TestCase):
def test_responses(self):
self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py
index e009952..588043e 100644
--- a/Lib/test/test_readline.py
+++ b/Lib/test/test_readline.py
@@ -15,9 +15,9 @@
That's why the tests cover only a small subset of the interface.
"""
- @unittest.skipIf(not hasattr(readline, 'clear_history'),
- "The history update test cannot be run because the "
- "clear_history method is not available.")
+ @unittest.skipUnless(hasattr(readline, "clear_history"),
+ "The history update test cannot be run because the "
+ "clear_history method is not available.")
def testHistoryUpdates(self):
readline.clear_history()
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 0e81145..bcabe75 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -581,6 +581,29 @@
finally:
unregister_archive_format('xxx')
+ def test_make_tarfile_in_curdir(self):
+ # Issue #21280
+ root_dir = self.mkdtemp()
+ saved_dir = os.getcwd()
+ try:
+ os.chdir(root_dir)
+ self.assertEqual(make_archive('test', 'tar'), 'test.tar')
+ self.assertTrue(os.path.isfile('test.tar'))
+ finally:
+ os.chdir(saved_dir)
+
+ @unittest.skipUnless(zlib, "Requires zlib")
+ def test_make_zipfile_in_curdir(self):
+ # Issue #21280
+ root_dir = self.mkdtemp()
+ saved_dir = os.getcwd()
+ try:
+ os.chdir(root_dir)
+ self.assertEqual(make_archive('test', 'zip'), 'test.zip')
+ self.assertTrue(os.path.isfile('test.zip'))
+ finally:
+ os.chdir(saved_dir)
+
def test_register_archive_format(self):
self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
diff --git a/Misc/ACKS b/Misc/ACKS
index d44240f..5372ee7 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1010,6 +1010,7 @@
Juan David Ibáñez Palomar
Jan Palus
Yongzhi Pan
+Martin Panter
Mathias Panzenböck
M. Papillon
Peter Parente
diff --git a/Misc/NEWS b/Misc/NEWS
index 01f23ef..6b32e7f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,43 @@
Python News
+++++++++++
+What's New in Python 2.7.10?
+============================
+
+*Release date: XXXX-XX-XX*
+
+Core and Builtins
+-----------------
+
+Library
+-------
+
+- Issue #21032. Fixed socket leak if HTTPConnection.getresponse() fails.
+ Original patch by Martin Panter.
+
+- Issue #22609: Constructors and update methods of mapping classes in the
+ collections module now accept the self keyword argument.
+
+Documentation
+-------------
+
+- Issue #21514: The documentation of the json module now refers to new JSON RFC
+ 7159 instead of obsoleted RFC 4627.
+
+Tools/Demos
+-----------
+
+- Issue #22314: pydoc now works when the LINES environment variable is set.
+
+- Issue #18905: "pydoc -p 0" now outputs actually used port. Based on patch by
+ Wieland Hoffmann.
+
+Tests
+-----
+
+- Issue #22943: bsddb tests are locale independend now.
+
+
What's New in Python 2.7.9?
===========================
@@ -85,7 +122,7 @@
- Issue #22776: Brought excluded code into the scope of a try block in
SysLogHandler.emit().
-
+
- Issue #17381: Fixed ranges handling in case-insensitive regular expressions.
- Issue #19329: Optimized compiling charsets in regular expressions.
@@ -280,6 +317,23 @@
IDLE
----
+- 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 #22221: IDLE now ignores the source encoding declaration on the second
line if the first line contains anything except a comment.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 4d0c8fa..379423c 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -8496,7 +8496,7 @@
/* This code is used to ensure that the tables of configuration value names
- * are in sorted order as required by conv_confname(), and also to build the
+ * are in sorted order as required by conv_confname(), and also to build
* the exported dictionaries that are used to publish information about the
* names available on the host platform.
*
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d0ac3ac..619ac9b 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -4798,7 +4798,7 @@
"%s.__new__(%s) is not safe, use %s.__new__()",
type->tp_name,
subtype->tp_name,
- staticbase == NULL ? "?" : staticbase->tp_name);
+ staticbase->tp_name);
return NULL;
}
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index 859af43..ae6ab9c 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -324,7 +324,7 @@
On overflow (e.g., when trying to convert '1e500' on an IEEE 754 machine),
if overflow_exception is NULL then +-Py_HUGE_VAL is returned, and no Python
- exception is raised. Otherwise, overflow_exception should point to a
+ exception is raised. Otherwise, overflow_exception should point to
a Python exception, this exception will be raised, -1.0 will be returned,
and *endptr will point just past the end of the converted value.
diff --git a/Python/random.c b/Python/random.c
index 0776bfc..1e5776d 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -274,7 +274,7 @@
}
/* Fill buffer with size pseudo-random bytes from the operating system random
- number generator (RNG). It is suitable for for most cryptographic purposes
+ number generator (RNG). It is suitable for most cryptographic purposes
except long living private keys for asymmetric encryption.
Return 0 on success, raise an exception and return -1 on error. */
diff --git a/Python/thread.c b/Python/thread.c
index dd333e8..c54670d 100644
--- a/Python/thread.c
+++ b/Python/thread.c
@@ -271,7 +271,7 @@
if (p->id == id && p->key == key)
goto Done;
/* Sanity check. These states should never happen but if
- * they do we must abort. Otherwise we'll end up spinning in
+ * they do we must abort. Otherwise we'll end up spinning
* in a tight loop with the lock held. A similar check is done
* in pystate.c tstate_delete_common(). */
if (p == prev_p)
diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py
index 09105a2..074c9a5 100644
--- a/Tools/msi/msi.py
+++ b/Tools/msi/msi.py
@@ -912,9 +912,9 @@
("Tk", "tk-8*", "license.terms"),
("Tix", "tix-*", "license.terms")):
out.write("\nThis copy of Python includes a copy of %s, which is licensed under the following terms:\n\n" % name)
- dirs = glob.glob(srcdir+"/../"+pat)
+ dirs = glob.glob(srcdir+"/externals/"+pat)
if not dirs:
- raise ValueError, "Could not find "+srcdir+"/../"+pat
+ raise ValueError, "Could not find "+srcdir+"/externals/"+pat
if len(dirs) > 2:
raise ValueError, "Multiple copies of "+pat
dir = dirs[0]
@@ -1019,6 +1019,8 @@
# Add additional files
dirs[dir]=lib
lib.glob("*.txt")
+ lib.glob("*.whl")
+ lib.glob("*.0")
if dir=='site-packages':
lib.add_file("README.txt", src="README")
continue
@@ -1043,6 +1045,7 @@
lib.add_file("check_soundcard.vbs")
lib.add_file("empty.vbs")
lib.add_file("Sine-1000Hz-300ms.aif")
+ lib.add_file("revocation.crl")
lib.glob("*.uue")
lib.glob("*.pem")
lib.glob("*.pck")
@@ -1117,7 +1120,7 @@
lib.start_component("TkDLLs", tcltk)
lib.add_file("_tkinter.pyd")
dlls.append("_tkinter.pyd")
- tcldir = os.path.normpath(srcdir+("/../tcltk%s/bin" % tclsuffix))
+ tcldir = os.path.normpath(srcdir+("/externals/tcltk%s/bin" % tclsuffix))
for f in glob.glob1(tcldir, "*.dll"):
lib.add_file(f, src=os.path.join(tcldir, f))
# check whether there are any unknown extensions
@@ -1141,7 +1144,7 @@
lib.add_file('libpython%s%s.a' % (major, minor))
if have_tcl:
# Add Tcl/Tk
- tcldirs = [(root, '../tcltk%s/lib' % tclsuffix, 'tcl')]
+ tcldirs = [(root, 'externals/tcltk%s/lib' % tclsuffix, 'tcl')]
tcltk.set_current()
while tcldirs:
parent, phys, dir = tcldirs.pop()
diff --git a/Tools/msi/uuids.py b/Tools/msi/uuids.py
index 2e4dc31..d8c426a 100644
--- a/Tools/msi/uuids.py
+++ b/Tools/msi/uuids.py
@@ -66,4 +66,6 @@
'2.7.7121':'{5E0D187D-238B-4e96-9C75-C4CF141F5385}', # 2.7.7rc1
'2.7.7150':'{049CA433-77A0-4e48-AC76-180A282C4E10}', # 2.7.7
'2.7.8150':'{61121B12-88BD-4261-A6EE-AB32610A56DD}', # 2.7.8
+ '2.7.9121':'{AAB1E8FF-6D00-4409-8F13-BE365AB92FFE}', # 2.7.9rc1
+ '2.7.9150':'{79F081BF-7454-43DB-BD8F-9EE596813232}', # 2.7.9
}