lincolnloop/python-qrcode — pull request #27 by jmgs (2013-04-01 20:26:14+00:00)
Title: Fixed scrambled modules for columns < 5, due to shift-by-one of column index

The variable col on method map_data is supposed to iterate over the columns from left to right descending by two, but skipping the seventh column (col==6), which contains a fixed pattern defined by the specification.

Looking at former revisions, the first versions of the code only shifted col==6, leaving cols 4 and 2 in place and an empty column at col==0.

This bug was fixed in a wrong way at commit c32c9e9 by modifying the iteration terminator, but the buggy result was that the index was still shifted, plus the loop executed an extra iteration, all filling columns 0 to 3 with garbage. (Ironically the bug fix says that it's impossible to reach column -1, but that's precisely what the code does)

Test example
------------

I discovered this bug while toying with a real event ticket (trying to replicate it). See the image:

http://imageshack.us/photo/my-images/255/ticketqrcode.jpg/

This QR code can be replicated, only after fixing the bug, with:

qr = qrcode.QRCode(
version = 3,
error_correction= qrcode.constants.ERROR_CORRECT_M,
box_size = 10,
border = 4,
)
qr.add_data('3FUwm9PeFwCt4fJGC+03d8apfDBTxr7MzrFytzv5')
qr.makeImpl(False, 3)
img = qr.make_image()

Where this bug probably came from
---------------------------------

This bug was probably caused by a misunderstanding of the way Python deals with loops. Note the difference between the following C and Python snippets:

/* C code */
int i;
for (i = 10; i > -1; i -= 2) {
if (i == 6)
i--;
printf ("%d ", i);
}
/* Output: 10 8 5 3 1 */

# Python code
for i in range(10, -1, -2):
if i == 6:
i -= 1
print i,
# Output: 10 8 5 4 2 0

Python rolls up the iterators before executing the loop, hence modifying the iterator doesn't affect the following iterations.