Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Checkins

cpython (merge default -> default): merge

 

 

Python checkins RSS feed   Index | Next | Previous | View Threaded


python-checkins at python

Jun 4, 2013, 2:36 PM

Post #1 of 14 (54 views)
Permalink
cpython (merge default -> default): merge

http://hg.python.org/cpython/rev/b9af38d33606
changeset: 84024:b9af38d33606
parent: 84023:9e833c1edeb6
parent: 84022:41a2cbe23349
user: Brett Cannon <brett [at] python>
date: Tue Jun 04 17:36:07 2013 -0400
summary:
merge

files:
Misc/NEWS | 3 ++
Modules/_bz2module.c | 12 +++-----
Modules/_cursesmodule.c | 14 +++------
Modules/_multiprocessing/multiprocessing.h | 9 ------
Modules/md5module.c | 6 +---
Modules/sha1module.c | 6 +---
Modules/socketmodule.c | 7 +---
Objects/floatobject.c | 13 ++------
Objects/frameobject.c | 11 ++-----
Objects/iterobject.c | 2 +-
Objects/longobject.c | 13 ++------
11 files changed, 29 insertions(+), 67 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,9 @@
- Tweak the exception message when the magic number or size value in a bytecode
file is truncated.

+- Issue #17932: Fix an integer overflow issue on Windows 64-bit in iterators:
+ change the C type of seqiterobject.it_index from long to Py_ssize_t.
+
- Issue #18065: Don't set __path__ to the package name for frozen packages.

- Issue #18088: When reloading a module, unconditionally reset all relevant
diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c
--- a/Modules/_bz2module.c
+++ b/Modules/_bz2module.c
@@ -36,8 +36,6 @@
#define RELEASE_LOCK(obj)
#endif

-#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
-

typedef struct {
PyObject_HEAD
@@ -157,7 +155,7 @@
/* On a 64-bit system, len might not fit in avail_in (an unsigned int).
Do compression in chunks of no more than UINT_MAX bytes each. */
if (c->bzs.avail_in == 0 && len > 0) {
- c->bzs.avail_in = MIN(len, UINT_MAX);
+ c->bzs.avail_in = Py_MIN(len, UINT_MAX);
len -= c->bzs.avail_in;
}

@@ -173,7 +171,7 @@
c->bzs.next_out = PyBytes_AS_STRING(result) + data_size;
buffer_left = PyBytes_GET_SIZE(result) - data_size;
}
- c->bzs.avail_out = MIN(buffer_left, UINT_MAX);
+ c->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX);
}

Py_BEGIN_ALLOW_THREADS
@@ -370,7 +368,7 @@
d->bzs.next_in = data;
/* On a 64-bit system, len might not fit in avail_in (an unsigned int).
Do decompression in chunks of no more than UINT_MAX bytes each. */
- d->bzs.avail_in = MIN(len, UINT_MAX);
+ d->bzs.avail_in = Py_MIN(len, UINT_MAX);
len -= d->bzs.avail_in;
d->bzs.next_out = PyBytes_AS_STRING(result);
d->bzs.avail_out = PyBytes_GET_SIZE(result);
@@ -399,7 +397,7 @@
if (d->bzs.avail_in == 0) {
if (len == 0)
break;
- d->bzs.avail_in = MIN(len, UINT_MAX);
+ d->bzs.avail_in = Py_MIN(len, UINT_MAX);
len -= d->bzs.avail_in;
}
if (d->bzs.avail_out == 0) {
@@ -410,7 +408,7 @@
d->bzs.next_out = PyBytes_AS_STRING(result) + data_size;
buffer_left = PyBytes_GET_SIZE(result) - data_size;
}
- d->bzs.avail_out = MIN(buffer_left, UINT_MAX);
+ d->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX);
}
}
if (data_size != PyBytes_GET_SIZE(result))
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -168,10 +168,6 @@
"must call start_color() first"); \
return 0; }

-#ifndef MIN
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-#endif
-
/* Utility Functions */

/*
@@ -1212,7 +1208,7 @@
if (!PyArg_ParseTuple(args,"i;n", &n))
return NULL;
Py_BEGIN_ALLOW_THREADS
- rtn2 = wgetnstr(self->win,rtn,MIN(n, 1023));
+ rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023));
Py_END_ALLOW_THREADS
break;
case 2:
@@ -1232,11 +1228,11 @@
#ifdef STRICT_SYSV_CURSES
Py_BEGIN_ALLOW_THREADS
rtn2 = wmove(self->win,y,x)==ERR ? ERR :
- wgetnstr(self->win, rtn, MIN(n, 1023));
+ wgetnstr(self->win, rtn, Py_MIN(n, 1023));
Py_END_ALLOW_THREADS
#else
Py_BEGIN_ALLOW_THREADS
- rtn2 = mvwgetnstr(self->win, y, x, rtn, MIN(n, 1023));
+ rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023));
Py_END_ALLOW_THREADS
#endif
break;
@@ -1374,7 +1370,7 @@
case 1:
if (!PyArg_ParseTuple(args,"i;n", &n))
return NULL;
- rtn2 = winnstr(self->win,rtn,MIN(n,1023));
+ rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023));
break;
case 2:
if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x))
@@ -1384,7 +1380,7 @@
case 3:
if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n))
return NULL;
- rtn2 = mvwinnstr(self->win, y, x, rtn, MIN(n,1023));
+ rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023));
break;
default:
PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments");
diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h
--- a/Modules/_multiprocessing/multiprocessing.h
+++ b/Modules/_multiprocessing/multiprocessing.h
@@ -99,13 +99,4 @@

extern PyTypeObject _PyMp_SemLockType;

-/*
- * Miscellaneous
- */
-
-#ifndef MIN
-# define MIN(x, y) ((x) < (y) ? x : y)
-# define MAX(x, y) ((x) > (y) ? x : y)
-#endif
-
#endif /* MULTIPROCESSING_H */
diff --git a/Modules/md5module.c b/Modules/md5module.c
--- a/Modules/md5module.c
+++ b/Modules/md5module.c
@@ -91,10 +91,6 @@
(y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
(y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }

-#ifndef MIN
- #define MIN(x, y) ( ((x)<(y))?(x):(y) )
-#endif
-

/* MD5 macros */

@@ -244,7 +240,7 @@
in += MD5_BLOCKSIZE;
inlen -= MD5_BLOCKSIZE;
} else {
- n = MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen));
+ n = Py_MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen));
memcpy(md5->buf + md5->curlen, in, (size_t)n);
md5->curlen += (MD5_INT32)n;
in += n;
diff --git a/Modules/sha1module.c b/Modules/sha1module.c
--- a/Modules/sha1module.c
+++ b/Modules/sha1module.c
@@ -92,10 +92,6 @@
(y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
(y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }

-#ifndef MIN
- #define MIN(x, y) ( ((x)<(y))?(x):(y) )
-#endif
-

/* SHA1 macros */

@@ -220,7 +216,7 @@
in += SHA1_BLOCKSIZE;
inlen -= SHA1_BLOCKSIZE;
} else {
- n = MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen));
+ n = Py_MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen));
memcpy(sha1->buf + sha1->curlen, in, (size_t)n);
sha1->curlen += (SHA1_INT32)n;
in += n;
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -95,9 +95,6 @@
#include "Python.h"
#include "structmember.h"

-#undef MAX
-#define MAX(x, y) ((x) < (y) ? (y) : (x))
-
/* Socket object documentation */
PyDoc_STRVAR(sock_doc,
"socket([family[, type[, proto]]]) -> socket object\n\
@@ -4819,7 +4816,7 @@
char* ip;
int retval;
#ifdef ENABLE_IPV6
- char packed[MAX(sizeof(struct in_addr), sizeof(struct in6_addr))];
+ char packed[Py_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))];
#else
char packed[sizeof(struct in_addr)];
#endif
@@ -4870,7 +4867,7 @@
int len;
const char* retval;
#ifdef ENABLE_IPV6
- char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1];
+ char ip[Py_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1];
#else
char ip[INET_ADDRSTRLEN + 1];
#endif
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -9,11 +9,6 @@
#include <ctype.h>
#include <float.h>

-#undef MAX
-#undef MIN
-#define MAX(x, y) ((x) < (y) ? (y) : (x))
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-

/* Special free list
free_list is a singly-linked list of available PyFloatObjects, linked
@@ -1131,7 +1126,7 @@
}

m = frexp(fabs(x), &e);
- shift = 1 - MAX(DBL_MIN_EXP - e, 0);
+ shift = 1 - Py_MAX(DBL_MIN_EXP - e, 0);
m = ldexp(m, shift);
e -= shift;

@@ -1285,8 +1280,8 @@
fdigits = coeff_end - s_store;
if (ndigits == 0)
goto parse_error;
- if (ndigits > MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2,
- LONG_MAX/2 + 1 - DBL_MAX_EXP)/4)
+ if (ndigits > Py_MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2,
+ LONG_MAX/2 + 1 - DBL_MAX_EXP)/4)
goto insane_length_error;

/* [p <exponent>] */
@@ -1342,7 +1337,7 @@

/* lsb = exponent of least significant bit of the *rounded* value.
This is top_exp - DBL_MANT_DIG unless result is subnormal. */
- lsb = MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG;
+ lsb = Py_MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG;

x = 0.0;
if (exp >= lsb) {
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -7,11 +7,6 @@
#include "opcode.h"
#include "structmember.h"

-#undef MIN
-#undef MAX
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-
#define OFF(x) offsetof(PyFrameObject, x)

static PyMemberDef frame_memberlist[] = {
@@ -160,8 +155,8 @@

/* We're now ready to look at the bytecode. */
PyBytes_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len);
- min_addr = MIN(new_lasti, f->f_lasti);
- max_addr = MAX(new_lasti, f->f_lasti);
+ min_addr = Py_MIN(new_lasti, f->f_lasti);
+ max_addr = Py_MAX(new_lasti, f->f_lasti);

/* You can't jump onto a line with an 'except' statement on it -
* they expect to have an exception on the top of the stack, which
@@ -293,7 +288,7 @@
break;
}

- min_delta_iblock = MIN(min_delta_iblock, delta_iblock);
+ min_delta_iblock = Py_MIN(min_delta_iblock, delta_iblock);

if (op >= HAVE_ARGUMENT) {
addr += 2;
diff --git a/Objects/iterobject.c b/Objects/iterobject.c
--- a/Objects/iterobject.c
+++ b/Objects/iterobject.c
@@ -4,7 +4,7 @@

typedef struct {
PyObject_HEAD
- long it_index;
+ Py_ssize_t it_index;
PyObject *it_seq; /* Set to NULL when iterator is exhausted */
} seqiterobject;

diff --git a/Objects/longobject.c b/Objects/longobject.c
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -89,11 +89,6 @@
*/
#define FIVEARY_CUTOFF 8

-#undef MIN
-#undef MAX
-#define MAX(x, y) ((x) < (y) ? (y) : (x))
-#define MIN(x, y) ((x) > (y) ? (y) : (x))
-
#define SIGCHECK(PyTryBlock) \
do { \
if (PyErr_CheckSignals()) PyTryBlock \
@@ -3029,7 +3024,7 @@
Py_ssize_t size_lo, size_hi;
const Py_ssize_t size_n = ABS(Py_SIZE(n));

- size_lo = MIN(size_n, size);
+ size_lo = Py_MIN(size_n, size);
size_hi = size_n - size_lo;

if ((hi = _PyLong_New(size_hi)) == NULL)
@@ -3300,7 +3295,7 @@
nbdone = 0;
while (bsize > 0) {
PyLongObject *product;
- const Py_ssize_t nbtouse = MIN(bsize, asize);
+ const Py_ssize_t nbtouse = Py_MIN(bsize, asize);

/* Multiply the next slice of b by a. */
memcpy(bslice->ob_digit, b->ob_digit + nbdone,
@@ -3591,7 +3586,7 @@
goto underflow_or_zero;

/* Choose value for shift; see comments for step 1 above. */
- shift = MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2;
+ shift = Py_MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2;

inexact = 0;

@@ -3662,7 +3657,7 @@
x_bits = (x_size-1)*PyLong_SHIFT+bits_in_digit(x->ob_digit[x_size-1]);

/* The number of extra bits that have to be rounded away. */
- extra_bits = MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG;
+ extra_bits = Py_MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG;
assert(extra_bits == 2 || extra_bits == 3);

/* Round by directly modifying the low digit of x. */

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 14, 2013, 3:33 PM

Post #2 of 14 (44 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/8ee9f50b0538
changeset: 84126:8ee9f50b0538
parent: 84125:46ef1d2af352
parent: 84124:bfd53dcb02ff
user: Brett Cannon <brett [at] python>
date: Fri Jun 14 18:33:21 2013 -0400
summary:
merge

files:
Doc/library/filecmp.rst | 13 +++++++++++++
Lib/filecmp.py | 11 ++++++++---
Lib/test/test_filecmp.py | 7 +++++++
Misc/ACKS | 1 +
Misc/NEWS | 3 +++
5 files changed, 32 insertions(+), 3 deletions(-)


diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst
--- a/Doc/library/filecmp.rst
+++ b/Doc/library/filecmp.rst
@@ -27,6 +27,10 @@
Note that no external programs are called from this function, giving it
portability and efficiency.

+ This function uses a cache for past comparisons and the results,
+ with a cache invalidation mechanism relying on stale signatures
+ or by explicitly calling :func:`clear_cache`.
+

.. function:: cmpfiles(dir1, dir2, common, shallow=True)

@@ -48,6 +52,15 @@
one of the three returned lists.


+.. function:: clear_cache()
+
+ .. versionadded:: 3.4
+
+ Clear the filecmp cache. This may be useful if a file is compared so quickly
+ after it is modified that it is within the mtime resolution of
+ the underlying filesystem.
+
+
.. _dircmp-objects:

The :class:`dircmp` class
diff --git a/Lib/filecmp.py b/Lib/filecmp.py
--- a/Lib/filecmp.py
+++ b/Lib/filecmp.py
@@ -6,6 +6,7 @@
Functions:
cmp(f1, f2, shallow=True) -> int
cmpfiles(a, b, common) -> ([], [], [])
+ clear_cache()

"""

@@ -13,7 +14,7 @@
import stat
from itertools import filterfalse

-__all__ = ['cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES']
+__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES']

_cache = {}
BUFSIZE = 8*1024
@@ -21,6 +22,9 @@
DEFAULT_IGNORES = [
'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__']

+def clear_cache():
+ """Clear the filecmp cache."""
+ _cache.clear()

def cmp(f1, f2, shallow=True):
"""Compare two files.
@@ -39,7 +43,8 @@
True if the files are the same, False otherwise.

This function uses a cache for past comparisons and the results,
- with a cache invalidation mechanism relying on stale signatures.
+ with a cache invalidation mechanism relying on stale signatures
+ or by explicitly calling clear_cache().

"""

@@ -56,7 +61,7 @@
if outcome is None:
outcome = _do_cmp(f1, f2)
if len(_cache) > 100: # limit the maximum size of the cache
- _cache.clear()
+ clear_cache()
_cache[f1, f2, s1, s2] = outcome
return outcome

diff --git a/Lib/test/test_filecmp.py b/Lib/test/test_filecmp.py
--- a/Lib/test/test_filecmp.py
+++ b/Lib/test/test_filecmp.py
@@ -39,6 +39,13 @@
self.assertFalse(filecmp.cmp(self.name, self.dir),
"File and directory compare as equal")

+ def test_cache_clear(self):
+ first_compare = filecmp.cmp(self.name, self.name_same, shallow=False)
+ second_compare = filecmp.cmp(self.name, self.name_diff, shallow=False)
+ filecmp.clear_cache()
+ self.assertTrue(len(filecmp._cache) == 0,
+ "Cache not cleared after calling clear_cache")
+
class DirCompareTestCase(unittest.TestCase):
def setUp(self):
tmpdir = tempfile.gettempdir()
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -738,6 +738,7 @@
Christopher Tur Lesniewski-Laas
Alain Leufroy
Mark Levinson
+Mark Levitt
William Lewis
Akira Li
Xuanji Li
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -123,6 +123,9 @@
Library
-------

+- Issue #18149: Add filecmp.clear_cache() to manually clear the filecmp cache.
+ Patch by Mark Levitt
+
- Issue #18193: Add importlib.reload().

- Issue #18157: Stop using imp.load_module() in pydoc.

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 15, 2013, 10:38 AM

Post #3 of 14 (44 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/acca81736b69
changeset: 84146:acca81736b69
parent: 84145:48b79f11571e
parent: 84143:5d57f2deffba
user: Brett Cannon <brett [at] python>
date: Sat Jun 15 13:38:07 2013 -0400
summary:
merge

files:
Doc/whatsnew/3.4.rst | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)


diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -222,8 +222,8 @@
------------------------------------------------

* :meth:`difflib.SequenceMatcher.isbjunk` and
- :meth:`difflib.SequenceMatcher.isbpopulur`: use ``x in sm.bjunk`` and
- ``x in sm.bpopular``, where sm is a :class:`~difflib.SequenceMatcher` object.
+ :meth:`difflib.SequenceMatcher.isbpopular` were removed: use ``x in sm.bjunk`` and
+ ``x in sm.bpopular``, where *sm* is a :class:`~difflib.SequenceMatcher` object.

* :func:`importlib.util.module_for_loader` is pending deprecation. Using
:func:`importlib.util.module_to_load` and

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 16, 2013, 10:14 AM

Post #4 of 14 (44 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/7ca3e27e3b1a
changeset: 84168:7ca3e27e3b1a
parent: 84167:1b8f08c4efd5
parent: 84166:81648329b37a
user: Brett Cannon <brett [at] python>
date: Sun Jun 16 13:14:06 2013 -0400
summary:
merge

files:
Doc/library/codecs.rst | 6 +++-
Doc/library/functions.rst | 40 ++++++++++++++++++++------
Lib/codecs.py | 1 +
Modules/_io/_iomodule.c | 4 +-
Modules/_io/textio.c | 5 ++-
5 files changed, 41 insertions(+), 15 deletions(-)


diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst
--- a/Doc/library/codecs.rst
+++ b/Doc/library/codecs.rst
@@ -78,7 +78,11 @@
reference (for encoding only)
* ``'backslashreplace'``: replace with backslashed escape sequences (for
encoding only)
- * ``'surrogateescape'``: replace with surrogate U+DCxx, see :pep:`383`
+ * ``'surrogateescape'``: on decoding, replace with code points in the Unicode
+ Private Use Area ranging from U+DC80 to U+DCFF. These private code
+ points will then be turned back into the same bytes when the
+ ``surrogateescape`` error handler is used when encoding the data.
+ (See :pep:`383` for more.)

as well as any other error handling name defined via :func:`register_error`.

diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -905,16 +905,36 @@
the list of supported encodings.

*errors* is an optional string that specifies how encoding and decoding
- errors are to be handled--this cannot be used in binary mode. Pass
- ``'strict'`` to raise a :exc:`ValueError` exception if there is an encoding
- error (the default of ``None`` has the same effect), or pass ``'ignore'`` to
- ignore errors. (Note that ignoring encoding errors can lead to data loss.)
- ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted
- where there is malformed data. When writing, ``'xmlcharrefreplace'``
- (replace with the appropriate XML character reference) or
- ``'backslashreplace'`` (replace with backslashed escape sequences) can be
- used. Any other error handling name that has been registered with
- :func:`codecs.register_error` is also valid.
+ errors are to be handled--this cannot be used in binary mode.
+ A variety of standard error handlers are available, though any
+ error handling name that has been registered with
+ :func:`codecs.register_error` is also valid. The standard names
+ are:
+
+ * ``'strict'`` to raise a :exc:`ValueError` exception if there is
+ an encoding error. The default value of ``None`` has the same
+ effect.
+
+ * ``'ignore'`` ignores errors. Note that ignoring encoding errors
+ can lead to data loss.
+
+ * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted
+ where there is malformed data.
+
+ * ``'surrogateescape'`` will represent any incorrect bytes as code
+ points in the Unicode Private Use Area ranging from U+DC80 to
+ U+DCFF. These private code points will then be turned back into
+ the same bytes when the ``surrogateescape`` error handler is used
+ when writing data. This is useful for processing files in an
+ unknown encoding.
+
+ * ``'xmlcharrefreplace'`` is only supported when writing to a file.
+ Characters not supported by the encoding are replaced with the
+ appropriate XML character reference ``&#nnn;``.
+
+ * ``'backslashreplace'`` (also only supported when writing)
+ replaces unsupported characters with Python's backslashed escape
+ sequences.

.. index::
single: universal newlines; open() built-in function
diff --git a/Lib/codecs.py b/Lib/codecs.py
--- a/Lib/codecs.py
+++ b/Lib/codecs.py
@@ -105,6 +105,7 @@
Python will use the official U+FFFD REPLACEMENT
CHARACTER for the builtin Unicode codecs on
decoding and '?' on encoding.
+ 'surrogateescape' - replace with private codepoints U+DCnn.
'xmlcharrefreplace' - Replace with the appropriate XML
character reference (only for encoding).
'backslashreplace' - Replace with backslashed escape sequences
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -168,8 +168,8 @@
"'strict' to raise a ValueError exception if there is an encoding error\n"
"(the default of None has the same effect), or pass 'ignore' to ignore\n"
"errors. (Note that ignoring encoding errors can lead to data loss.)\n"
-"See the documentation for codecs.register for a list of the permitted\n"
-"encoding error strings.\n"
+"See the documentation for codecs.register or run 'help(codecs.Codec)'\n"
+"for a list of the permitted encoding error strings.\n"
"\n"
"newline controls how universal newlines works (it only applies to text\n"
"mode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\n"
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -642,8 +642,9 @@
"encoding gives the name of the encoding that the stream will be\n"
"decoded or encoded with. It defaults to locale.getpreferredencoding(False).\n"
"\n"
- "errors determines the strictness of encoding and decoding (see the\n"
- "codecs.register) and defaults to \"strict\".\n"
+ "errors determines the strictness of encoding and decoding (see\n"
+ "help(codecs.Codec) or the documentation for codecs.register) and\n"
+ "defaults to \"strict\".\n"
"\n"
"newline controls how line endings are handled. It can be None, '',\n"
"'\\n', '\\r', and '\\r\\n'. It works as follows:\n"

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 18, 2013, 5:51 PM

Post #5 of 14 (44 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/338be75f11c1
changeset: 84216:338be75f11c1
parent: 84215:ded443c603f0
parent: 84214:1638f28f3b2f
user: Brett Cannon <brett [at] python>
date: Tue Jun 18 20:51:42 2013 -0400
summary:
merge

files:
Misc/NEWS | 2 ++
Modules/_ctypes/libffi/src/powerpc/ffi.c | 4 +++-
Modules/socketmodule.c | 5 +++++
3 files changed, 10 insertions(+), 1 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -123,6 +123,8 @@
Library
-------

+- Issue #18259: Declare sethostname in socketmodule.c for AIX
+
- Issue #18147: Add diagnostic functions to ssl.SSLContext(). get_ca_list()
lists all loaded CA certificates and cert_store_stats() returns amount of
loaded X.509 certs, X.509 CA certs and CRLs.
diff --git a/Modules/_ctypes/libffi/src/powerpc/ffi.c b/Modules/_ctypes/libffi/src/powerpc/ffi.c
--- a/Modules/_ctypes/libffi/src/powerpc/ffi.c
+++ b/Modules/_ctypes/libffi/src/powerpc/ffi.c
@@ -146,12 +146,14 @@
} p_argv;
size_t struct_copy_size;
unsigned gprvalue;
+#ifndef __NO_FPRS__
+ double double_tmp;
+#endif

stacktop.c = (char *) stack + bytes;
gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
intarg_count = 0;
#ifndef __NO_FPRS__
- double double_tmp;
fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS;
fparg_count = 0;
copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c);
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -4066,6 +4066,11 @@
Py_buffer buf;
int res, flag = 0;

+#ifdef _AIX
+/* issue #18259, not declared in any useful header file */
+extern int sethostname(const char *, size_t);
+#endif
+
if (!PyArg_ParseTuple(args, "S:sethostname", &hnobj)) {
PyErr_Clear();
if (!PyArg_ParseTuple(args, "O&:sethostname",

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 23, 2013, 1:57 PM

Post #6 of 14 (42 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/153f7c0df033
changeset: 84297:153f7c0df033
parent: 84296:bc52faaa50e5
parent: 84293:627e3096340e
user: Christian Heimes <christian [at] cheimes>
date: Sun Jun 23 22:57:22 2013 +0200
summary:
merge

files:
Doc/library/smtplib.rst | 51 ++++++++++++++++------------
Lib/smtplib.py | 44 +++++++++++++-----------
2 files changed, 53 insertions(+), 42 deletions(-)


diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst
--- a/Doc/library/smtplib.rst
+++ b/Doc/library/smtplib.rst
@@ -24,17 +24,20 @@

A :class:`SMTP` instance encapsulates an SMTP connection. It has methods
that support a full repertoire of SMTP and ESMTP operations. If the optional
- host and port parameters are given, the SMTP :meth:`connect` method is called
- with those parameters during initialization. If the :meth:`connect` call
- returns anything other than a success code, an :exc:`SMTPConnectError` is
- raised. The optional *timeout* parameter specifies a timeout in seconds for
- blocking operations like the connection attempt (if not specified, the
- global default timeout setting will be used). The optional source_address
- parameter allows to bind to some specific source address in a machine with
- multiple network interfaces, and/or to some specific source TCP port. It
- takes a 2-tuple (host, port), for the socket to bind to as its source
- address before connecting. If omitted (or if host or port are ``''`` and/or
- 0 respectively) the OS default behavior will be used.
+ host and port parameters are given, the SMTP :meth:`connect` method is
+ called with those parameters during initialization. If specified,
+ *local_hostname* is used as the FQDN of the local host in the HELO/EHLO
+ command. Otherwise, the local hostname is found using
+ :func:`socket.getfqdn`. If the :meth:`connect` call returns anything other
+ than a success code, an :exc:`SMTPConnectError` is raised. The optional
+ *timeout* parameter specifies a timeout in seconds for blocking operations
+ like the connection attempt (if not specified, the global default timeout
+ setting will be used). The optional source_address parameter allows to bind
+ to some specific source address in a machine with multiple network
+ interfaces, and/or to some specific source TCP port. It takes a 2-tuple
+ (host, port), for the socket to bind to as its source address before
+ connecting. If omitted (or if host or port are ``''`` and/or 0 respectively)
+ the OS default behavior will be used.

For normal use, you should only require the initialization/connect,
:meth:`sendmail`, and :meth:`~smtplib.quit` methods.
@@ -57,17 +60,21 @@
.. versionchanged:: 3.3
source_address argument was added.

-.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout], context=None, source_address=None)
+.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, \
+ certfile=None [, timeout], context=None, \
+ source_address=None)

A :class:`SMTP_SSL` instance behaves exactly the same as instances of
:class:`SMTP`. :class:`SMTP_SSL` should be used for situations where SSL is
required from the beginning of the connection and using :meth:`starttls` is
not appropriate. If *host* is not specified, the local host is used. If
- *port* is zero, the standard SMTP-over-SSL port (465) is used. *keyfile*
- and *certfile* are also optional, and can contain a PEM formatted private key
- and certificate chain file for the SSL connection. *context* also optional, can contain
- a SSLContext, and is an alternative to keyfile and certfile; If it is specified both
- keyfile and certfile must be None. The optional *timeout*
+ *port* is zero, the standard SMTP-over-SSL port (465) is used. The optional
+ arguments *local_hostname* and *source_address* have the same meaning as
+ they do in the :class:`SMTP` class. *keyfile* and *certfile* are also
+ optional, and can contain a PEM formatted private key and certificate chain
+ file for the SSL connection. *context* also optional, can contain a
+ SSLContext, and is an alternative to keyfile and certfile; If it is
+ specified both keyfile and certfile must be None. The optional *timeout*
parameter specifies a timeout in seconds for blocking operations like the
connection attempt (if not specified, the global default timeout setting
will be used). The optional source_address parameter allows to bind to some
@@ -90,12 +97,12 @@
standard SMTP client. It's common to use Unix sockets for LMTP, so our
:meth:`connect` method must support that as well as a regular host:port
server. The optional arguments local_hostname and source_address have the
- same meaning as that of SMTP client. To specify a Unix socket, you must use
- an absolute path for *host*, starting with a '/'.
+ same meaning as they do in the :class:`SMTP` class. To specify a Unix
+ socket, you must use an absolute path for *host*, starting with a '/'.

- Authentication is supported, using the regular SMTP mechanism. When using a Unix
- socket, LMTP generally don't support or require any authentication, but your
- mileage might vary.
+ Authentication is supported, using the regular SMTP mechanism. When using a
+ Unix socket, LMTP generally don't support or require any authentication, but
+ your mileage might vary.


A nice selection of exceptions is defined as well:
diff --git a/Lib/smtplib.py b/Lib/smtplib.py
--- a/Lib/smtplib.py
+++ b/Lib/smtplib.py
@@ -222,13 +222,14 @@
If specified, `host' is the name of the remote host to which to
connect. If specified, `port' specifies the port to which to connect.
By default, smtplib.SMTP_PORT is used. If a host is specified the
- connect method is called, and if it returns anything other than
- a success code an SMTPConnectError is raised. If specified,
- `local_hostname` is used as the FQDN of the local host. By default,
- the local hostname is found using socket.getfqdn(). The
- `source_address` parameter takes a 2-tuple (host, port) for the socket
- to bind to as its source address before connecting. If the host is ''
- and port is 0, the OS default behavior will be used.
+ connect method is called, and if it returns anything other than a
+ success code an SMTPConnectError is raised. If specified,
+ `local_hostname` is used as the FQDN of the local host in the HELO/EHLO
+ command. Otherwise, the local hostname is found using
+ socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host,
+ port) for the socket to bind to as its source address before
+ connecting. If the host is '' and port is 0, the OS default behavior
+ will be used.

"""
self.timeout = timeout
@@ -852,15 +853,17 @@
if _have_ssl:

class SMTP_SSL(SMTP):
- """ This is a subclass derived from SMTP that connects over an SSL encrypted
- socket (to use this class you need a socket module that was compiled with SSL
- support). If host is not specified, '' (the local host) is used. If port is
- omitted, the standard SMTP-over-SSL port (465) is used. The optional
- source_address takes a two-tuple (host,port) for socket to bind to. keyfile and certfile
- are also optional - they can contain a PEM formatted private key and
- certificate chain file for the SSL connection. context also optional, can contain
- a SSLContext, and is an alternative to keyfile and certfile; If it is specified both
- keyfile and certfile must be None.
+ """ This is a subclass derived from SMTP that connects over an SSL
+ encrypted socket (to use this class you need a socket module that was
+ compiled with SSL support). If host is not specified, '' (the local
+ host) is used. If port is omitted, the standard SMTP-over-SSL port
+ (465) is used. local_hostname and source_address have the same meaning
+ as they do in the SMTP class. keyfile and certfile are also optional -
+ they can contain a PEM formatted private key and certificate chain file
+ for the SSL connection. context also optional, can contain a
+ SSLContext, and is an alternative to keyfile and certfile; If it is
+ specified both keyfile and certfile must be None.
+
"""

default_port = SMTP_SSL_PORT
@@ -903,10 +906,11 @@
"""LMTP - Local Mail Transfer Protocol

The LMTP protocol, which is very similar to ESMTP, is heavily based
- on the standard SMTP client. It's common to use Unix sockets for LMTP,
- so our connect() method must support that as well as a regular
- host:port server. To specify a Unix socket, you must use an absolute
- path as the host, starting with a '/'.
+ on the standard SMTP client. It's common to use Unix sockets for
+ LMTP, so our connect() method must support that as well as a regular
+ host:port server. local_hostname and source_address have the same
+ meaning as they do in the SMTP class. To specify a Unix socket,
+ you must use an absolute path as the host, starting with a '/'.

Authentication is supported, using the regular SMTP mechanism. When
using a Unix socket, LMTP generally don't support or require any

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jun 23, 2013, 3:47 PM

Post #7 of 14 (42 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/7aab60b70f90
changeset: 84304:7aab60b70f90
parent: 84303:cb70776241bb
parent: 84302:75bc0ae02bcd
user: Raymond Hettinger <python [at] rcn>
date: Sun Jun 23 15:47:03 2013 -0700
summary:
merge

files:
Include/pyport.h | 15 ++++++------
Modules/_io/fileio.c | 2 +-
Modules/_stat.c | 36 ++++--------------------------
Modules/posixmodule.c | 4 +-
4 files changed, 16 insertions(+), 41 deletions(-)


diff --git a/Include/pyport.h b/Include/pyport.h
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -393,9 +393,15 @@
#include <stat.h>
#endif

-#if defined(PYCC_VACPP)
+#ifndef S_IFMT
/* VisualAge C/C++ Failed to Define MountType Field in sys/stat.h */
-#define S_IFMT (S_IFDIR|S_IFCHR|S_IFREG)
+#define S_IFMT 0170000
+#endif
+
+#ifndef S_IFLNK
+/* Windows doesn't define S_IFLNK but posixmodule.c maps
+ * IO_REPARSE_TAG_SYMLINK to S_IFLNK */
+# define S_IFLNK 0120000
#endif

#ifndef S_ISREG
@@ -410,11 +416,6 @@
#define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR)
#endif

-#ifndef S_ISBLK
-#define S_ISBLK(x) (((x) & S_IFMT) == S_IFBLK)
-#endif
-
-
#ifdef __cplusplus
/* Move this down here since some C++ #include's don't like to be included
inside an extern "C" */
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -171,7 +171,7 @@
static int
dircheck(fileio* self, PyObject *nameobj)
{
-#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
+#if defined(HAVE_FSTAT) && defined(S_ISDIR) && defined(EISDIR)
struct stat buf;
if (self->fd < 0)
return 0;
diff --git a/Modules/_stat.c b/Modules/_stat.c
--- a/Modules/_stat.c
+++ b/Modules/_stat.c
@@ -39,35 +39,18 @@
*
* Only the names are defined by POSIX but not their value. All common file
* types seems to have the same numeric value on all platforms, though.
+ *
+ * pyport.h guarantees S_IFMT, S_IFDIR, S_IFCHR, S_IFREG and S_IFLNK
*/
-#ifndef S_IFMT
-# define S_IFMT 0170000
-#endif
-
-#ifndef S_IFDIR
-# define S_IFDIR 0040000
-#endif
-
-#ifndef S_IFCHR
-# define S_IFCHR 0020000
-#endif

#ifndef S_IFBLK
# define S_IFBLK 0060000
#endif

-#ifndef S_IFREG
-# define S_IFREG 0100000
-#endif
-
#ifndef S_IFIFO
# define S_IFIFO 0010000
#endif

-#ifndef S_IFLNK
-# define S_IFLNK 0120000
-#endif
-
#ifndef S_IFSOCK
# define S_IFSOCK 0140000
#endif
@@ -85,23 +68,14 @@
#endif


-/* S_ISXXX() */
-#ifndef S_ISDIR
-# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#endif
-
-#ifndef S_ISCHR
-# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
-#endif
+/* S_ISXXX()
+ * pyport.h defines S_ISDIR(), S_ISREG() and S_ISCHR()
+ */

#ifndef S_ISBLK
# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
#endif

-#ifndef S_ISREG
-# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
-#endif
-
#ifndef S_ISFIFO
# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
#endif
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1405,9 +1405,9 @@
result->st_ino = (((__int64)info->nFileIndexHigh)<<32) + info->nFileIndexLow;
if (reparse_tag == IO_REPARSE_TAG_SYMLINK) {
/* first clear the S_IFMT bits */
- result->st_mode ^= (result->st_mode & 0170000);
+ result->st_mode ^= (result->st_mode & S_IFMT);
/* now set the bits that make this a symlink */
- result->st_mode |= 0120000;
+ result->st_mode |= S_IFLNK;
}

return 0;

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 6, 2013, 3:05 PM

Post #8 of 14 (40 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/e8a1b4bcabcb
changeset: 84469:e8a1b4bcabcb
parent: 84468:e80634ad5a0e
parent: 84466:3555cc0ca35b
user: Brett Cannon <brett [at] python>
date: Sat Jul 06 18:05:02 2013 -0400
summary:
merge

files:
Modules/_collectionsmodule.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)


diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -805,17 +805,17 @@
Py_ssize_t index;
Py_ssize_t indexlo = deque->leftindex;

- for (b = deque->leftblock; b != NULL; b = b->rightlink) {
- const Py_ssize_t indexhi = b == deque->rightblock ?
- deque->rightindex :
- BLOCKLEN - 1;
-
- for (index = indexlo; index <= indexhi; ++index) {
+ for (b = deque->leftblock; b != deque->rightblock; b = b->rightlink) {
+ for (index = indexlo; index < BLOCKLEN ; index++) {
item = b->data[index];
Py_VISIT(item);
}
indexlo = 0;
}
+ for (index = indexlo; index <= deque->rightindex; index++) {
+ item = b->data[index];
+ Py_VISIT(item);
+ }
return 0;
}


--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 6, 2013, 8:50 PM

Post #9 of 14 (40 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/cc0c8c1c9cca
changeset: 84475:cc0c8c1c9cca
parent: 84474:e7025b1e69ce
parent: 84473:51ed51d10e60
user: Raymond Hettinger <python [at] rcn>
date: Sat Jul 06 17:50:01 2013 -1000
summary:
merge

files:
Doc/c-api/memory.rst | 171 ++++++++-
Doc/whatsnew/3.4.rst | 18 +-
Include/objimpl.h | 60 +-
Include/pymem.h | 94 +++-
Misc/NEWS | 7 +
Modules/_testcapimodule.c | 163 ++++++++
Objects/object.c | 20 -
Objects/obmalloc.c | 504 ++++++++++++++++++-------
PCbuild/python.vcxproj | 2 +-
9 files changed, 801 insertions(+), 238 deletions(-)


diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst
--- a/Doc/c-api/memory.rst
+++ b/Doc/c-api/memory.rst
@@ -84,6 +84,48 @@
for the I/O buffer escapes completely the Python memory manager.


+Raw Memory Interface
+====================
+
+The following function sets are wrappers to the system allocator. These
+functions are thread-safe, the :term:`GIL <global interpreter lock>` does not
+need to be held.
+
+The default raw memory block allocator uses the following functions:
+:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when
+requesting zero bytes.
+
+.. versionadded:: 3.4
+
+.. c:function:: void* PyMem_RawMalloc(size_t n)
+
+ Allocates *n* bytes and returns a pointer of type :c:type:`void\*` to the
+ allocated memory, or *NULL* if the request fails. Requesting zero bytes
+ returns a distinct non-*NULL* pointer if possible, as if
+ ``PyMem_RawMalloc(1)`` had been called instead. The memory will not have
+ been initialized in any way.
+
+
+.. c:function:: void* PyMem_RawRealloc(void *p, size_t n)
+
+ Resizes the memory block pointed to by *p* to *n* bytes. The contents will
+ be unchanged to the minimum of the old and the new sizes. If *p* is *NULL*,
+ the call is equivalent to ``PyMem_RawMalloc(n)``; else if *n* is equal to
+ zero, the memory block is resized but is not freed, and the returned pointer
+ is non-*NULL*. Unless *p* is *NULL*, it must have been returned by a
+ previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`. If
+ the request fails, :c:func:`PyMem_RawRealloc` returns *NULL* and *p* remains
+ a valid pointer to the previous memory area.
+
+
+.. c:function:: void PyMem_RawFree(void *p)
+
+ Frees the memory block pointed to by *p*, which must have been returned by a
+ previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`.
+ Otherwise, or if ``PyMem_Free(p)`` has been called before, undefined
+ behavior occurs. If *p* is *NULL*, no operation is performed.
+
+
.. _memoryinterface:

Memory Interface
@@ -91,8 +133,16 @@

The following function sets, modeled after the ANSI C standard, but specifying
behavior when requesting zero bytes, are available for allocating and releasing
-memory from the Python heap:
+memory from the Python heap.

+The default memory block allocator uses the following functions:
+:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when
+requesting zero bytes.
+
+.. warning::
+
+ The :term:`GIL <global interpreter lock>` must be held when using these
+ functions.

.. c:function:: void* PyMem_Malloc(size_t n)

@@ -155,6 +205,125 @@
:c:func:`PyMem_NEW`, :c:func:`PyMem_RESIZE`, :c:func:`PyMem_DEL`.


+Customize Memory Allocators
+===========================
+
+.. versionadded:: 3.4
+
+.. c:type:: PyMemAllocator
+
+ Structure used to describe a memory block allocator. The structure has
+ four fields:
+
+ +----------------------------------------------------------+---------------------------------------+
+ | Field | Meaning |
+ +==========================================================+=======================================+
+ | ``void *ctx`` | user context passed as first argument |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void* malloc(void *ctx, size_t size)`` | allocate a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void* realloc(void *ctx, void *ptr, size_t new_size)`` | allocate or resize a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void free(void *ctx, void *ptr)`` | free a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+
+.. c:type:: PyMemAllocatorDomain
+
+ Enum used to identify an allocator domain. Domains:
+
+ * :c:data:`PYMEM_DOMAIN_RAW`: functions :c:func:`PyMem_RawMalloc`,
+ :c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree`
+ * :c:data:`PYMEM_DOMAIN_MEM`: functions :c:func:`PyMem_Malloc`,
+ :c:func:`PyMem_Realloc` and :c:func:`PyMem_Free`
+ * :c:data:`PYMEM_DOMAIN_OBJ`: functions :c:func:`PyObject_Malloc`,
+ :c:func:`PyObject_Realloc` and :c:func:`PyObject_Free`
+
+
+.. c:function:: void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+
+ Get the memory block allocator of the specified domain.
+
+
+.. c:function:: void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+
+ Set the memory block allocator of the specified domain.
+
+ The new allocator must return a distinct non-NULL pointer when requesting
+ zero bytes.
+
+ For the :c:data:`PYMEM_DOMAIN_RAW` domain, the allocator must be
+ thread-safe: the :term:`GIL <global interpreter lock>` is not held when the
+ allocator is called.
+
+ If the new allocator is not a hook (does not call the previous allocator),
+ the :c:func:`PyMem_SetupDebugHooks` function must be called to reinstall the
+ debug hooks on top on the new allocator.
+
+
+.. c:function:: void PyMem_SetupDebugHooks(void)
+
+ Setup hooks to detect bugs in the following Python memory allocator
+ functions:
+
+ - :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc`,
+ :c:func:`PyMem_RawFree`
+ - :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc`, :c:func:`PyMem_Free`
+ - :c:func:`PyObject_Malloc`, :c:func:`PyObject_Realloc`,
+ :c:func:`PyObject_Free`
+
+ Newly allocated memory is filled with the byte ``0xCB``, freed memory is
+ filled with the byte ``0xDB``. Additionnal checks:
+
+ - detect API violations, ex: :c:func:`PyObject_Free` called on a buffer
+ allocated by :c:func:`PyMem_Malloc`
+ - detect write before the start of the buffer (buffer underflow)
+ - detect write after the end of the buffer (buffer overflow)
+
+ The function does nothing if Python is not compiled is debug mode.
+
+
+Customize PyObject Arena Allocator
+==================================
+
+Python has a *pymalloc* allocator for allocations smaller than 512 bytes. This
+allocator is optimized for small objects with a short lifetime. It uses memory
+mappings called "arenas" with a fixed size of 256 KB. It falls back to
+:c:func:`PyMem_Malloc` and :c:func:`PyMem_Realloc` for allocations larger than
+512 bytes. *pymalloc* is the default allocator used by
+:c:func:`PyObject_Malloc`.
+
+The default arena allocator uses the following functions:
+
+* :c:func:`VirtualAlloc` and :c:func:`VirtualFree` on Windows,
+* :c:func:`mmap` and :c:func:`munmap` if available,
+* :c:func:`malloc` and :c:func:`free` otherwise.
+
+.. versionadded:: 3.4
+
+.. c:type:: PyObjectArenaAllocator
+
+ Structure used to describe an arena allocator. The structure has
+ three fields:
+
+ +--------------------------------------------------+---------------------------------------+
+ | Field | Meaning |
+ +==================================================+=======================================+
+ | ``void *ctx`` | user context passed as first argument |
+ +--------------------------------------------------+---------------------------------------+
+ | ``void* alloc(void *ctx, size_t size)`` | allocate an arena of size bytes |
+ +--------------------------------------------------+---------------------------------------+
+ | ``void free(void *ctx, size_t size, void *ptr)`` | free an arena |
+ +--------------------------------------------------+---------------------------------------+
+
+.. c:function:: PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
+
+ Get the arena allocator.
+
+.. c:function:: PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
+
+ Set the arena allocator.
+
+
.. _memoryexamples:

Examples
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -112,21 +112,11 @@
Please read on for a comprehensive list of user-facing changes.


-.. PEP-sized items next.
+PEP 445: Add new APIs to customize Python memory allocators
+===========================================================

-.. _pep-4XX:
-
-.. PEP 4XX: Example PEP
-.. ====================
-
-
-.. (Implemented by Foo Bar.)
-
-.. .. seealso::
-
- :pep:`4XX` - Example PEP
- PEP written by Example Author
-
+The :pep:`445` adds new Application Programming Interfaces (API) to customize
+Python memory allocators.



diff --git a/Include/objimpl.h b/Include/objimpl.h
--- a/Include/objimpl.h
+++ b/Include/objimpl.h
@@ -94,9 +94,9 @@
the object gets initialized via PyObject_{Init, InitVar} after obtaining
the raw memory.
*/
-PyAPI_FUNC(void *) PyObject_Malloc(size_t);
-PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t);
-PyAPI_FUNC(void) PyObject_Free(void *);
+PyAPI_FUNC(void *) PyObject_Malloc(size_t size);
+PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyObject_Free(void *ptr);

/* This function returns the number of allocated memory blocks, regardless of size */
PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void);
@@ -106,41 +106,15 @@
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out);
#endif /* #ifndef Py_LIMITED_API */
-#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
-PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes);
-PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyObject_DebugFree(void *p);
-PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p);
-PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p);
-PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes);
-PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p);
-PyAPI_FUNC(void) _PyObject_DebugCheckAddressApi(char api, const void *p);
-PyAPI_FUNC(void *) _PyMem_DebugMalloc(size_t nbytes);
-PyAPI_FUNC(void *) _PyMem_DebugRealloc(void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyMem_DebugFree(void *p);
-#define PyObject_MALLOC _PyObject_DebugMalloc
-#define PyObject_Malloc _PyObject_DebugMalloc
-#define PyObject_REALLOC _PyObject_DebugRealloc
-#define PyObject_Realloc _PyObject_DebugRealloc
-#define PyObject_FREE _PyObject_DebugFree
-#define PyObject_Free _PyObject_DebugFree
+#endif

-#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */
+/* Macros */
#define PyObject_MALLOC PyObject_Malloc
#define PyObject_REALLOC PyObject_Realloc
#define PyObject_FREE PyObject_Free
-#endif
+#define PyObject_Del PyObject_Free
+#define PyObject_DEL PyObject_Free

-#else /* ! WITH_PYMALLOC */
-#define PyObject_MALLOC PyMem_MALLOC
-#define PyObject_REALLOC PyMem_REALLOC
-#define PyObject_FREE PyMem_FREE
-
-#endif /* WITH_PYMALLOC */
-
-#define PyObject_Del PyObject_Free
-#define PyObject_DEL PyObject_FREE

/*
* Generic object allocator interface
@@ -224,6 +198,26 @@
constructor you would start directly with PyObject_Init/InitVar
*/

+#ifndef Py_LIMITED_API
+typedef struct {
+ /* user context passed as the first argument to the 2 functions */
+ void *ctx;
+
+ /* allocate an arena of size bytes */
+ void* (*alloc) (void *ctx, size_t size);
+
+ /* free an arena */
+ void (*free) (void *ctx, void *ptr, size_t size);
+} PyObjectArenaAllocator;
+
+/* Get the arena allocator. */
+PyAPI_FUNC(void) PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator);
+
+/* Set the arena allocator. */
+PyAPI_FUNC(void) PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator);
+#endif
+
+
/*
* Garbage Collection Support
* ==========================
diff --git a/Include/pymem.h b/Include/pymem.h
--- a/Include/pymem.h
+++ b/Include/pymem.h
@@ -11,6 +11,11 @@
extern "C" {
#endif

+PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size);
+PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
+
+
/* BEWARE:

Each interface exports both functions and macros. Extension modules should
@@ -49,21 +54,11 @@
performed on failure (no exception is set, no warning is printed, etc).
*/

-PyAPI_FUNC(void *) PyMem_Malloc(size_t);
-PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t);
-PyAPI_FUNC(void) PyMem_Free(void *);
-
-/* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are
- no longer supported. They used to call PyErr_NoMemory() on failure. */
+PyAPI_FUNC(void *) PyMem_Malloc(size_t size);
+PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_Free(void *ptr);

/* Macros. */
-#ifdef PYMALLOC_DEBUG
-/* Redirect all memory operations to Python's debugging allocator. */
-#define PyMem_MALLOC _PyMem_DebugMalloc
-#define PyMem_REALLOC _PyMem_DebugRealloc
-#define PyMem_FREE _PyMem_DebugFree
-
-#else /* ! PYMALLOC_DEBUG */

/* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL
for malloc(0), which would be treated as an error. Some platforms
@@ -71,13 +66,9 @@
pymalloc. To solve these problems, allocate an extra byte. */
/* Returns NULL to indicate error if a negative size or size larger than
Py_ssize_t can represent is supplied. Helps prevents security holes. */
-#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
- : malloc((n) ? (n) : 1))
-#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
- : realloc((p), (n) ? (n) : 1))
-#define PyMem_FREE free
-
-#endif /* PYMALLOC_DEBUG */
+#define PyMem_MALLOC(n) PyMem_Malloc(n)
+#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n)
+#define PyMem_FREE(p) PyMem_Free(p)

/*
* Type-oriented memory interface
@@ -115,6 +106,69 @@
#define PyMem_Del PyMem_Free
#define PyMem_DEL PyMem_FREE

+#ifndef Py_LIMITED_API
+typedef enum {
+ /* PyMem_RawMalloc(), PyMem_RawRealloc() and PyMem_RawFree() */
+ PYMEM_DOMAIN_RAW,
+
+ /* PyMem_Malloc(), PyMem_Realloc() and PyMem_Free() */
+ PYMEM_DOMAIN_MEM,
+
+ /* PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() */
+ PYMEM_DOMAIN_OBJ
+} PyMemAllocatorDomain;
+
+typedef struct {
+ /* user context passed as the first argument to the 3 functions */
+ void *ctx;
+
+ /* allocate a memory block */
+ void* (*malloc) (void *ctx, size_t size);
+
+ /* allocate or resize a memory block */
+ void* (*realloc) (void *ctx, void *ptr, size_t new_size);
+
+ /* release a memory block */
+ void (*free) (void *ctx, void *ptr);
+} PyMemAllocator;
+
+/* Get the memory block allocator of the specified domain. */
+PyAPI_FUNC(void) PyMem_GetAllocator(PyMemAllocatorDomain domain,
+ PyMemAllocator *allocator);
+
+/* Set the memory block allocator of the specified domain.
+
+ The new allocator must return a distinct non-NULL pointer when requesting
+ zero bytes.
+
+ For the PYMEM_DOMAIN_RAW domain, the allocator must be thread-safe: the GIL
+ is not held when the allocator is called.
+
+ If the new allocator is not a hook (don't call the previous allocator), the
+ PyMem_SetupDebugHooks() function must be called to reinstall the debug hooks
+ on top on the new allocator. */
+PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain,
+ PyMemAllocator *allocator);
+
+/* Setup hooks to detect bugs in the following Python memory allocator
+ functions:
+
+ - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree()
+ - PyMem_Malloc(), PyMem_Realloc(), PyMem_Free()
+ - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free()
+
+ Newly allocated memory is filled with the byte 0xCB, freed memory is filled
+ with the byte 0xDB. Additionnal checks:
+
+ - detect API violations, ex: PyObject_Free() called on a buffer allocated
+ by PyMem_Malloc()
+ - detect write before the start of the buffer (buffer underflow)
+ - detect write after the end of the buffer (buffer overflow)
+
+ The function does nothing if Python is not compiled is debug mode. */
+PyAPI_FUNC(void) PyMem_SetupDebugHooks(void);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,13 @@
Core and Builtins
-----------------

+- Issue #17206: On Windows, increase the stack size from 2 MB to 4.2 MB to fix
+ a stack overflow in the marshal module (fix a crash in test_marshal).
+ Patch written by Jeremy Kloth.
+
+- Issue #3329: Implement the PEP 445: Add new APIs to customize Python memory
+ allocators.
+
- Issue #18328: Reorder ops in PyThreadState_Delete*() functions. Now the
tstate is first removed from TLS and then deallocated.

diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2511,6 +2511,161 @@
Py_RETURN_NONE;
}

+static PyObject *
+test_pymem_alloc0(PyObject *self)
+{
+ void *ptr;
+
+ ptr = PyMem_Malloc(0);
+ if (ptr == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc(0) returns NULL");
+ return NULL;
+ }
+ PyMem_Free(ptr);
+
+ ptr = PyObject_Malloc(0);
+ if (ptr == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "PyObject_Malloc(0) returns NULL");
+ return NULL;
+ }
+ PyObject_Free(ptr);
+
+ Py_RETURN_NONE;
+}
+
+typedef struct {
+ PyMemAllocator alloc;
+
+ size_t malloc_size;
+ void *realloc_ptr;
+ size_t realloc_new_size;
+ void *free_ptr;
+} alloc_hook_t;
+
+static void* hook_malloc (void* ctx, size_t size)
+{
+ alloc_hook_t *hook = (alloc_hook_t *)ctx;
+ hook->malloc_size = size;
+ return hook->alloc.malloc(hook->alloc.ctx, size);
+}
+
+static void* hook_realloc (void* ctx, void* ptr, size_t new_size)
+{
+ alloc_hook_t *hook = (alloc_hook_t *)ctx;
+ hook->realloc_ptr = ptr;
+ hook->realloc_new_size = new_size;
+ return hook->alloc.realloc(hook->alloc.ctx, ptr, new_size);
+}
+
+static void hook_free (void *ctx, void *ptr)
+{
+ alloc_hook_t *hook = (alloc_hook_t *)ctx;
+ hook->free_ptr = ptr;
+ hook->alloc.free(hook->alloc.ctx, ptr);
+}
+
+static PyObject *
+test_setallocators(PyMemAllocatorDomain domain)
+{
+ PyObject *res = NULL;
+ const char *error_msg;
+ alloc_hook_t hook;
+ PyMemAllocator alloc;
+ size_t size, size2;
+ void *ptr, *ptr2;
+
+ hook.malloc_size = 0;
+ hook.realloc_ptr = NULL;
+ hook.realloc_new_size = 0;
+ hook.free_ptr = NULL;
+
+ alloc.ctx = &hook;
+ alloc.malloc = &hook_malloc;
+ alloc.realloc = &hook_realloc;
+ alloc.free = &hook_free;
+ PyMem_GetAllocator(domain, &hook.alloc);
+ PyMem_SetAllocator(domain, &alloc);
+
+ size = 42;
+ switch(domain)
+ {
+ case PYMEM_DOMAIN_RAW: ptr = PyMem_RawMalloc(size); break;
+ case PYMEM_DOMAIN_MEM: ptr = PyMem_Malloc(size); break;
+ case PYMEM_DOMAIN_OBJ: ptr = PyObject_Malloc(size); break;
+ default: ptr = NULL; break;
+ }
+
+ if (ptr == NULL) {
+ error_msg = "malloc failed";
+ goto fail;
+ }
+
+ if (hook.malloc_size != size) {
+ error_msg = "malloc invalid size";
+ goto fail;
+ }
+
+ size2 = 200;
+ switch(domain)
+ {
+ case PYMEM_DOMAIN_RAW: ptr2 = PyMem_RawRealloc(ptr, size2); break;
+ case PYMEM_DOMAIN_MEM: ptr2 = PyMem_Realloc(ptr, size2); break;
+ case PYMEM_DOMAIN_OBJ: ptr2 = PyObject_Realloc(ptr, size2); break;
+ }
+
+ if (ptr2 == NULL) {
+ error_msg = "realloc failed";
+ goto fail;
+ }
+
+ if (hook.realloc_ptr != ptr
+ || hook.realloc_new_size != size2) {
+ error_msg = "realloc invalid parameters";
+ goto fail;
+ }
+
+ switch(domain)
+ {
+ case PYMEM_DOMAIN_RAW: PyMem_RawFree(ptr2); break;
+ case PYMEM_DOMAIN_MEM: PyMem_Free(ptr2); break;
+ case PYMEM_DOMAIN_OBJ: PyObject_Free(ptr2); break;
+ }
+
+ if (hook.free_ptr != ptr2) {
+ error_msg = "free invalid pointer";
+ goto fail;
+ }
+
+ Py_INCREF(Py_None);
+ res = Py_None;
+ goto finally;
+
+fail:
+ PyErr_SetString(PyExc_RuntimeError, error_msg);
+
+finally:
+ PyMem_SetAllocator(domain, &hook.alloc);
+ return res;
+}
+
+static PyObject *
+test_pymem_setrawallocators(PyObject *self)
+{
+ return test_setallocators(PYMEM_DOMAIN_RAW);
+}
+
+static PyObject *
+test_pymem_setallocators(PyObject *self)
+{
+ return test_setallocators(PYMEM_DOMAIN_MEM);
+}
+
+static PyObject *
+test_pyobject_setallocators(PyObject *self)
+{
+ return test_setallocators(PYMEM_DOMAIN_OBJ);
+}
+
static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@@ -2611,6 +2766,14 @@
{"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS},
{"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS},
{"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS},
+ {"test_pymem",
+ (PyCFunction)test_pymem_alloc0, METH_NOARGS},
+ {"test_pymem_alloc0",
+ (PyCFunction)test_pymem_setrawallocators, METH_NOARGS},
+ {"test_pymem_setallocators",
+ (PyCFunction)test_pymem_setallocators, METH_NOARGS},
+ {"test_pyobject_setallocators",
+ (PyCFunction)test_pyobject_setallocators, METH_NOARGS},
{NULL, NULL} /* sentinel */
};

diff --git a/Objects/object.c b/Objects/object.c
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1859,26 +1859,6 @@
Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size;


-/* Python's malloc wrappers (see pymem.h) */
-
-void *
-PyMem_Malloc(size_t nbytes)
-{
- return PyMem_MALLOC(nbytes);
-}
-
-void *
-PyMem_Realloc(void *p, size_t nbytes)
-{
- return PyMem_REALLOC(p, nbytes);
-}
-
-void
-PyMem_Free(void *p)
-{
- PyMem_FREE(p);
-}
-
void
_PyObject_DebugTypeStats(FILE *out)
{
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -1,18 +1,326 @@
#include "Python.h"

+/* Python's malloc wrappers (see pymem.h) */
+
+#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
+/* Forward declaration */
+static void* _PyMem_DebugMalloc(void *ctx, size_t size);
+static void _PyMem_DebugFree(void *ctx, void *p);
+static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size);
+
+static void _PyObject_DebugDumpAddress(const void *p);
+static void _PyMem_DebugCheckAddress(char api_id, const void *p);
+#endif
+
#ifdef WITH_PYMALLOC

-#ifdef HAVE_MMAP
- #include <sys/mman.h>
- #ifdef MAP_ANONYMOUS
- #define ARENAS_USE_MMAP
- #endif
+#ifdef MS_WINDOWS
+# include <windows.h>
+#elif defined(HAVE_MMAP)
+# include <sys/mman.h>
+# ifdef MAP_ANONYMOUS
+# define ARENAS_USE_MMAP
+# endif
#endif

+/* Forward declaration */
+static void* _PyObject_Malloc(void *ctx, size_t size);
+static void _PyObject_Free(void *ctx, void *p);
+static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size);
+#endif
+
+
+static void *
+_PyMem_RawMalloc(void *ctx, size_t size)
+{
+ /* PyMem_Malloc(0) means malloc(1). Some systems would return NULL
+ for malloc(0), which would be treated as an error. Some platforms would
+ return a pointer with no memory behind it, which would break pymalloc.
+ To solve these problems, allocate an extra byte. */
+ if (size == 0)
+ size = 1;
+ return malloc(size);
+}
+
+static void *
+_PyMem_RawRealloc(void *ctx, void *ptr, size_t size)
+{
+ if (size == 0)
+ size = 1;
+ return realloc(ptr, size);
+}
+
+static void
+_PyMem_RawFree(void *ctx, void *ptr)
+{
+ free(ptr);
+}
+
+
#ifdef MS_WINDOWS
-#include <windows.h>
+static void *
+_PyObject_ArenaVirtualAlloc(void *ctx, size_t size)
+{
+ return VirtualAlloc(NULL, size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+}
+
+static void
+_PyObject_ArenaVirtualFree(void *ctx, void *ptr, size_t size)
+{
+ VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+#elif defined(ARENAS_USE_MMAP)
+static void *
+_PyObject_ArenaMmap(void *ctx, size_t size)
+{
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (ptr == MAP_FAILED)
+ return NULL;
+ assert(ptr != NULL);
+ return ptr;
+}
+
+static void
+_PyObject_ArenaMunmap(void *ctx, void *ptr, size_t size)
+{
+ munmap(ptr, size);
+}
+
+#else
+static void *
+_PyObject_ArenaMalloc(void *ctx, size_t size)
+{
+ return malloc(size);
+}
+
+static void
+_PyObject_ArenaFree(void *ctx, void *ptr, size_t size)
+{
+ free(ptr);
+}
#endif

+
+#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawRealloc, _PyMem_RawFree
+#ifdef WITH_PYMALLOC
+#define PYOBJECT_FUNCS _PyObject_Malloc, _PyObject_Realloc, _PyObject_Free
+#else
+#define PYOBJECT_FUNCS PYRAW_FUNCS
+#endif
+
+#ifdef PYMALLOC_DEBUG
+typedef struct {
+ /* We tag each block with an API ID in order to tag API violations */
+ char api_id;
+ PyMemAllocator alloc;
+} debug_alloc_api_t;
+static struct {
+ debug_alloc_api_t raw;
+ debug_alloc_api_t mem;
+ debug_alloc_api_t obj;
+} _PyMem_Debug = {
+ {'r', {NULL, PYRAW_FUNCS}},
+ {'m', {NULL, PYRAW_FUNCS}},
+ {'o', {NULL, PYOBJECT_FUNCS}}
+ };
+
+#define PYDEBUG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
+#endif
+
+static PyMemAllocator _PyMem_Raw = {
+#ifdef PYMALLOC_DEBUG
+ &_PyMem_Debug.raw, PYDEBUG_FUNCS
+#else
+ NULL, PYRAW_FUNCS
+#endif
+ };
+
+static PyMemAllocator _PyMem = {
+#ifdef PYMALLOC_DEBUG
+ &_PyMem_Debug.mem, PYDEBUG_FUNCS
+#else
+ NULL, PYRAW_FUNCS
+#endif
+ };
+
+static PyMemAllocator _PyObject = {
+#ifdef PYMALLOC_DEBUG
+ &_PyMem_Debug.obj, PYDEBUG_FUNCS
+#else
+ NULL, PYOBJECT_FUNCS
+#endif
+ };
+
+#undef PYRAW_FUNCS
+#undef PYOBJECT_FUNCS
+#undef PYDEBUG_FUNCS
+
+static PyObjectArenaAllocator _PyObject_Arena = {NULL,
+#ifdef MS_WINDOWS
+ _PyObject_ArenaVirtualAlloc, _PyObject_ArenaVirtualFree
+#elif defined(ARENAS_USE_MMAP)
+ _PyObject_ArenaMmap, _PyObject_ArenaMunmap
+#else
+ _PyObject_ArenaMalloc, _PyObject_ArenaFree
+#endif
+ };
+
+void
+PyMem_SetupDebugHooks(void)
+{
+#ifdef PYMALLOC_DEBUG
+ PyMemAllocator alloc;
+
+ alloc.malloc = _PyMem_DebugMalloc;
+ alloc.realloc = _PyMem_DebugRealloc;
+ alloc.free = _PyMem_DebugFree;
+
+ if (_PyMem_Raw.malloc != _PyMem_DebugMalloc) {
+ alloc.ctx = &_PyMem_Debug.raw;
+ PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
+ }
+
+ if (_PyMem.malloc != _PyMem_DebugMalloc) {
+ alloc.ctx = &_PyMem_Debug.mem;
+ PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
+ }
+
+ if (_PyObject.malloc != _PyMem_DebugMalloc) {
+ alloc.ctx = &_PyMem_Debug.obj;
+ PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
+ PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
+ }
+#endif
+}
+
+void
+PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+{
+ switch(domain)
+ {
+ case PYMEM_DOMAIN_RAW: *allocator = _PyMem_Raw; break;
+ case PYMEM_DOMAIN_MEM: *allocator = _PyMem; break;
+ case PYMEM_DOMAIN_OBJ: *allocator = _PyObject; break;
+ default:
+ /* unknown domain */
+ allocator->ctx = NULL;
+ allocator->malloc = NULL;
+ allocator->realloc = NULL;
+ allocator->free = NULL;
+ }
+}
+
+void
+PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+{
+ switch(domain)
+ {
+ case PYMEM_DOMAIN_RAW: _PyMem_Raw = *allocator; break;
+ case PYMEM_DOMAIN_MEM: _PyMem = *allocator; break;
+ case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;
+ /* ignore unknown domain */
+ }
+
+}
+
+void
+PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
+{
+ *allocator = _PyObject_Arena;
+}
+
+void
+PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
+{
+ _PyObject_Arena = *allocator;
+}
+
+void *
+PyMem_RawMalloc(size_t size)
+{
+ /*
+ * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
+ * Most python internals blindly use a signed Py_ssize_t to track
+ * things without checking for overflows or negatives.
+ * As size_t is unsigned, checking for size < 0 is not required.
+ */
+ if (size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+
+ return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size);
+}
+
+void*
+PyMem_RawRealloc(void *ptr, size_t new_size)
+{
+ /* see PyMem_RawMalloc() */
+ if (new_size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+ return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size);
+}
+
+void PyMem_RawFree(void *ptr)
+{
+ _PyMem_Raw.free(_PyMem_Raw.ctx, ptr);
+}
+
+void *
+PyMem_Malloc(size_t size)
+{
+ /* see PyMem_RawMalloc() */
+ if (size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+ return _PyMem.malloc(_PyMem.ctx, size);
+}
+
+void *
+PyMem_Realloc(void *ptr, size_t new_size)
+{
+ /* see PyMem_RawMalloc() */
+ if (new_size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+ return _PyMem.realloc(_PyMem.ctx, ptr, new_size);
+}
+
+void
+PyMem_Free(void *ptr)
+{
+ _PyMem.free(_PyMem.ctx, ptr);
+}
+
+void *
+PyObject_Malloc(size_t size)
+{
+ /* see PyMem_RawMalloc() */
+ if (size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+ return _PyObject.malloc(_PyObject.ctx, size);
+}
+
+void *
+PyObject_Realloc(void *ptr, size_t new_size)
+{
+ /* see PyMem_RawMalloc() */
+ if (new_size > (size_t)PY_SSIZE_T_MAX)
+ return NULL;
+ return _PyObject.realloc(_PyObject.ctx, ptr, new_size);
+}
+
+void
+PyObject_Free(void *ptr)
+{
+ _PyObject.free(_PyObject.ctx, ptr);
+}
+
+
+#ifdef WITH_PYMALLOC
+
#ifdef WITH_VALGRIND
#include <valgrind/valgrind.h>

@@ -549,7 +857,6 @@
struct arena_object* arenaobj;
uint excess; /* number of bytes above pool alignment */
void *address;
- int err;

#ifdef PYMALLOC_DEBUG
if (Py_GETENV("PYTHONMALLOCSTATS"))
@@ -571,7 +878,7 @@
return NULL; /* overflow */
#endif
nbytes = numarenas * sizeof(*arenas);
- arenaobj = (struct arena_object *)realloc(arenas, nbytes);
+ arenaobj = (struct arena_object *)PyMem_Realloc(arenas, nbytes);
if (arenaobj == NULL)
return NULL;
arenas = arenaobj;
@@ -602,19 +909,8 @@
arenaobj = unused_arena_objects;
unused_arena_objects = arenaobj->nextarena;
assert(arenaobj->address == 0);
-#ifdef MS_WINDOWS
- address = (void*)VirtualAlloc(NULL, ARENA_SIZE,
- MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
- err = (address == NULL);
-#elif defined(ARENAS_USE_MMAP)
- address = mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- err = (address == MAP_FAILED);
-#else
- address = malloc(ARENA_SIZE);
- err = (address == 0);
-#endif
- if (err) {
+ address = _PyObject_Arena.alloc(_PyObject_Arena.ctx, ARENA_SIZE);
+ if (address == NULL) {
/* The allocation failed: return NULL after putting the
* arenaobj back.
*/
@@ -777,9 +1073,8 @@
* Unless the optimizer reorders everything, being too smart...
*/

-#undef PyObject_Malloc
-void *
-PyObject_Malloc(size_t nbytes)
+static void *
+_PyObject_Malloc(void *ctx, size_t nbytes)
{
block *bp;
poolp pool;
@@ -796,17 +1091,6 @@
#endif

/*
- * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
- * Most python internals blindly use a signed Py_ssize_t to track
- * things without checking for overflows or negatives.
- * As size_t is unsigned, checking for nbytes < 0 is not required.
- */
- if (nbytes > PY_SSIZE_T_MAX) {
- _Py_AllocatedBlocks--;
- return NULL;
- }
-
- /*
* This implicitly redirects malloc(0).
*/
if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) {
@@ -978,10 +1262,8 @@
* last chance to serve the request) or when the max memory limit
* has been reached.
*/
- if (nbytes == 0)
- nbytes = 1;
{
- void *result = malloc(nbytes);
+ void *result = PyMem_Malloc(nbytes);
if (!result)
_Py_AllocatedBlocks--;
return result;
@@ -990,9 +1272,8 @@

/* free */

-#undef PyObject_Free
-void
-PyObject_Free(void *p)
+static void
+_PyObject_Free(void *ctx, void *p)
{
poolp pool;
block *lastfree;
@@ -1101,13 +1382,8 @@
unused_arena_objects = ao;

/* Free the entire arena. */
-#ifdef MS_WINDOWS
- VirtualFree((void *)ao->address, 0, MEM_RELEASE);
-#elif defined(ARENAS_USE_MMAP)
- munmap((void *)ao->address, ARENA_SIZE);
-#else
- free((void *)ao->address);
-#endif
+ _PyObject_Arena.free(_PyObject_Arena.ctx,
+ (void *)ao->address, ARENA_SIZE);
ao->address = 0; /* mark unassociated */
--narenas_currently_allocated;

@@ -1216,7 +1492,7 @@
redirect:
#endif
/* We didn't allocate this address. */
- free(p);
+ PyMem_Free(p);
}

/* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0,
@@ -1224,9 +1500,8 @@
* return a non-NULL result.
*/

-#undef PyObject_Realloc
-void *
-PyObject_Realloc(void *p, size_t nbytes)
+static void *
+_PyObject_Realloc(void *ctx, void *p, size_t nbytes)
{
void *bp;
poolp pool;
@@ -1236,16 +1511,7 @@
#endif

if (p == NULL)
- return PyObject_Malloc(nbytes);
-
- /*
- * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
- * Most python internals blindly use a signed Py_ssize_t to track
- * things without checking for overflows or negatives.
- * As size_t is unsigned, checking for nbytes < 0 is not required.
- */
- if (nbytes > PY_SSIZE_T_MAX)
- return NULL;
+ return _PyObject_Malloc(ctx, nbytes);

#ifdef WITH_VALGRIND
/* Treat running_on_valgrind == -1 the same as 0 */
@@ -1273,10 +1539,10 @@
}
size = nbytes;
}
- bp = PyObject_Malloc(nbytes);
+ bp = _PyObject_Malloc(ctx, nbytes);
if (bp != NULL) {
memcpy(bp, p, size);
- PyObject_Free(p);
+ _PyObject_Free(ctx, p);
}
return bp;
}
@@ -1294,14 +1560,14 @@
* at p. Instead we punt: let C continue to manage this block.
*/
if (nbytes)
- return realloc(p, nbytes);
+ return PyMem_Realloc(p, nbytes);
/* C doesn't define the result of realloc(p, 0) (it may or may not
* return NULL then), but Python's docs promise that nbytes==0 never
* returns NULL. We don't pass 0 to realloc(), to avoid that endcase
* to begin with. Even then, we can't be sure that realloc() won't
* return NULL.
*/
- bp = realloc(p, 1);
+ bp = PyMem_Realloc(p, 1);
return bp ? bp : p;
}

@@ -1311,24 +1577,6 @@
/* pymalloc not enabled: Redirect the entry points to malloc. These will
* only be used by extensions that are compiled with pymalloc enabled. */

-void *
-PyObject_Malloc(size_t n)
-{
- return PyMem_MALLOC(n);
-}
-
-void *
-PyObject_Realloc(void *p, size_t n)
-{
- return PyMem_REALLOC(p, n);
-}
-
-void
-PyObject_Free(void *p)
-{
- PyMem_FREE(p);
-}
-
Py_ssize_t
_Py_GetAllocatedBlocks(void)
{
@@ -1354,10 +1602,6 @@
#define DEADBYTE 0xDB /* dead (newly freed) memory */
#define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */

-/* We tag each block with an API ID in order to tag API violations */
-#define _PYMALLOC_MEM_ID 'm' /* the PyMem_Malloc() API */
-#define _PYMALLOC_OBJ_ID 'o' /* The PyObject_Malloc() API */
-
static size_t serialno = 0; /* incremented on each debug {m,re}alloc */

/* serialno is always incremented via calling this routine. The point is
@@ -1440,58 +1684,18 @@
p[2*S+n: 2*S+n+S]
Copies of FORBIDDENBYTE. Used to catch over- writes and reads.
p[2*S+n+S: 2*S+n+2*S]
- A serial number, incremented by 1 on each call to _PyObject_DebugMalloc
- and _PyObject_DebugRealloc.
+ A serial number, incremented by 1 on each call to _PyMem_DebugMalloc
+ and _PyMem_DebugRealloc.
This is a big-endian size_t.
If "bad memory" is detected later, the serial number gives an
excellent way to set a breakpoint on the next run, to capture the
instant at which this block was passed out.
*/

-/* debug replacements for the PyMem_* memory API */
-void *
-_PyMem_DebugMalloc(size_t nbytes)
+static void *
+_PyMem_DebugMalloc(void *ctx, size_t nbytes)
{
- return _PyObject_DebugMallocApi(_PYMALLOC_MEM_ID, nbytes);
-}
-void *
-_PyMem_DebugRealloc(void *p, size_t nbytes)
-{
- return _PyObject_DebugReallocApi(_PYMALLOC_MEM_ID, p, nbytes);
-}
-void
-_PyMem_DebugFree(void *p)
-{
- _PyObject_DebugFreeApi(_PYMALLOC_MEM_ID, p);
-}
-
-/* debug replacements for the PyObject_* memory API */
-void *
-_PyObject_DebugMalloc(size_t nbytes)
-{
- return _PyObject_DebugMallocApi(_PYMALLOC_OBJ_ID, nbytes);
-}
-void *
-_PyObject_DebugRealloc(void *p, size_t nbytes)
-{
- return _PyObject_DebugReallocApi(_PYMALLOC_OBJ_ID, p, nbytes);
-}
-void
-_PyObject_DebugFree(void *p)
-{
- _PyObject_DebugFreeApi(_PYMALLOC_OBJ_ID, p);
-}
-void
-_PyObject_DebugCheckAddress(const void *p)
-{
- _PyObject_DebugCheckAddressApi(_PYMALLOC_OBJ_ID, p);
-}
-
-
-/* generic debug memory api, with an "id" to identify the API in use */
-void *
-_PyObject_DebugMallocApi(char id, size_t nbytes)
-{
+ debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
uchar *p; /* base address of malloc'ed block */
uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */
size_t total; /* nbytes + 4*SST */
@@ -1502,14 +1706,14 @@
/* overflow: can't represent total as a size_t */
return NULL;

- p = (uchar *)PyObject_Malloc(total);
+ p = (uchar *)api->alloc.malloc(api->alloc.ctx, total);
if (p == NULL)
return NULL;

/* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */
write_size_t(p, nbytes);
- p[SST] = (uchar)id;
- memset(p + SST + 1 , FORBIDDENBYTE, SST-1);
+ p[SST] = (uchar)api->api_id;
+ memset(p + SST + 1, FORBIDDENBYTE, SST-1);

if (nbytes > 0)
memset(p + 2*SST, CLEANBYTE, nbytes);
@@ -1527,25 +1731,27 @@
Then fills the original bytes with DEADBYTE.
Then calls the underlying free.
*/
-void
-_PyObject_DebugFreeApi(char api, void *p)
+static void
+_PyMem_DebugFree(void *ctx, void *p)
{
+ debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */
size_t nbytes;

if (p == NULL)
return;
- _PyObject_DebugCheckAddressApi(api, p);
+ _PyMem_DebugCheckAddress(api->api_id, p);
nbytes = read_size_t(q);
nbytes += 4*SST;
if (nbytes > 0)
memset(q, DEADBYTE, nbytes);
- PyObject_Free(q);
+ api->alloc.free(api->alloc.ctx, q);
}

-void *
-_PyObject_DebugReallocApi(char api, void *p, size_t nbytes)
+static void *
+_PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes)
{
+ debug_alloc_api_t *api = (debug_alloc_api_t *)ctx;
uchar *q = (uchar *)p;
uchar *tail;
size_t total; /* nbytes + 4*SST */
@@ -1553,9 +1759,9 @@
int i;

if (p == NULL)
- return _PyObject_DebugMallocApi(api, nbytes);
+ return _PyMem_DebugMalloc(ctx, nbytes);

- _PyObject_DebugCheckAddressApi(api, p);
+ _PyMem_DebugCheckAddress(api->api_id, p);
bumpserialno();
original_nbytes = read_size_t(q - 2*SST);
total = nbytes + 4*SST;
@@ -1572,12 +1778,12 @@
* case we didn't get the chance to mark the old memory with DEADBYTE,
* but we live with that.
*/
- q = (uchar *)PyObject_Realloc(q - 2*SST, total);
+ q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total);
if (q == NULL)
return NULL;

write_size_t(q, nbytes);
- assert(q[SST] == (uchar)api);
+ assert(q[SST] == (uchar)api->api_id);
for (i = 1; i < SST; ++i)
assert(q[SST + i] == FORBIDDENBYTE);
q += 2*SST;
@@ -1599,8 +1805,8 @@
* and call Py_FatalError to kill the program.
* The API id, is also checked.
*/
- void
-_PyObject_DebugCheckAddressApi(char api, const void *p)
+static void
+_PyMem_DebugCheckAddress(char api, const void *p)
{
const uchar *q = (const uchar *)p;
char msgbuf[64];
@@ -1652,7 +1858,7 @@
}

/* Display info to stderr about the memory block at p. */
-void
+static void
_PyObject_DebugDumpAddress(const void *p)
{
const uchar *q = (const uchar *)p;
diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj
--- a/PCbuild/python.vcxproj
+++ b/PCbuild/python.vcxproj
@@ -243,7 +243,7 @@
<Link>
<OutputFile>$(OutDir)python_d.exe</OutputFile>
<SubSystem>Console</SubSystem>
- <StackReserveSize>2100000</StackReserveSize>
+ <StackReserveSize>4194304</StackReserveSize>
<BaseAddress>0x1d000000</BaseAddress>
</Link>
</ItemDefinitionGroup>

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 20, 2013, 4:55 PM

Post #10 of 14 (29 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/251c09a9d411
changeset: 84752:251c09a9d411
parent: 84751:19a8c3e36cfb
parent: 84749:5643e873f06e
user: Christian Heimes <christian [at] cheimes>
date: Sun Jul 21 01:54:15 2013 +0200
summary:
merge

files:
Lib/idlelib/EditorWindow.py | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)


diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/EditorWindow.py
@@ -821,7 +821,11 @@
menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
for menubarItem in self.menudict:
menu = self.menudict[menubarItem]
- end = menu.index(END) + 1
+ end = menu.index(END)
+ if end is None:
+ # Skip empty menus
+ continue
+ end += 1
for index in range(0, end):
if menu.type(index) == 'command':
accel = menu.entrycget(index, 'accelerator')

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 22, 2013, 11:08 AM

Post #11 of 14 (29 views)
Permalink
cpython (merge default -> default): Merge [In reply to]

http://hg.python.org/cpython/rev/ffe9d73b5f8d
changeset: 84792:ffe9d73b5f8d
parent: 84791:240adc564539
parent: 84790:5812a3683402
user: Brian Curtin <brian [at] python>
date: Mon Jul 22 13:08:21 2013 -0500
summary:
Merge

files:
Lib/test/test_zipfile.py | 1438 ++++++++++---------------
Misc/NEWS | 4 +
2 files changed, 582 insertions(+), 860 deletions(-)


diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -10,10 +10,9 @@


from tempfile import TemporaryFile
-from random import randint, random
-from unittest import skipUnless
+from random import randint, random, getrandbits

-from test.support import (TESTFN, run_unittest, findfile, unlink,
+from test.support import (TESTFN, findfile, unlink,
requires_zlib, requires_bz2, requires_lzma,
captured_stdout)

@@ -27,14 +26,24 @@
('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]

+def get_files(test):
+ yield TESTFN2
+ with TemporaryFile() as f:
+ yield f
+ test.assertFalse(f.closed)
+ with io.BytesIO() as f:
+ yield f
+ test.assertFalse(f.closed)

-class TestsWithSourceFile(unittest.TestCase):
+class AbstractTestsWithSourceFile:
+ @classmethod
+ def setUpClass(cls):
+ cls.line_gen = [.bytes("Zipfile test line %d. random float: %f\n" %
+ (i, random()), "ascii")
+ for i in range(FIXEDTEST_SIZE)]
+ cls.data = b''.join(cls.line_gen)
+
def setUp(self):
- self.line_gen = (bytes("Zipfile test line %d. random float: %f" %
- (i, random()), "ascii")
- for i in range(FIXEDTEST_SIZE))
- self.data = b'\n'.join(self.line_gen) + b'\n'
-
# Make a source file with some lines
with open(TESTFN, "wb") as fp:
fp.write(self.data)
@@ -97,12 +106,10 @@

# Check that testzip doesn't raise an exception
zipfp.testzip()
- if not isinstance(f, str):
- f.close()

- def test_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_STORED)
+ def test_basic(self):
+ for f in get_files(self):
+ self.zip_test(f, self.compression)

def zip_open_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -127,30 +134,10 @@

self.assertEqual(b''.join(zipdata1), self.data)
self.assertEqual(b''.join(zipdata2), self.data)
- if not isinstance(f, str):
- f.close()

- def test_open_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_STORED)
-
- def test_open_via_zip_info(self):
- # Create the ZIP archive
- with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
- zipfp.writestr("name", "foo")
- zipfp.writestr("name", "bar")
-
- with zipfile.ZipFile(TESTFN2, "r") as zipfp:
- infos = zipfp.infolist()
- data = b""
- for info in infos:
- with zipfp.open(info) as zipopen:
- data += zipopen.read()
- self.assertTrue(data == b"foobar" or data == b"barfoo")
- data = b""
- for info in infos:
- data += zipfp.read(info)
- self.assertTrue(data == b"foobar" or data == b"barfoo")
+ def test_open(self):
+ for f in get_files(self):
+ self.zip_open_test(f, self.compression)

def zip_random_open_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -166,36 +153,17 @@
zipdata1.append(read_data)

self.assertEqual(b''.join(zipdata1), self.data)
- if not isinstance(f, str):
- f.close()

- def test_random_open_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_STORED)
-
- def test_univeral_readaheads(self):
- f = io.BytesIO()
-
- data = b'a\r\n' * 16 * 1024
- zipfp = zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED)
- zipfp.writestr(TESTFN, data)
- zipfp.close()
-
- data2 = b''
- zipfp = zipfile.ZipFile(f, 'r')
- with zipfp.open(TESTFN, 'rU') as zipopen:
- for line in zipopen:
- data2 += line
- zipfp.close()
-
- self.assertEqual(data, data2.replace(b'\n', b'\r\n'))
+ def test_random_open(self):
+ for f in get_files(self):
+ self.zip_random_open_test(f, self.compression)

def zip_readline_read_test(self, f, compression):
self.make_test_archive(f, compression)

# Read the ZIP archive
- zipfp = zipfile.ZipFile(f, "r")
- with zipfp.open(TESTFN) as zipopen:
+ with zipfile.ZipFile(f, "r") as zipfp, \
+ zipfp.open(TESTFN) as zipopen:
data = b''
while True:
read = zipopen.readline()
@@ -209,9 +177,11 @@
data += read

self.assertEqual(data, self.data)
- zipfp.close()
- if not isinstance(f, str):
- f.close()
+
+ def test_readline_read(self):
+ # Issue #7610: calls to readline() interleaved with calls to read().
+ for f in get_files(self):
+ self.zip_readline_read_test(f, self.compression)

def zip_readline_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -221,9 +191,11 @@
with zipfp.open(TESTFN) as zipopen:
for line in self.line_gen:
linedata = zipopen.readline()
- self.assertEqual(linedata, line + '\n')
- if not isinstance(f, str):
- f.close()
+ self.assertEqual(linedata, line)
+
+ def test_readline(self):
+ for f in get_files(self):
+ self.zip_readline_test(f, self.compression)

def zip_readlines_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -233,9 +205,11 @@
with zipfp.open(TESTFN) as zipopen:
ziplines = zipopen.readlines()
for line, zipline in zip(self.line_gen, ziplines):
- self.assertEqual(zipline, line + '\n')
- if not isinstance(f, str):
- f.close()
+ self.assertEqual(zipline, line)
+
+ def test_readlines(self):
+ for f in get_files(self):
+ self.zip_readlines_test(f, self.compression)

def zip_iterlines_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -244,173 +218,64 @@
with zipfile.ZipFile(f, "r") as zipfp:
with zipfp.open(TESTFN) as zipopen:
for line, zipline in zip(self.line_gen, zipopen):
- self.assertEqual(zipline, line + '\n')
- if not isinstance(f, str):
- f.close()
+ self.assertEqual(zipline, line)

- def test_readline_read_stored(self):
- # Issue #7610: calls to readline() interleaved with calls to read().
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_read_test(f, zipfile.ZIP_STORED)
+ def test_iterlines(self):
+ for f in get_files(self):
+ self.zip_iterlines_test(f, self.compression)

- def test_readline_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_test(f, zipfile.ZIP_STORED)
-
- def test_readlines_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readlines_test(f, zipfile.ZIP_STORED)
-
- def test_iterlines_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_iterlines_test(f, zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_DEFLATED)
-
-
- @requires_zlib
- def test_open_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_random_open_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readline_read_deflated(self):
- # Issue #7610: calls to readline() interleaved with calls to read().
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_read_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readline_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readlines_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readlines_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_iterlines_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_iterlines_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
def test_low_compression(self):
"""Check for cases where compressed data is larger than original."""
# Create the ZIP archive
- with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_DEFLATED) as zipfp:
+ with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp:
zipfp.writestr("strfile", '12')

# Get an open object for strfile
- with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_DEFLATED) as zipfp:
+ with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp:
with zipfp.open("strfile") as openobj:
self.assertEqual(openobj.read(1), b'1')
self.assertEqual(openobj.read(1), b'2')

- @requires_bz2
- def test_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_BZIP2)
+ def test_writestr_compression(self):
+ zipfp = zipfile.ZipFile(TESTFN2, "w")
+ zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
+ info = zipfp.getinfo('b.txt')
+ self.assertEqual(info.compress_type, self.compression)

- @requires_bz2
- def test_open_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_BZIP2)
+ def test_read_return_size(self):
+ # Issue #9837: ZipExtFile.read() shouldn't return more bytes
+ # than requested.
+ for test_size in (1, 4095, 4096, 4097, 16384):
+ file_size = test_size + 1
+ junk = getrandbits(8 * file_size).to_bytes(file_size, 'little')
+ with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf:
+ zipf.writestr('foo', junk)
+ with zipf.open('foo', 'r') as fp:
+ buf = fp.read(test_size)
+ self.assertEqual(len(buf), test_size)

- @requires_bz2
- def test_random_open_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_BZIP2)
+ def tearDown(self):
+ unlink(TESTFN)
+ unlink(TESTFN2)

- @requires_bz2
- def test_readline_read_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_read_test(f, zipfile.ZIP_BZIP2)

- @requires_bz2
- def test_readline_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_test(f, zipfile.ZIP_BZIP2)
+class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
+ unittest.TestCase):
+ compression = zipfile.ZIP_STORED
+ test_low_compression = None

- @requires_bz2
- def test_readlines_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readlines_test(f, zipfile.ZIP_BZIP2)
+ def zip_test_writestr_permissions(self, f, compression):
+ # Make sure that writestr creates files with mode 0600,
+ # when it is passed a name rather than a ZipInfo instance.

- @requires_bz2
- def test_iterlines_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_iterlines_test(f, zipfile.ZIP_BZIP2)
+ self.make_test_archive(f, compression)
+ with zipfile.ZipFile(f, "r") as zipfp:
+ zinfo = zipfp.getinfo('strfile')
+ self.assertEqual(zinfo.external_attr, 0o600 << 16)

- @requires_bz2
- def test_low_compression_bzip2(self):
- """Check for cases where compressed data is larger than original."""
- # Create the ZIP archive
- with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_BZIP2) as zipfp:
- zipfp.writestr("strfile", '12')
-
- # Get an open object for strfile
- with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_BZIP2) as zipfp:
- with zipfp.open("strfile") as openobj:
- self.assertEqual(openobj.read(1), b'1')
- self.assertEqual(openobj.read(1), b'2')
-
- @requires_lzma
- def test_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_open_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_random_open_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readline_read_lzma(self):
- # Issue #7610: calls to readline() interleaved with calls to read().
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_read_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readline_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readline_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readlines_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_readlines_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_iterlines_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_iterlines_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_low_compression_lzma(self):
- """Check for cases where compressed data is larger than original."""
- # Create the ZIP archive
- with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_LZMA) as zipfp:
- zipfp.writestr("strfile", '12')
-
- # Get an open object for strfile
- with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_LZMA) as zipfp:
- with zipfp.open("strfile") as openobj:
- self.assertEqual(openobj.read(1), b'1')
- self.assertEqual(openobj.read(1), b'2')
+ def test_writestr_permissions(self):
+ for f in get_files(self):
+ self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED)

def test_absolute_arcnames(self):
with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
@@ -470,7 +335,26 @@
with open(TESTFN, "rb") as f:
self.assertEqual(zipfp.read(TESTFN), f.read())

- @requires_zlib
+ def test_write_to_readonly(self):
+ """Check that trying to call write() on a readonly ZipFile object
+ raises a RuntimeError."""
+ with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
+ zipfp.writestr("somefile.txt", "bogus")
+
+ with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
+ self.assertRaises(RuntimeError, zipfp.write, TESTFN)
+
+ def test_add_file_before_1980(self):
+ # Set atime and mtime to 1970-01-01
+ os.utime(TESTFN, (0, 0))
+ with zipfile.ZipFile(TESTFN2, "w") as zipfp:
+ self.assertRaises(ValueError, zipfp.write, TESTFN)
+
+@requires_zlib
+class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile,
+ unittest.TestCase):
+ compression = zipfile.ZIP_DEFLATED
+
def test_per_file_compression(self):
"""Check that files within a Zip archive can have different
compression options."""
@@ -482,15 +366,263 @@
self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED)
self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED)

- def test_write_to_readonly(self):
- """Check that trying to call write() on a readonly ZipFile object
- raises a RuntimeError."""
- with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
- zipfp.writestr("somefile.txt", "bogus")
+@requires_bz2
+class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile,
+ unittest.TestCase):
+ compression = zipfile.ZIP_BZIP2

- with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
- self.assertRaises(RuntimeError, zipfp.write, TESTFN)
+@requires_lzma
+class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile,
+ unittest.TestCase):
+ compression = zipfile.ZIP_LZMA

+
+class AbstractTestZip64InSmallFiles:
+ # These tests test the ZIP64 functionality without using large files,
+ # see test_zipfile64 for proper tests.
+
+ @classmethod
+ def setUpClass(cls):
+ line_gen = (bytes("Test of zipfile line %d." % i, "ascii")
+ for i in range(0, FIXEDTEST_SIZE))
+ cls.data = b'\n'.join(line_gen)
+
+ def setUp(self):
+ self._limit = zipfile.ZIP64_LIMIT
+ zipfile.ZIP64_LIMIT = 5
+
+ # Make a source file with some lines
+ with open(TESTFN, "wb") as fp:
+ fp.write(self.data)
+
+ def zip_test(self, f, compression):
+ # Create the ZIP archive
+ with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp:
+ zipfp.write(TESTFN, "another.name")
+ zipfp.write(TESTFN, TESTFN)
+ zipfp.writestr("strfile", self.data)
+
+ # Read the ZIP archive
+ with zipfile.ZipFile(f, "r", compression) as zipfp:
+ self.assertEqual(zipfp.read(TESTFN), self.data)
+ self.assertEqual(zipfp.read("another.name"), self.data)
+ self.assertEqual(zipfp.read("strfile"), self.data)
+
+ # Print the ZIP directory
+ fp = io.StringIO()
+ zipfp.printdir(fp)
+
+ directory = fp.getvalue()
+ lines = directory.splitlines()
+ self.assertEqual(len(lines), 4) # Number of files + header
+
+ self.assertIn('File Name', lines[0])
+ self.assertIn('Modified', lines[0])
+ self.assertIn('Size', lines[0])
+
+ fn, date, time_, size = lines[1].split()
+ self.assertEqual(fn, 'another.name')
+ self.assertTrue(time.strptime(date, '%Y-%m-%d'))
+ self.assertTrue(time.strptime(time_, '%H:%M:%S'))
+ self.assertEqual(size, str(len(self.data)))
+
+ # Check the namelist
+ names = zipfp.namelist()
+ self.assertEqual(len(names), 3)
+ self.assertIn(TESTFN, names)
+ self.assertIn("another.name", names)
+ self.assertIn("strfile", names)
+
+ # Check infolist
+ infos = zipfp.infolist()
+ names = [i.filename for i in infos]
+ self.assertEqual(len(names), 3)
+ self.assertIn(TESTFN, names)
+ self.assertIn("another.name", names)
+ self.assertIn("strfile", names)
+ for i in infos:
+ self.assertEqual(i.file_size, len(self.data))
+
+ # check getinfo
+ for nm in (TESTFN, "another.name", "strfile"):
+ info = zipfp.getinfo(nm)
+ self.assertEqual(info.filename, nm)
+ self.assertEqual(info.file_size, len(self.data))
+
+ # Check that testzip doesn't raise an exception
+ zipfp.testzip()
+
+ def test_basic(self):
+ for f in get_files(self):
+ self.zip_test(f, self.compression)
+
+ def tearDown(self):
+ zipfile.ZIP64_LIMIT = self._limit
+ unlink(TESTFN)
+ unlink(TESTFN2)
+
+
+class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_STORED
+
+ def large_file_exception_test(self, f, compression):
+ with zipfile.ZipFile(f, "w", compression) as zipfp:
+ self.assertRaises(zipfile.LargeZipFile,
+ zipfp.write, TESTFN, "another.name")
+
+ def large_file_exception_test2(self, f, compression):
+ with zipfile.ZipFile(f, "w", compression) as zipfp:
+ self.assertRaises(zipfile.LargeZipFile,
+ zipfp.writestr, "another.name", self.data)
+
+ def test_large_file_exception(self):
+ for f in get_files(self):
+ self.large_file_exception_test(f, zipfile.ZIP_STORED)
+ self.large_file_exception_test2(f, zipfile.ZIP_STORED)
+
+ def test_absolute_arcnames(self):
+ with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
+ allowZip64=True) as zipfp:
+ zipfp.write(TESTFN, "/absolute")
+
+ with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
+ self.assertEqual(zipfp.namelist(), ["absolute"])
+
+@requires_zlib
+class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_DEFLATED
+
+@requires_bz2
+class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_BZIP2
+
+@requires_lzma
+class LzmaTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_LZMA
+
+
+class PyZipFileTests(unittest.TestCase):
+ def assertCompiledIn(self, name, namelist):
+ if name + 'o' not in namelist:
+ self.assertIn(name + 'c', namelist)
+
+ def test_write_pyfile(self):
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ fn = __file__
+ if fn.endswith('.pyc') or fn.endswith('.pyo'):
+ path_split = fn.split(os.sep)
+ if os.altsep is not None:
+ path_split.extend(fn.split(os.altsep))
+ if '__pycache__' in path_split:
+ fn = importlib.util.source_from_cache(fn)
+ else:
+ fn = fn[:-1]
+
+ zipfp.writepy(fn)
+
+ bn = os.path.basename(fn)
+ self.assertNotIn(bn, zipfp.namelist())
+ self.assertCompiledIn(bn, zipfp.namelist())
+
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ fn = __file__
+ if fn.endswith(('.pyc', '.pyo')):
+ fn = fn[:-1]
+
+ zipfp.writepy(fn, "testpackage")
+
+ bn = "%s/%s" % ("testpackage", os.path.basename(fn))
+ self.assertNotIn(bn, zipfp.namelist())
+ self.assertCompiledIn(bn, zipfp.namelist())
+
+ def test_write_python_package(self):
+ import email
+ packagedir = os.path.dirname(email.__file__)
+
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ zipfp.writepy(packagedir)
+
+ # Check for a couple of modules at different levels of the
+ # hierarchy
+ names = zipfp.namelist()
+ self.assertCompiledIn('email/__init__.py', names)
+ self.assertCompiledIn('email/mime/text.py', names)
+
+ def test_write_with_optimization(self):
+ import email
+ packagedir = os.path.dirname(email.__file__)
+ # use .pyc if running test in optimization mode,
+ # use .pyo if running test in debug mode
+ optlevel = 1 if __debug__ else 0
+ ext = '.pyo' if optlevel == 1 else '.pyc'
+
+ with TemporaryFile() as t, \
+ zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
+ zipfp.writepy(packagedir)
+
+ names = zipfp.namelist()
+ self.assertIn('email/__init__' + ext, names)
+ self.assertIn('email/mime/text' + ext, names)
+
+ def test_write_python_directory(self):
+ os.mkdir(TESTFN2)
+ try:
+ with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
+ fp.write("print(42)\n")
+
+ with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
+ fp.write("print(42 * 42)\n")
+
+ with open(os.path.join(TESTFN2, "mod2.txt"), "w") as fp:
+ fp.write("bla bla bla\n")
+
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ zipfp.writepy(TESTFN2)
+
+ names = zipfp.namelist()
+ self.assertCompiledIn('mod1.py', names)
+ self.assertCompiledIn('mod2.py', names)
+ self.assertNotIn('mod2.txt', names)
+
+ finally:
+ shutil.rmtree(TESTFN2)
+
+ def test_write_non_pyfile(self):
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ with open(TESTFN, 'w') as f:
+ f.write('most definitely not a python file')
+ self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
+ os.remove(TESTFN)
+
+ def test_write_pyfile_bad_syntax(self):
+ os.mkdir(TESTFN2)
+ try:
+ with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
+ fp.write("Bad syntax in python file\n")
+
+ with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
+ # syntax errors are printed to stdout
+ with captured_stdout() as s:
+ zipfp.writepy(os.path.join(TESTFN2, "mod1.py"))
+
+ self.assertIn("SyntaxError", s.getvalue())
+
+ # as it will not have compiled the python file, it will
+ # include the .py file not .pyc or .pyo
+ names = zipfp.namelist()
+ self.assertIn('mod1.py', names)
+ self.assertNotIn('mod1.pyc', names)
+ self.assertNotIn('mod1.pyo', names)
+
+ finally:
+ shutil.rmtree(TESTFN2)
+
+
+class ExtractTests(unittest.TestCase):
def test_extract(self):
with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
for fpath, fdata in SMALL_TEST_DATA:
@@ -636,47 +768,40 @@

os.remove(TESTFN2)

- def test_writestr_compression_stored(self):
- zipfp = zipfile.ZipFile(TESTFN2, "w")
- zipfp.writestr("a.txt", "hello world", compress_type=zipfile.ZIP_STORED)
- info = zipfp.getinfo('a.txt')
- self.assertEqual(info.compress_type, zipfile.ZIP_STORED)

- @requires_zlib
- def test_writestr_compression_deflated(self):
- zipfp = zipfile.ZipFile(TESTFN2, "w")
- zipfp.writestr("b.txt", "hello world", compress_type=zipfile.ZIP_DEFLATED)
- info = zipfp.getinfo('b.txt')
- self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED)
+class OtherTests(unittest.TestCase):
+ def test_open_via_zip_info(self):
+ # Create the ZIP archive
+ with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
+ zipfp.writestr("name", "foo")
+ zipfp.writestr("name", "bar")

- @requires_bz2
- def test_writestr_compression_bzip2(self):
- zipfp = zipfile.ZipFile(TESTFN2, "w")
- zipfp.writestr("b.txt", "hello world", compress_type=zipfile.ZIP_BZIP2)
- info = zipfp.getinfo('b.txt')
- self.assertEqual(info.compress_type, zipfile.ZIP_BZIP2)
+ with zipfile.ZipFile(TESTFN2, "r") as zipfp:
+ infos = zipfp.infolist()
+ data = b""
+ for info in infos:
+ with zipfp.open(info) as zipopen:
+ data += zipopen.read()
+ self.assertIn(data, {b"foobar", b"barfoo"})
+ data = b""
+ for info in infos:
+ data += zipfp.read(info)
+ self.assertIn(data, {b"foobar", b"barfoo"})

- @requires_lzma
- def test_writestr_compression_lzma(self):
- zipfp = zipfile.ZipFile(TESTFN2, "w")
- zipfp.writestr("b.txt", "hello world", compress_type=zipfile.ZIP_LZMA)
- info = zipfp.getinfo('b.txt')
- self.assertEqual(info.compress_type, zipfile.ZIP_LZMA)
+ def test_universal_readaheads(self):
+ f = io.BytesIO()

- def zip_test_writestr_permissions(self, f, compression):
- # Make sure that writestr creates files with mode 0600,
- # when it is passed a name rather than a ZipInfo instance.
+ data = b'a\r\n' * 16 * 1024
+ with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as zipfp:
+ zipfp.writestr(TESTFN, data)

- self.make_test_archive(f, compression)
- with zipfile.ZipFile(f, "r") as zipfp:
- zinfo = zipfp.getinfo('strfile')
- self.assertEqual(zinfo.external_attr, 0o600 << 16)
- if not isinstance(f, str):
- f.close()
+ data2 = b''
+ with zipfile.ZipFile(f, 'r') as zipfp, \
+ zipfp.open(TESTFN, 'rU') as zipopen:
+ for line in zipopen:
+ data2 += line

- def test_writestr_permissions(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED)
+ self.assertEqual(data, data2.replace(b'\n', b'\r\n'))

def test_writestr_extended_local_header_issue1202(self):
with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
@@ -690,12 +815,12 @@
with zipfile.ZipFile(TESTFN2, "w") as zipfp:
for fpath, fdata in SMALL_TEST_DATA:
zipfp.writestr(fpath, fdata)
- self.assertTrue(zipfp.fp is not None, 'zipfp is not open')
- self.assertTrue(zipfp.fp is None, 'zipfp is not closed')
+ self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
+ self.assertIsNone(zipfp.fp, 'zipfp is not closed')

with zipfile.ZipFile(TESTFN2, "r") as zipfp:
- self.assertTrue(zipfp.fp is not None, 'zipfp is not open')
- self.assertTrue(zipfp.fp is None, 'zipfp is not closed')
+ self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
+ self.assertIsNone(zipfp.fp, 'zipfp is not closed')

def test_close_on_exception(self):
"""Check that the zipfile is closed if an exception is raised in the
@@ -708,317 +833,7 @@
with zipfile.ZipFile(TESTFN2, "r") as zipfp2:
raise zipfile.BadZipFile()
except zipfile.BadZipFile:
- self.assertTrue(zipfp2.fp is None, 'zipfp is not closed')
-
- def test_add_file_before_1980(self):
- # Set atime and mtime to 1970-01-01
- os.utime(TESTFN, (0, 0))
- with zipfile.ZipFile(TESTFN2, "w") as zipfp:
- self.assertRaises(ValueError, zipfp.write, TESTFN)
-
-
-
-
-
-
-
- @requires_zlib
- def test_unicode_filenames(self):
- # bug #10801
- fname = findfile('zip_cp437_header.zip')
- with zipfile.ZipFile(fname) as zipfp:
- for name in zipfp.namelist():
- zipfp.open(name).close()
-
- def tearDown(self):
- unlink(TESTFN)
- unlink(TESTFN2)
-
-
-class TestZip64InSmallFiles(unittest.TestCase):
- # These tests test the ZIP64 functionality without using large files,
- # see test_zipfile64 for proper tests.
-
- def setUp(self):
- self._limit = zipfile.ZIP64_LIMIT
- zipfile.ZIP64_LIMIT = 5
-
- line_gen = (bytes("Test of zipfile line %d." % i, "ascii")
- for i in range(0, FIXEDTEST_SIZE))
- self.data = b'\n'.join(line_gen)
-
- # Make a source file with some lines
- with open(TESTFN, "wb") as fp:
- fp.write(self.data)
-
- def large_file_exception_test(self, f, compression):
- with zipfile.ZipFile(f, "w", compression) as zipfp:
- self.assertRaises(zipfile.LargeZipFile,
- zipfp.write, TESTFN, "another.name")
-
- def large_file_exception_test2(self, f, compression):
- with zipfile.ZipFile(f, "w", compression) as zipfp:
- self.assertRaises(zipfile.LargeZipFile,
- zipfp.writestr, "another.name", self.data)
- if not isinstance(f, str):
- f.close()
-
- def test_large_file_exception(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.large_file_exception_test(f, zipfile.ZIP_STORED)
- self.large_file_exception_test2(f, zipfile.ZIP_STORED)
-
- def zip_test(self, f, compression):
- # Create the ZIP archive
- with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp:
- zipfp.write(TESTFN, "another.name")
- zipfp.write(TESTFN, TESTFN)
- zipfp.writestr("strfile", self.data)
-
- # Read the ZIP archive
- with zipfile.ZipFile(f, "r", compression) as zipfp:
- self.assertEqual(zipfp.read(TESTFN), self.data)
- self.assertEqual(zipfp.read("another.name"), self.data)
- self.assertEqual(zipfp.read("strfile"), self.data)
-
- # Print the ZIP directory
- fp = io.StringIO()
- zipfp.printdir(fp)
-
- directory = fp.getvalue()
- lines = directory.splitlines()
- self.assertEqual(len(lines), 4) # Number of files + header
-
- self.assertIn('File Name', lines[0])
- self.assertIn('Modified', lines[0])
- self.assertIn('Size', lines[0])
-
- fn, date, time_, size = lines[1].split()
- self.assertEqual(fn, 'another.name')
- self.assertTrue(time.strptime(date, '%Y-%m-%d'))
- self.assertTrue(time.strptime(time_, '%H:%M:%S'))
- self.assertEqual(size, str(len(self.data)))
-
- # Check the namelist
- names = zipfp.namelist()
- self.assertEqual(len(names), 3)
- self.assertIn(TESTFN, names)
- self.assertIn("another.name", names)
- self.assertIn("strfile", names)
-
- # Check infolist
- infos = zipfp.infolist()
- names = [i.filename for i in infos]
- self.assertEqual(len(names), 3)
- self.assertIn(TESTFN, names)
- self.assertIn("another.name", names)
- self.assertIn("strfile", names)
- for i in infos:
- self.assertEqual(i.file_size, len(self.data))
-
- # check getinfo
- for nm in (TESTFN, "another.name", "strfile"):
- info = zipfp.getinfo(nm)
- self.assertEqual(info.filename, nm)
- self.assertEqual(info.file_size, len(self.data))
-
- # Check that testzip doesn't raise an exception
- zipfp.testzip()
- if not isinstance(f, str):
- f.close()
-
- def test_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_LZMA)
-
- def test_absolute_arcnames(self):
- with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
- allowZip64=True) as zipfp:
- zipfp.write(TESTFN, "/absolute")
-
- with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
- self.assertEqual(zipfp.namelist(), ["absolute"])
-
- def tearDown(self):
- zipfile.ZIP64_LIMIT = self._limit
- unlink(TESTFN)
- unlink(TESTFN2)
-
-
-class PyZipFileTests(unittest.TestCase):
- def test_write_pyfile(self):
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- fn = __file__
- if fn.endswith('.pyc') or fn.endswith('.pyo'):
- path_split = fn.split(os.sep)
- if os.altsep is not None:
- path_split.extend(fn.split(os.altsep))
- if '__pycache__' in path_split:
- fn = importlib.util.source_from_cache(fn)
- else:
- fn = fn[:-1]
-
- zipfp.writepy(fn)
-
- bn = os.path.basename(fn)
- self.assertNotIn(bn, zipfp.namelist())
- self.assertTrue(bn + 'o' in zipfp.namelist() or
- bn + 'c' in zipfp.namelist())
-
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- fn = __file__
- if fn.endswith(('.pyc', '.pyo')):
- fn = fn[:-1]
-
- zipfp.writepy(fn, "testpackage")
-
- bn = "%s/%s" % ("testpackage", os.path.basename(fn))
- self.assertNotIn(bn, zipfp.namelist())
- self.assertTrue(bn + 'o' in zipfp.namelist() or
- bn + 'c' in zipfp.namelist())
-
- def test_write_python_package(self):
- import email
- packagedir = os.path.dirname(email.__file__)
-
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- zipfp.writepy(packagedir)
-
- # Check for a couple of modules at different levels of the
- # hierarchy
- names = zipfp.namelist()
- self.assertTrue('email/__init__.pyo' in names or
- 'email/__init__.pyc' in names)
- self.assertTrue('email/mime/text.pyo' in names or
- 'email/mime/text.pyc' in names)
-
- def test_write_with_optimization(self):
- import email
- packagedir = os.path.dirname(email.__file__)
- # use .pyc if running test in optimization mode,
- # use .pyo if running test in debug mode
- optlevel = 1 if __debug__ else 0
- ext = '.pyo' if optlevel == 1 else '.pyc'
-
- with TemporaryFile() as t, \
- zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
- zipfp.writepy(packagedir)
-
- names = zipfp.namelist()
- self.assertIn('email/__init__' + ext, names)
- self.assertIn('email/mime/text' + ext, names)
-
- def test_write_python_directory(self):
- os.mkdir(TESTFN2)
- try:
- with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
- fp.write("print(42)\n")
-
- with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
- fp.write("print(42 * 42)\n")
-
- with open(os.path.join(TESTFN2, "mod2.txt"), "w") as fp:
- fp.write("bla bla bla\n")
-
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- zipfp.writepy(TESTFN2)
-
- names = zipfp.namelist()
- self.assertTrue('mod1.pyc' in names or 'mod1.pyo' in names)
- self.assertTrue('mod2.pyc' in names or 'mod2.pyo' in names)
- self.assertNotIn('mod2.txt', names)
-
- finally:
- shutil.rmtree(TESTFN2)
-
- def test_write_non_pyfile(self):
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- with open(TESTFN, 'w') as f:
- f.write('most definitely not a python file')
- self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
- os.remove(TESTFN)
-
- def test_write_pyfile_bad_syntax(self):
- os.mkdir(TESTFN2)
- try:
- with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
- fp.write("Bad syntax in python file\n")
-
- with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
- # syntax errors are printed to stdout
- with captured_stdout() as s:
- zipfp.writepy(os.path.join(TESTFN2, "mod1.py"))
-
- self.assertIn("SyntaxError", s.getvalue())
-
- # as it will not have compiled the python file, it will
- # include the .py file not .pyc or .pyo
- names = zipfp.namelist()
- self.assertIn('mod1.py', names)
- self.assertNotIn('mod1.pyc', names)
- self.assertNotIn('mod1.pyo', names)
-
- finally:
- shutil.rmtree(TESTFN2)
-
-class OtherTests(unittest.TestCase):
- zips_with_bad_crc = {
- zipfile.ZIP_STORED: (
- b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
- b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
- b'ilehello,AworldP'
- b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
- b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
- b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
- b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
- b'\0\0/\0\0\0\0\0'),
- zipfile.ZIP_DEFLATED: (
- b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
- b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
- b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
- b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
- b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
- b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
- b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
- b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00'),
- zipfile.ZIP_BZIP2: (
- b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
- b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
- b'ileBZh91AY&SY\xd4\xa8\xca'
- b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
- b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
- b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
- b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
- b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
- b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
- b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[.\x00'
- b'\x00\x00\x00\x00'),
- zipfile.ZIP_LZMA: (
- b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
- b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
- b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
- b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
- b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
- b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
- b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
- b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
- b'\x00>\x00\x00\x00\x00\x00'),
- }
+ self.assertIsNone(zipfp2.fp, 'zipfp is not closed')

def test_unsupported_version(self):
# File has an extract_version of 120
@@ -1027,10 +842,19 @@
b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06'
b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00')
+
self.assertRaises(NotImplementedError, zipfile.ZipFile,
io.BytesIO(data), 'r')

- def test_unicode_filenames(self):
+ @requires_zlib
+ def test_read_unicode_filenames(self):
+ # bug #10801
+ fname = findfile('zip_cp437_header.zip')
+ with zipfile.ZipFile(fname) as zipfp:
+ for name in zipfp.namelist():
+ zipfp.open(name).close()
+
+ def test_write_unicode_filenames(self):
with zipfile.ZipFile(TESTFN, "w") as zf:
zf.writestr("foo.txt", "Test for unicode filename")
zf.writestr("\xf6.txt", "Test for unicode filename")
@@ -1078,20 +902,16 @@
# - passing a filename
with open(TESTFN, "w") as fp:
fp.write("this is not a legal zip file\n")
- chk = zipfile.is_zipfile(TESTFN)
- self.assertFalse(chk)
+ self.assertFalse(zipfile.is_zipfile(TESTFN))
# - passing a file object
with open(TESTFN, "rb") as fp:
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(not chk)
+ self.assertFalse(zipfile.is_zipfile(fp))
# - passing a file-like object
fp = io.BytesIO()
fp.write(b"this is not a legal zip file\n")
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(not chk)
+ self.assertFalse(zipfile.is_zipfile(fp))
fp.seek(0, 0)
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(not chk)
+ self.assertFalse(zipfile.is_zipfile(fp))

def test_damaged_zipfile(self):
"""Check that zipfiles with missing bytes at the end raise BadZipFile."""
@@ -1113,22 +933,18 @@
with zipfile.ZipFile(TESTFN, mode="w") as zipf:
zipf.writestr("foo.txt", b"O, for a Muse of Fire!")

- chk = zipfile.is_zipfile(TESTFN)
- self.assertTrue(chk)
+ self.assertTrue(zipfile.is_zipfile(TESTFN))
# - passing a file object
with open(TESTFN, "rb") as fp:
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(chk)
+ self.assertTrue(zipfile.is_zipfile(fp))
fp.seek(0, 0)
zip_contents = fp.read()
# - passing a file-like object
fp = io.BytesIO()
fp.write(zip_contents)
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(chk)
+ self.assertTrue(zipfile.is_zipfile(fp))
fp.seek(0, 0)
- chk = zipfile.is_zipfile(fp)
- self.assertTrue(chk)
+ self.assertTrue(zipfile.is_zipfile(fp))

def test_non_existent_file_raises_OSError(self):
# make sure we don't raise an AttributeError when a partially-constructed
@@ -1309,93 +1125,6 @@
with zipfile.ZipFile(TESTFN, "r") as zipf:
self.assertEqual(zipf.comment, b"this is a comment")

- def check_testzip_with_bad_crc(self, compression):
- """Tests that files with bad CRCs return their name from testzip."""
- zipdata = self.zips_with_bad_crc[compression]
-
- with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
- # testzip returns the name of the first corrupt file, or None
- self.assertEqual('afile', zipf.testzip())
-
- def test_testzip_with_bad_crc_stored(self):
- self.check_testzip_with_bad_crc(zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_testzip_with_bad_crc_deflated(self):
- self.check_testzip_with_bad_crc(zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_testzip_with_bad_crc_bzip2(self):
- self.check_testzip_with_bad_crc(zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_testzip_with_bad_crc_lzma(self):
- self.check_testzip_with_bad_crc(zipfile.ZIP_LZMA)
-
- def check_read_with_bad_crc(self, compression):
- """Tests that files with bad CRCs raise a BadZipFile exception when read."""
- zipdata = self.zips_with_bad_crc[compression]
-
- # Using ZipFile.read()
- with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
- self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
-
- # Using ZipExtFile.read()
- with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
- with zipf.open('afile', 'r') as corrupt_file:
- self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
-
- # Same with small reads (in order to exercise the buffering logic)
- with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
- with zipf.open('afile', 'r') as corrupt_file:
- corrupt_file.MIN_READ_SIZE = 2
- with self.assertRaises(zipfile.BadZipFile):
- while corrupt_file.read(2):
- pass
-
- def test_read_with_bad_crc_stored(self):
- self.check_read_with_bad_crc(zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_read_with_bad_crc_deflated(self):
- self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_read_with_bad_crc_bzip2(self):
- self.check_read_with_bad_crc(zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_read_with_bad_crc_lzma(self):
- self.check_read_with_bad_crc(zipfile.ZIP_LZMA)
-
- def check_read_return_size(self, compression):
- # Issue #9837: ZipExtFile.read() shouldn't return more bytes
- # than requested.
- for test_size in (1, 4095, 4096, 4097, 16384):
- file_size = test_size + 1
- junk = b''.join(struct.pack('B', randint(0, 255))
- for x in range(file_size))
- with zipfile.ZipFile(io.BytesIO(), "w", compression) as zipf:
- zipf.writestr('foo', junk)
- with zipf.open('foo', 'r') as fp:
- buf = fp.read(test_size)
- self.assertEqual(len(buf), test_size)
-
- def test_read_return_size_stored(self):
- self.check_read_return_size(zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_read_return_size_deflated(self):
- self.check_read_return_size(zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_read_return_size_bzip2(self):
- self.check_read_return_size(zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_read_return_size_lzma(self):
- self.check_read_return_size(zipfile.ZIP_LZMA)
-
def test_empty_zipfile(self):
# Check that creating a file in 'w' or 'a' mode and closing without
# adding any files to the archives creates a valid empty ZIP file
@@ -1430,6 +1159,93 @@
unlink(TESTFN2)


+class AbstractBadCrcTests:
+ def test_testzip_with_bad_crc(self):
+ """Tests that files with bad CRCs return their name from testzip."""
+ zipdata = self.zip_with_bad_crc
+
+ with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
+ # testzip returns the name of the first corrupt file, or None
+ self.assertEqual('afile', zipf.testzip())
+
+ def test_read_with_bad_crc(self):
+ """Tests that files with bad CRCs raise a BadZipFile exception when read."""
+ zipdata = self.zip_with_bad_crc
+
+ # Using ZipFile.read()
+ with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
+ self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
+
+ # Using ZipExtFile.read()
+ with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
+ with zipf.open('afile', 'r') as corrupt_file:
+ self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
+
+ # Same with small reads (in order to exercise the buffering logic)
+ with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
+ with zipf.open('afile', 'r') as corrupt_file:
+ corrupt_file.MIN_READ_SIZE = 2
+ with self.assertRaises(zipfile.BadZipFile):
+ while corrupt_file.read(2):
+ pass
+
+
+class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
+ compression = zipfile.ZIP_STORED
+ zip_with_bad_crc = (
+ b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
+ b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
+ b'ilehello,AworldP'
+ b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
+ b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
+ b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
+ b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
+ b'\0\0/\0\0\0\0\0')
+
+@requires_zlib
+class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
+ compression = zipfile.ZIP_DEFLATED
+ zip_with_bad_crc = (
+ b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
+ b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
+ b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
+ b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
+ b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
+ b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
+ b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
+
+@requires_bz2
+class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
+ compression = zipfile.ZIP_BZIP2
+ zip_with_bad_crc = (
+ b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
+ b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
+ b'ileBZh91AY&SY\xd4\xa8\xca'
+ b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
+ b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
+ b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
+ b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
+ b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
+ b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
+ b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[.\x00'
+ b'\x00\x00\x00\x00')
+
+@requires_lzma
+class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
+ compression = zipfile.ZIP_LZMA
+ zip_with_bad_crc = (
+ b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
+ b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
+ b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
+ b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
+ b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
+ b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
+ b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
+ b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
+ b'\x00>\x00\x00\x00\x00\x00')
+
+
class DecryptionTests(unittest.TestCase):
"""Check that ZIP decryption works. Since the library does not
support encryption at the moment, we use a pre-generated encrypted
@@ -1495,13 +1311,14 @@
self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")

+class AbstractTestsWithRandomBinaryFiles:
+ @classmethod
+ def setUpClass(cls):
+ datacount = randint(16, 64)*1024 + randint(1, 1024)
+ cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
+ for i in range(datacount))

-class TestsWithRandomBinaryFiles(unittest.TestCase):
def setUp(self):
- datacount = randint(16, 64)*1024 + randint(1, 1024)
- self.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
- for i in range(datacount))
-
# Make a source file with some lines
with open(TESTFN, "wb") as fp:
fp.write(self.data)
@@ -1525,27 +1342,10 @@
self.assertEqual(len(testdata), len(self.data))
self.assertEqual(testdata, self.data)
self.assertEqual(zipfp.read("another.name"), self.data)
- if not isinstance(f, str):
- f.close()

- def test_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_test(f, zipfile.ZIP_LZMA)
+ def test_read(self):
+ for f in get_files(self):
+ self.zip_test(f, self.compression)

def zip_open_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1575,27 +1375,10 @@
testdata2 = b''.join(zipdata2)
self.assertEqual(len(testdata2), len(self.data))
self.assertEqual(testdata2, self.data)
- if not isinstance(f, str):
- f.close()

- def test_open_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_open_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_open_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_open_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_open_test(f, zipfile.ZIP_LZMA)
+ def test_open(self):
+ for f in get_files(self):
+ self.zip_open_test(f, self.compression)

def zip_random_open_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1613,27 +1396,30 @@
testdata = b''.join(zipdata1)
self.assertEqual(len(testdata), len(self.data))
self.assertEqual(testdata, self.data)
- if not isinstance(f, str):
- f.close()

- def test_random_open_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_STORED)
+ def test_random_open(self):
+ for f in get_files(self):
+ self.zip_random_open_test(f, self.compression)

- @requires_zlib
- def test_random_open_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_DEFLATED)

- @requires_bz2
- def test_random_open_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_BZIP2)
+class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_STORED

- @requires_lzma
- def test_random_open_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.zip_random_open_test(f, zipfile.ZIP_LZMA)
+@requires_zlib
+class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_DEFLATED
+
+@requires_bz2
+class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_BZIP2
+
+@requires_lzma
+class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
+ unittest.TestCase):
+ compression = zipfile.ZIP_LZMA


@requires_zlib
@@ -1711,21 +1497,22 @@
unlink(TESTFN)


-class UniversalNewlineTests(unittest.TestCase):
+class AbstractUniversalNewlineTests:
+ @classmethod
+ def setUpClass(cls):
+ cls.line_gen = [.bytes("Test of zipfile line %d." % i, "ascii")
+ for i in range(FIXEDTEST_SIZE)]
+ cls.seps = (b'\r', b'\r\n', b'\n')
+ cls.arcdata = {}
+ for n, s in enumerate(cls.seps):
+ cls.arcdata[s] = s.join(cls.line_gen) + s
+
def setUp(self):
- self.line_gen = [.bytes("Test of zipfile line %d." % i, "ascii")
- for i in range(FIXEDTEST_SIZE)]
- self.seps = ('\r', '\r\n', '\n')
- self.arcdata, self.arcfiles = {}, {}
+ self.arcfiles = {}
for n, s in enumerate(self.seps):
- b = s.encode("ascii")
- self.arcdata[s] = b.join(self.line_gen) + b
self.arcfiles[s] = '%s-%d' % (TESTFN, n)
- f = open(self.arcfiles[s], "wb")
- try:
+ with open(self.arcfiles[s], "wb") as f:
f.write(self.arcdata[s])
- finally:
- f.close()

def make_test_archive(self, f, compression):
# Create the ZIP archive
@@ -1742,8 +1529,10 @@
with zipfp.open(fn, "rU") as fp:
zipdata = fp.read()
self.assertEqual(self.arcdata[sep], zipdata)
- if not isinstance(f, str):
- f.close()
+
+ def test_read(self):
+ for f in get_files(self):
+ self.read_test(f, self.compression)

def readline_read_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1764,10 +1553,11 @@
break
data += read

- self.assertEqual(data, self.arcdata['\n'])
+ self.assertEqual(data, self.arcdata[b'\n'])

- if not isinstance(f, str):
- f.close()
+ def test_readline_read(self):
+ for f in get_files(self):
+ self.readline_read_test(f, self.compression)

def readline_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1779,8 +1569,10 @@
for line in self.line_gen:
linedata = zipopen.readline()
self.assertEqual(linedata, line + b'\n')
- if not isinstance(f, str):
- f.close()
+
+ def test_readline(self):
+ for f in get_files(self):
+ self.readline_test(f, self.compression)

def readlines_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1792,8 +1584,10 @@
ziplines = fp.readlines()
for line, zipline in zip(self.line_gen, ziplines):
self.assertEqual(zipline, line + b'\n')
- if not isinstance(f, str):
- f.close()
+
+ def test_readlines(self):
+ for f in get_files(self):
+ self.readlines_test(f, self.compression)

def iterlines_test(self, f, compression):
self.make_test_archive(f, compression)
@@ -1804,105 +1598,10 @@
with zipfp.open(fn, "rU") as fp:
for line, zipline in zip(self.line_gen, fp):
self.assertEqual(zipline, line + b'\n')
- if not isinstance(f, str):
- f.close()

- def test_read_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.read_test(f, zipfile.ZIP_STORED)
-
- def test_readline_read_stored(self):
- # Issue #7610: calls to readline() interleaved with calls to read().
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_read_test(f, zipfile.ZIP_STORED)
-
- def test_readline_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_test(f, zipfile.ZIP_STORED)
-
- def test_readlines_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readlines_test(f, zipfile.ZIP_STORED)
-
- def test_iterlines_stored(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.iterlines_test(f, zipfile.ZIP_STORED)
-
- @requires_zlib
- def test_read_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.read_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readline_read_deflated(self):
- # Issue #7610: calls to readline() interleaved with calls to read().
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_read_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readline_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_readlines_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readlines_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_zlib
- def test_iterlines_deflated(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.iterlines_test(f, zipfile.ZIP_DEFLATED)
-
- @requires_bz2
- def test_read_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.read_test(f, zipfile.ZIP_BZIP2)
-
- @requires_bz2
- def test_readline_read_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_read_test(f, zipfile.ZIP_BZIP2)
-
- @requires_bz2
- def test_readline_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_test(f, zipfile.ZIP_BZIP2)
-
- @requires_bz2
- def test_readlines_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readlines_test(f, zipfile.ZIP_BZIP2)
-
- @requires_bz2
- def test_iterlines_bzip2(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.iterlines_test(f, zipfile.ZIP_BZIP2)
-
- @requires_lzma
- def test_read_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.read_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readline_read_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_read_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readline_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readline_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_readlines_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.readlines_test(f, zipfile.ZIP_LZMA)
-
- @requires_lzma
- def test_iterlines_lzma(self):
- for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
- self.iterlines_test(f, zipfile.ZIP_LZMA)
+ def test_iterlines(self):
+ for f in get_files(self):
+ self.iterlines_test(f, self.compression)

def tearDown(self):
for sep, fn in self.arcfiles.items():
@@ -1911,5 +1610,24 @@
unlink(TESTFN2)


+class StoredUniversalNewlineTests(AbstractUniversalNewlineTests,
+ unittest.TestCase):
+ compression = zipfile.ZIP_STORED
+
+@requires_zlib
+class DeflateUniversalNewlineTests(AbstractUniversalNewlineTests,
+ unittest.TestCase):
+ compression = zipfile.ZIP_DEFLATED
+
+@requires_bz2
+class Bzip2UniversalNewlineTests(AbstractUniversalNewlineTests,
+ unittest.TestCase):
+ compression = zipfile.ZIP_BZIP2
+
+@requires_lzma
+class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests,
+ unittest.TestCase):
+ compression = zipfile.ZIP_LZMA
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -548,6 +548,10 @@
Tests
-----

+- Issue #17944: test_zipfile now discoverable and uses subclassing to
+ generate tests for different compression types. Fixed a bug with skipping
+ some tests due to use of exhausted iterators.
+
- Issue #18266: test_largefile now works with unittest test discovery and
supports running only selected tests. Patch by Zachary Ware.


--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 31, 2013, 2:58 AM

Post #12 of 14 (29 views)
Permalink
cpython (merge default -> default): merge [In reply to]

http://hg.python.org/cpython/rev/e825df34af94
changeset: 84930:e825df34af94
parent: 84929:6f2ba9a33bdf
parent: 84927:acfb863b0937
user: Christian Heimes <christian [at] cheimes>
date: Wed Jul 31 11:58:41 2013 +0200
summary:
merge

files:
Doc/library/unittest.rst | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)


diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -974,12 +974,12 @@
Test that a warning is triggered when *callable* is called with any
positional or keyword arguments that are also passed to
:meth:`assertWarns`. The test passes if *warning* is triggered and
- fails if it isn't. Also, any unexpected exception is an error.
+ fails if it isn't. Any exception is an error.
To catch any of a group of warnings, a tuple containing the warning
classes may be passed as *warnings*.

If only the *warning* and possibly the *msg* arguments are given,
- returns a context manager so that the code under test can be written
+ return a context manager so that the code under test can be written
inline rather than as a function::

with self.assertWarns(SomeWarning):
@@ -992,7 +992,7 @@
:attr:`warning` attribute, and the source line which triggered the
warnings in the :attr:`filename` and :attr:`lineno` attributes.
This can be useful if the intention is to perform additional checks
- on the exception raised::
+ on the warning caught::

with self.assertWarns(SomeWarning) as cm:
do_something()

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Jul 31, 2013, 12:54 PM

Post #13 of 14 (29 views)
Permalink
cpython (merge default -> default): Merge [In reply to]

http://hg.python.org/cpython/rev/3a7dedc7067f
changeset: 84935:3a7dedc7067f
parent: 84934:e4594c7dfeeb
parent: 84932:8327780d3841
user: Antoine Pitrou <solipsis [at] pitrou>
date: Wed Jul 31 21:54:18 2013 +0200
summary:
Merge

files:
.hgignore | 5 ++
Doc/library/aifc.rst | 3 +-
Doc/library/difflib.rst | 2 +-
Doc/library/email.iterators.rst | 2 +-
Doc/library/email.policy.rst | 2 +-
Doc/library/enum.rst | 10 ++--
Doc/library/unittest.rst | 6 +-
Doc/library/wave.rst | 5 ++
Doc/tutorial/inputoutput.rst | 13 +++-
Doc/whatsnew/3.4.rst | 5 +-
Lib/test/test_wave.py | 29 ++++++++----
Lib/wave.py | 13 +++++
Makefile.pre.in | 49 ++++++++++++++++++++-
Misc/NEWS | 6 ++
Modules/_io/iobase.c | 4 +-
Modules/_sha3/sha3module.c | 4 +-
Modules/_testcapimodule.c | 11 ++++
17 files changed, 137 insertions(+), 32 deletions(-)


diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -36,6 +36,7 @@
Modules/config.c
Modules/ld_so_aix$
Parser/pgen$
+^lcov-report/
^core
^python-gdb.py
^python.exe-gdb.py
@@ -91,3 +92,7 @@
.coverage
coverage/
htmlcov/
+*.gcda
+*.gcno
+*.gcov
+coverage.info
diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst
--- a/Doc/library/aifc.rst
+++ b/Doc/library/aifc.rst
@@ -51,7 +51,8 @@
used for writing, the file object should be seekable, unless you know ahead of
time how many samples you are going to write in total and use
:meth:`writeframesraw` and :meth:`setnframes`.
- Objects returned by :func:`.open` also supports the :keyword:`with` statement.
+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`~aifc.close` method is called.

.. versionchanged:: 3.4
Support for the :keyword:`with` statement was added.
diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst
--- a/Doc/library/difflib.rst
+++ b/Doc/library/difflib.rst
@@ -752,7 +752,7 @@
# we're passing these as arguments to the diff function
fromdate = time.ctime(os.stat(fromfile).st_mtime)
todate = time.ctime(os.stat(tofile).st_mtime)
- with open(fromlines) as fromf, open(tofile) as tof:
+ with open(fromfile) as fromf, open(tofile) as tof:
fromlines, tolines = list(fromf), list(tof)

if options.u:
diff --git a/Doc/library/email.iterators.rst b/Doc/library/email.iterators.rst
--- a/Doc/library/email.iterators.rst
+++ b/Doc/library/email.iterators.rst
@@ -68,7 +68,7 @@
text/plain
text/plain

- .. testcleanup::
+ .. testsetup::

>>> somefile.close()

diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
--- a/Doc/library/email.policy.rst
+++ b/Doc/library/email.policy.rst
@@ -85,7 +85,7 @@
>>> p.stdin.close()
>>> rc = p.wait()

-.. testcleanup::
+.. testsetup::

>>> mymsg.close()
>>> mocker.stop()
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -483,7 +483,7 @@
... def __new__(cls):
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
- ... obj._value = value
+ ... obj._value_ = value
... return obj
...
>>> class Color(AutoNumber):
@@ -505,19 +505,19 @@
>>> class OrderedEnum(Enum):
... def __ge__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value >= other._value
+ ... return self.value >= other.value
... return NotImplemented
... def __gt__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value > other._value
+ ... return self.value > other.value
... return NotImplemented
... def __le__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value <= other._value
+ ... return self.value <= other.value
... return NotImplemented
... def __lt__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value < other._value
+ ... return self.value < other.value
... return NotImplemented
...
>>> class Grade(OrderedEnum):
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -974,12 +974,12 @@
Test that a warning is triggered when *callable* is called with any
positional or keyword arguments that are also passed to
:meth:`assertWarns`. The test passes if *warning* is triggered and
- fails if it isn't. Also, any unexpected exception is an error.
+ fails if it isn't. Any exception is an error.
To catch any of a group of warnings, a tuple containing the warning
classes may be passed as *warnings*.

If only the *warning* and possibly the *msg* arguments are given,
- returns a context manager so that the code under test can be written
+ return a context manager so that the code under test can be written
inline rather than as a function::

with self.assertWarns(SomeWarning):
@@ -992,7 +992,7 @@
:attr:`warning` attribute, and the source line which triggered the
warnings in the :attr:`filename` and :attr:`lineno` attributes.
This can be useful if the intention is to perform additional checks
- on the exception raised::
+ on the warning caught::

with self.assertWarns(SomeWarning) as cm:
do_something()
diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst
--- a/Doc/library/wave.rst
+++ b/Doc/library/wave.rst
@@ -39,6 +39,11 @@
:meth:`close` method is called; it is the caller's responsibility to close
the file object.

+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`Wave_read.close()
+ <wave.Wave_read.close>` or :meth:`Wave_write.close()
+ <wave.Wave_write.close()>` method is called.
+

.. function:: openfp(file, mode)

diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst
--- a/Doc/tutorial/inputoutput.rst
+++ b/Doc/tutorial/inputoutput.rst
@@ -322,9 +322,11 @@
>>> f.write(s)
18

-``f.tell()`` returns an integer giving the file object's current position in the
-file, measured in bytes from the beginning of the file. To change the file
-object's position, use ``f.seek(offset, from_what)``. The position is computed
+``f.tell()`` returns an integer giving the file object's current position in the file
+represented as number of bytes from the beginning of the file when in `binary mode` and
+an opaque number when in `text mode`.
+
+To change the file object's position, use ``f.seek(offset, from_what)``. The position is computed
from adding *offset* to a reference point; the reference point is selected by
the *from_what* argument. A *from_what* value of 0 measures from the beginning
of the file, 1 uses the current file position, and 2 uses the end of the file as
@@ -345,7 +347,10 @@

In text files (those opened without a ``b`` in the mode string), only seeks
relative to the beginning of the file are allowed (the exception being seeking
-to the very file end with ``seek(0, 2)``).
+to the very file end with ``seek(0, 2)``) and the only valid *offset* values are
+those returned from the ``f.tell()``, or zero. Any other *offset* value produces
+undefined behaviour.
+

When you're done with a file, call ``f.close()`` to close it and free up any
system resources taken up by the open file. After calling ``f.close()``,
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -239,8 +239,11 @@
The :meth:`~wave.getparams` method now returns a namedtuple rather than a
plain tuple. (Contributed by Claudiu Popa in :issue:`17487`.)

+:meth:`wave.open` now supports the context manager protocol. (Contributed
+by Claudiu Popa in :issue:`17616`.)
+
stat
----
+----

The stat module is now backed by a C implementation in :mod:`_stat`. A C
implementation is required as most of the values aren't standardized and
diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py
--- a/Lib/test/test_wave.py
+++ b/Lib/test/test_wave.py
@@ -1,7 +1,5 @@
-from test.support import TESTFN, run_unittest
-import os
+from test.support import TESTFN, unlink
import wave
-import struct
import unittest

nchannels = 2
@@ -17,10 +15,7 @@
def tearDown(self):
if self.f is not None:
self.f.close()
- try:
- os.remove(TESTFN)
- except OSError:
- pass
+ unlink(TESTFN)

def test_it(self, test_rounding=False):
self.f = wave.open(TESTFN, 'wb')
@@ -74,9 +69,23 @@
self.assertEqual(params.comptype, self.f.getcomptype())
self.assertEqual(params.compname, self.f.getcompname())

+ def test_context_manager(self):
+ self.f = wave.open(TESTFN, 'wb')
+ self.f.setnchannels(nchannels)
+ self.f.setsampwidth(sampwidth)
+ self.f.setframerate(framerate)
+ self.f.close()

-def test_main():
- run_unittest(TestWave)
+ with wave.open(TESTFN) as f:
+ self.assertFalse(f.getfp().closed)
+ self.assertIs(f.getfp(), None)
+
+ with open(TESTFN, 'wb') as testfile:
+ with self.assertRaises(wave.Error):
+ with wave.open(testfile, 'wb'):
+ pass
+ self.assertEqual(testfile.closed, False)
+

if __name__ == '__main__':
- test_main()
+ unittest.main()
diff --git a/Lib/wave.py b/Lib/wave.py
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -167,6 +167,13 @@

def __del__(self):
self.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -323,6 +330,12 @@
def __del__(self):
self.close()

+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
diff --git a/Makefile.pre.in b/Makefile.pre.in
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -211,6 +211,12 @@
PROFILE_TASK= $(srcdir)/Tools/pybench/pybench.py -n 2 --with-gc --with-syscheck
#PROFILE_TASK= $(srcdir)/Lib/test/regrtest.py

+# report files for gcov / lcov coverage report
+COVERAGE_INFO= $(abs_builddir)/coverage.info
+COVERAGE_REPORT=$(abs_builddir)/lcov-report
+COVERAGE_REPORT_OPTIONS=--no-branch-coverage --title "CPython lcov report"
+
+
# === Definitions added by makesetup ===


@@ -463,11 +469,48 @@
build_all_use_profile:
$(MAKE) all CFLAGS="$(CFLAGS) -fprofile-use -fprofile-correction"

+# Compile and run with gcov
+.PHONY=coverage coverage-lcov coverage-report
coverage:
@echo "Building with support for coverage checking:"
- $(MAKE) clean
+ $(MAKE) clean profile-removal
$(MAKE) all CFLAGS="$(CFLAGS) -O0 -pg -fprofile-arcs -ftest-coverage" LIBS="$(LIBS) -lgcov"

+coverage-lcov:
+ @echo "Creating Coverage HTML report with LCOV:"
+ @rm -f $(COVERAGE_INFO)
+ @rm -rf $(COVERAGE_REPORT)
+ @lcov --capture --directory $(abs_builddir) \
+ --base-directory $(realpath $(abs_builddir)) \
+ --path $(realpath $(abs_srcdir)) \
+ --output-file $(COVERAGE_INFO)
+ : # remove 3rd party modules and system headers
+ @lcov --remove $(COVERAGE_INFO) \
+ '*/Modules/_ctypes/libffi*/*' \
+ '*/Modules/_sha3/keccak/*' \
+ '*/Modules/_decimal/libmpdec/*' \
+ '*/Modules/expat/*' \
+ '*/Modules/zlib/*' \
+ '*/Include/*' \
+ '/usr/include/*' \
+ '/usr/local/include/*' \
+ --output-file $(COVERAGE_INFO)
+ @genhtml $(COVERAGE_INFO) --output-directory $(COVERAGE_REPORT) \
+ $(COVERAGE_REPORT_OPTIONS)
+ @echo
+ @echo "lcov report at $(COVERAGE_REPORT)/index.html"
+ @echo
+
+coverage-report:
+ : # force rebuilding of parser and importlib
+ @touch $(GRAMMAR_INPUT)
+ @touch $(srcdir)/Lib/importlib/_bootstrap.py
+ : # build with coverage info
+ $(MAKE) coverage
+ : # run tests, ignore failures
+ $(TESTRUNNER) $(TESTOPTS) || true
+ : # build lcov report
+ $(MAKE) coverage-lcov

# Build the interpreter
$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY)
@@ -1012,7 +1055,7 @@
tkinter/test/test_ttk site-packages test \
test/capath test/data \
test/cjkencodings test/decimaltestdata test/xmltestdata \
- test/subprocessdata test/sndhdrdata \
+ test/subprocessdata test/sndhdrdata test/support \
test/tracedmodules test/encoded_modules \
test/namespace_pkgs \
test/namespace_pkgs/both_portions \
@@ -1396,6 +1439,8 @@

profile-removal:
find . -name '*.gc??' -exec rm -f {} ';'
+ rm -f $(COVERAGE_INFO)
+ rm -rf $(COVERAGE_REPORT)

clobber: clean profile-removal
-rm -f $(BUILDPYTHON) $(PGEN) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -177,6 +177,8 @@
initialization, so as to reclaim allocated resources (Python callbacks)
at shutdown. Original patch by Robin Schreiber.

+- Issue #17616: wave.open now supports the context manager protocol.
+
- Issue #18599: Fix name attribute of _sha1.sha1() object. It now returns
'SHA1' instead of 'SHA'.

@@ -729,6 +731,10 @@
Build
-----

+- Issue #18481: Add C coverage reporting with gcov and lcov. A new make target
+ "coverage-report" creates an instrumented Python build, runs unit tests
+ and creates a HTML. The report can be updated with "make coverage-lcov".
+
- Issue #17845: Clarified the message printed when some module are not built.

- Issue #18256: Compilation fix for recent AIX releases. Patch by
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -210,8 +210,10 @@
/* If `closed` doesn't exist or can't be evaluated as bool, then the
object is probably in an unusable state, so ignore. */
res = PyObject_GetAttr(self, _PyIO_str_closed);
- if (res == NULL)
+ if (res == NULL) {
PyErr_Clear();
+ closed = -1;
+ }
else {
closed = PyObject_IsTrue(res);
Py_DECREF(res);
diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c
--- a/Modules/_sha3/sha3module.c
+++ b/Modules/_sha3/sha3module.c
@@ -322,7 +322,7 @@
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);

/* add new data, the function takes the length in bits not bytes */
-#ifdef WITH_THREADS
+#ifdef WITH_THREAD
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
self->lock = PyThread_allocate_lock();
}
@@ -464,7 +464,7 @@
}

if (data_obj) {
-#ifdef WITH_THREADS
+#ifdef WITH_THREAD
if (buf.len >= HASHLIB_GIL_MINSIZE) {
/* invariant: New objects can't be accessed by other code yet,
* thus it's safe to release the GIL without locking the object.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2614,6 +2614,16 @@
}

static PyObject *
+test_incref_decref_API(PyObject *ob)
+{
+ PyObject *obj = PyLong_FromLong(0);
+ Py_IncRef(ob);
+ Py_DecRef(obj);
+ Py_DecRef(obj);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
test_pymem_alloc0(PyObject *self)
{
void *ptr;
@@ -2781,6 +2791,7 @@
{"test_incref_doesnt_leak", (PyCFunction)test_incref_doesnt_leak, METH_NOARGS},
{"test_xdecref_doesnt_leak",(PyCFunction)test_xdecref_doesnt_leak, METH_NOARGS},
{"test_decref_doesnt_leak", (PyCFunction)test_decref_doesnt_leak, METH_NOARGS},
+ {"test_incref_decref_API", (PyCFunction)test_incref_decref_API, METH_NOARGS},
{"test_long_and_overflow", (PyCFunction)test_long_and_overflow,
METH_NOARGS},
{"test_long_as_double", (PyCFunction)test_long_as_double,METH_NOARGS},

--
Repository URL: http://hg.python.org/cpython


python-checkins at python

Aug 10, 2013, 3:40 PM

Post #14 of 14 (7 views)
Permalink
cpython (merge default -> default): Merge [In reply to]

http://hg.python.org/cpython/rev/3fd62c312f1a
changeset: 85117:3fd62c312f1a
parent: 85112:1edff836c954
parent: 85116:febe4f36e020
user: Terry Jan Reedy <tjreedy [at] udel>
date: Sat Aug 10 18:40:04 2013 -0400
summary:
Merge

files:
Lib/queue.py | 8 ++++----
Misc/ACKS | 1 +
Misc/NEWS | 5 ++++-
3 files changed, 9 insertions(+), 5 deletions(-)


diff --git a/Lib/queue.py b/Lib/queue.py
--- a/Lib/queue.py
+++ b/Lib/queue.py
@@ -120,7 +120,7 @@

If optional args 'block' is true and 'timeout' is None (the default),
block if necessary until a free slot is available. If 'timeout' is
- a positive number, it blocks at most 'timeout' seconds and raises
+ a non-negative number, it blocks at most 'timeout' seconds and raises
the Full exception if no free slot was available within that time.
Otherwise ('block' is false), put an item on the queue if a free slot
is immediately available, else raise the Full exception ('timeout'
@@ -135,7 +135,7 @@
while self._qsize() >= self.maxsize:
self.not_full.wait()
elif timeout < 0:
- raise ValueError("'timeout' must be a positive number")
+ raise ValueError("'timeout' must be a non-negative number")
else:
endtime = time() + timeout
while self._qsize() >= self.maxsize:
@@ -152,7 +152,7 @@

If optional args 'block' is true and 'timeout' is None (the default),
block if necessary until an item is available. If 'timeout' is
- a positive number, it blocks at most 'timeout' seconds and raises
+ a non-negative number, it blocks at most 'timeout' seconds and raises
the Empty exception if no item was available within that time.
Otherwise ('block' is false), return an item if one is immediately
available, else raise the Empty exception ('timeout' is ignored
@@ -166,7 +166,7 @@
while not self._qsize():
self.not_empty.wait()
elif timeout < 0:
- raise ValueError("'timeout' must be a positive number")
+ raise ValueError("'timeout' must be a non-negative number")
else:
endtime = time() + timeout
while not self._qsize():
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -773,6 +773,7 @@
Lukas Lueg
Loren Luke
Fredrik Lundh
+Zhongyue Luo
Mark Lutz
Taras Lyapun
Jim Lynch
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -24,6 +24,9 @@
Library
-------

+- Issue #18676: Change 'positive' to 'non-negative' in queue.py put and get
+ docstrings and ValueError messages. Patch by Zhongyue Luo
+
- Fix refcounting issue with extension types in tkinter.

- Issue #8112: xlmrpc.server's DocXMLRPCServer server no longer raises an error
@@ -826,7 +829,7 @@
Build
-----

-- Issue #16067: Add description into MSI file to replace installer's
+- Issue #16067: Add description into MSI file to replace installer's
temporary name.

- Issue #18257: Fix readlink usage in python-config. Install the python

--
Repository URL: http://hg.python.org/cpython

Python checkins RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.