diff --git a/CHANGES.rst b/CHANGES.rst index 26e6b6aa..f635bb0b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -42,6 +42,7 @@ WIP 8.x - **Added** ``GappedCircleModuleDrawer`` (PIL) to render QR code modules as non-contiguous circles. (BenwestGate in `#373`_) - **Added** ability to execute as a Python module: ``python -m qrcode --output qrcode.png "hello world"`` (stefansjs in `#400`_) +- **Added** ``--mask-pattern`` option to the ``qr`` command line script to select a specific mask pattern instead of the automatically chosen one. (ChrisJr404 in `#433`_) - **Removed** the hardcoded 'id' argument from SVG elements. The fixed element ID caused conflicts when embedding multiple QR codes in a single document. (m000 in `#385`_) - **Fixed** typos in code that used ``embeded`` instead of ``embedded``. For backwards compatibility, the misspelled parameter names are still accepted but now emit deprecation warnings. These deprecated parameter names will be removed in v9.0. (benjnicholls in `#349`_) - **Fixed** an issue where an `` NoReturn: help="The error correction level to use. Choices are L (7%), " "M (15%, default), Q (25%), and H (30%).", ) + parser.add_option( + "--mask-pattern", + type=int, + help="The mask pattern (0-7) to use. By default the pattern that " + "scores best under the standard penalty rules is chosen automatically.", + ) parser.add_option( "--ascii", help="Print as ascii even if stdout is piped.", action="store_true" ) @@ -103,10 +109,14 @@ def raise_error(msg: str) -> NoReturn: else: image_factory = None - qr = qrcode.QRCode( - error_correction=error_correction[opts.error_correction], - image_factory=image_factory, - ) + try: + qr = qrcode.QRCode( + error_correction=error_correction[opts.error_correction], + mask_pattern=opts.mask_pattern, + image_factory=image_factory, + ) + except (TypeError, ValueError) as e: + raise_error(str(e)) if args: data = args[0] diff --git a/qrcode/tests/test_script.py b/qrcode/tests/test_script.py index ab4d2903..8100d0d7 100644 --- a/qrcode/tests/test_script.py +++ b/qrcode/tests/test_script.py @@ -57,6 +57,26 @@ def test_optimize(): main(["testtext", "--optimize", "0"]) +@mock.patch("os.isatty", return_value=True) +@mock.patch("qrcode.QRCode") +def test_mask_pattern(mock_qrcode, mock_isatty): + main(["testtext", "--mask-pattern", "1"]) + assert mock_qrcode.call_args.kwargs["mask_pattern"] == 1 + + +@mock.patch("os.isatty", return_value=True) +@mock.patch("qrcode.QRCode") +def test_mask_pattern_default(mock_qrcode, mock_isatty): + main(["testtext"]) + assert mock_qrcode.call_args.kwargs["mask_pattern"] is None + + +def test_mask_pattern_out_of_range(capsys): + with pytest.raises(SystemExit): + main(["testtext", "--mask-pattern", "8"]) + assert "Mask pattern should be in range(8)" in capsys.readouterr()[1] + + def test_factory(): main(["testtext", "--factory", "svg"])