[Python-bugs-list] [ python-Bugs-640230 ] Discovered typo in zlib test.

SourceForge.net noreply@sourceforge.net
Sun, 02 Feb 2003 19:22:47 -0800


Bugs item #640230, was opened at 2002-11-18 13:36
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=640230&group_id=5470

Category: None
Group: None
Status: Open
Resolution: None
>Priority: 7
Submitted By: Scott David Daniels (scott_daniels)
Assigned to: Nobody/Anonymous (nobody)
Summary: Discovered typo in zlib test.

Initial Comment:
In test_zlib.py (while chasing what appears to be a
documentation problem), I found a flaw in the code
that tests max_length limitted output from a 
decompression object test.

Starting at line 100, the existing code is:
...
bufs.append(deco.flush())
decomp2 = ''.join(buf)
if decomp2 != buf:
    print "max_length decompressobj failed"
else:
    print "max_length decompressobj succeeded"
...

This test will always succeed (''.join(str) == str).

What seems obviously meant is:
...
bufs.append(deco.flush())
decomp2 = ''.join(bufs)
if decomp2 != buf:
    print "max_length decompressobj failed"
else:
    print "max_length decompressobj succeeded"
...



----------------------------------------------------------------------

>Comment By: Tim Peters (tim_one)
Date: 2003-02-02 22:22

Message:
Logged In: YES 
user_id=31435

It looks like the problem with getting this reviewed is that 
no current developer understands the zlib code or its test.  
I bumped the priority in a sneaky attempt to attract more 
interest <wink>.  *If* nobody else bites soon, and I can 
make time, I'll assign it to myself.

Your efforts are appreciated!  Even if it doesn't seem like it.

----------------------------------------------------------------------

Comment By: Scott David Daniels (scott_daniels)
Date: 2003-01-31 22:33

Message:
Logged In: YES 
user_id=493818

I've attached a PyUnit-style replacement for test_zlib.py that 
tests this condition to Patch #678531,  which provides a
context 
diff updating zlibmodule.c to fix this problem.  Hope this
is enough 
to get the fix reviewed.


----------------------------------------------------------------------

Comment By: Scott David Daniels (scott_daniels)
Date: 2002-12-12 13:32

Message:
Logged In: YES 
user_id=493818

The following replacement for PyZlib_unflush addresses some 
of the problem, but leaves unaddressed the issues of what 
unused_data and unconsumed_tail should be at the end of the 
routine.

static PyObject *
PyZlib_unflush(compobject *self, PyObject *args)
{
    int err, length = DEFAULTALLOC;
    PyObject * retval = NULL;
    unsigned long start_total_out;

    if (!PyArg_ParseTuple(args, "|i:flush", &length))
	return NULL;
    if (!(retval = PyString_FromStringAndSize(NULL, length)))
	return NULL;


    ENTER_ZLIB

    start_total_out = self->zst.total_out;
    self->zst.avail_out = length;
    self->zst.next_out = (Byte *)PyString_AS_STRING(retval);

    Py_BEGIN_ALLOW_THREADS
    err = inflate(&(self->zst), Z_FINISH);
    Py_END_ALLOW_THREADS

    /* while Z_OK and the output buffer is full, there might
be more output,
       so extend the output buffer and try again */
    while ((err == Z_OK || err == Z_BUF_ERROR) &&
self->zst.avail_out == 0) {
	if (_PyString_Resize(&retval, length << 1) < 0)
	    goto error;
	self->zst.next_out = (Byte *)PyString_AS_STRING(retval) +
length;
	self->zst.avail_out = length;
	length = length << 1;

	Py_BEGIN_ALLOW_THREADS
	err = inflate(&(self->zst), Z_FINISH);
	Py_END_ALLOW_THREADS
    }

    /* Maybe _always_ call inflateEnd if clearing
is_initialized */
    if (err == Z_STREAM_END) {
	err = inflateEnd(&(self->zst));
	if (err != Z_OK) {
	    zlib_error(self->zst, err, "from inflateEnd()");
	    Py_DECREF(retval);
	    retval = NULL;
	    goto error;
	}
    }
    self->is_initialised = 0;
    _PyString_Resize(&retval, self->zst.total_out -
start_total_out);
    /* ??? Here fix unused_data and unconsumed_tail */

error:

    LEAVE_ZLIB

    return retval;
}



----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2002-11-19 17:08

Message:
Logged In: YES 
user_id=31435

I'd say it's a bug in flush(), or in the docs, then.  They say "all 
pending input is processed, and a string containing the 
remaining uncompressed output is returned", and that's not 
happening here.  That's pretty clearly what the author of this 
test *expected* to happen, too (the original bug you 
discovered looked much more like a typo than a failure to 
understand the module interface).

----------------------------------------------------------------------

Comment By: Scott David Daniels (scott_daniels)
Date: 2002-11-19 16:40

Message:
Logged In: YES 
user_id=493818

OK, The reason the code fails after the fix is as follows:
deco.flush() does not return any output.
The loop control says, "while we have more unexamined 
source."

However, the decompressor can consume all of the input 
before it has provided all of its output.  So, there are two 
possible fixes:

1) Minimal change:  Change the loop control to say, in 
effect, "While we have more input or are producing output.":
Around line 92:
Change:
    bufs = []
    while cb:
        max_length = 1 + len(cb)/10
To:
    bufs = []
    while cb or chunk:
        max_length = 1 + len(cb)/10


2) More reasonable(?) change:  After the insertion loop, (just 
before the flush) extract all remaining output:
Around line 99:
        cb = deco.unconsumed_tail
    bufs.append(deco.flush())
    decomp2 = ''.join(bufs)
Becomes:
        cb = deco.unconsumed_tail
    while bufs[-1]:
        bufs.append(deco.decompress('', max_length))
    bufs.append(deco.flush())
    decomp2 = ''.join(bufs)


---
Note, in any case, the
    bufs.append(deco.flush())
originally on line 100 _always_ appends a zero-length string.
I would suggest changing it to simply:
    deco.flush()

    

----------------------------------------------------------------------

Comment By: Michael Hudson (mwh)
Date: 2002-11-19 04:50

Message:
Logged In: YES 
user_id=6656

Assuming Tim meant what he said, not what he did: raising
priority.

----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2002-11-18 15:56

Message:
Logged In: YES 
user_id=31435

Good eye!  Unfortunately, when I repair that, the test fails 
here -- decomp2 is a proper prefix of buf then.  It's "too 
short".  Raised priority but left unassigned (someone who 
understands zlib better will have to jump in).

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=640230&group_id=5470