lincolnloop / python-qrcode

Python QR Code image generator
https://pypi.python.org/pypi/qrcode
Other
4.36k stars 666 forks source link

Consider adding a setter to the `mask_pattern` attribute #191

Closed pricebenjamin closed 3 years ago

pricebenjamin commented 4 years ago

Modifying the mask_pattern of a previously instantiated QRCode object works as expected, so long as the user knows which values to assign.

>>> qr = qrcode.QRCode(version=1)
>>> for i in range(8):
...     qr.mask_pattern = i
...     qr.make()
...     qr.print_tty()
...     # Works as expected

However, assigning incorrect values can lead to strange issues.

Here's a case where we break an error message. Instead of raising the TypeError we wanted, a separate TypeError is raised (because pattern is an int, not a str).

>>> import qrcode
>>> qr = qrcode.QRCode(version=1)
>>> qr.mask_pattern = 8
>>> qr.make()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 97, in make
    self.makeImpl(False, self.mask_pattern)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 124, in makeImpl
    self.map_data(self.data_cache, mask_pattern)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 382, in map_data
    mask_func = util.mask_func(mask_pattern)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/util.py", line 151, in mask_func
    raise TypeError("Bad mask pattern: " + pattern)  # pragma: no cover
TypeError: can only concatenate str (not "int") to str

Worse than that, we can hang the program:

>>> qr = qrcode.QRCode(version=1)
>>> qr.mask_pattern = -1
>>> qr.make()
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 97, in make
    self.makeImpl(False, self.mask_pattern)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 116, in makeImpl
    self.setup_type_info(test, mask_pattern)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/main.py", line 347, in setup_type_info
    bits = util.BCH_type_info(data)
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/util.py", line 106, in BCH_type_info
    while BCH_digit(d) - BCH_digit(G15) >= 0:
  File "/home/ben/miniconda3/envs/qr/lib/python3.7/site-packages/qrcode/util.py", line 121, in BCH_digit
    while data != 0:
KeyboardInterrupt

It's clear that parts of this library were written under the assumption that this value would not change after instantiation. If that's the case, perhaps this attribute could be made read-only. Otherwise, a simple setter could be used to verify the value before assignment. (Bonus: the _check_mask_pattern function already exists!)