From 042a682eeeeff561fc552e5768002d13a9d5c5d5 Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Tue, 5 Oct 2021 17:59:07 -0700 Subject: [PATCH] If no barcode is detected, return a False-y BarCode object instead of None This should be better for principle of lease surprise, and will address the underlying issue of #18. Also adds tests to verify that this works. --- README.md | 3 ++- test/barcodes/empty.png | Bin 0 -> 71 bytes test/test_all.py | 16 +++++++++++----- zxing/__init__.py | 7 +++++-- zxing/__main__.py | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 test/barcodes/empty.png diff --git a/README.md b/README.md index a3adab2..1578cbd 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ The attributes of the decoded `BarCode` object are `raw`, `parsed`, `path`, `for [here](https://zxing.github.io/zxing/apidocs/com/google/zxing/BarcodeFormat.html). The `decode()` method accepts an image path (or list of paths) and takes optional parameters `try_harder` (boolean), `possible_formats` (list of formats to consider), and `pure_barcode` (boolean). -If no barcode is found, it returns `None`, and if it encounters any other recognizable error from the Java ZXing library, it raises `BarCodeReaderException`. +If no barcode is found, it returns a `False`-y `BarCode` object with all fields except `uri` set to `None`. +If it encounters any other recognizable error from the Java ZXing library, it raises `BarCodeReaderException`. ## Command-line interface diff --git a/test/barcodes/empty.png b/test/barcodes/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..7b8b70909e9465e30d3b285bbf33e375835abaff GIT binary patch literal 71 zcmeAS@N?(olH%oJU|`_&^l%9R(u^R?!3HGdMUQlXB)B|X978JRB!i?_85nuFbhiM7 Q89ZJ6T-G@yH1To)0A_^?Gynhq literal 0 HcmV?d00001 diff --git a/test/test_all.py b/test/test_all.py index d6d6701..8a73d32 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -20,6 +20,12 @@ ('QR_CODE-screen_scraping_torture_test.png', 'QR_CODE', '\n\\n¡Atención ☹! UTF-8 characters,\n\r embedded newlines,\r &&am&p;& trailing whitespace\t \r '), ] +test_non_barcodes = [ + ('empty.png', None, None), +] + +test_valid_images = test_barcodes + test_non_barcodes + test_reader = None @@ -43,7 +49,7 @@ def _check_decoding(filename, expected_format, expected_raw, extra={}): logging.debug('Trying to parse {}, expecting {!r}.'.format(path, expected_raw)) dec = test_reader.decode(path, pure_barcode=True, **extra) if expected_raw is None: - if dec is not None: + if dec.raw is not None: raise AssertionError('Expected failure, but got result in {} format'.format(expected_format, dec.format)) else: if dec.raw != expected_raw: @@ -54,7 +60,7 @@ def _check_decoding(filename, expected_format, expected_raw, extra={}): def test_decoding(): global test_reader - yield from ((_check_decoding, filename, expected_format, expected_raw) for filename, expected_format, expected_raw in test_barcodes) + yield from ((_check_decoding, filename, expected_format, expected_raw) for filename, expected_format, expected_raw in test_valid_images) def test_possible_formats(): @@ -64,9 +70,9 @@ def test_possible_formats(): @with_setup(setup_reader) def test_decoding_multiple(): - reader = zxing.BarCodeReader() - filenames = [os.path.join(test_barcode_dir, filename) for filename, expected_format, expected_raw in test_barcodes] - for dec, (filename, expected_format, expected_raw) in zip(reader.decode(filenames, pure_barcode=True), test_barcodes): + global test_reader + filenames = [os.path.join(test_barcode_dir, filename) for filename, expected_format, expected_raw in test_valid_images] + for dec, (filename, expected_format, expected_raw) in zip(test_reader.decode(filenames, pure_barcode=True), test_valid_images): if dec.raw != expected_raw: raise AssertionError('{}: Expected {!r} but got {!r}'.format(filename, expected_raw, dec.parsed)) if dec.format != expected_format: diff --git a/zxing/__init__.py b/zxing/__init__.py index d872a0a..428f06f 100644 --- a/zxing/__init__.py +++ b/zxing/__init__.py @@ -135,7 +135,7 @@ def parse(cls, zxing_output): for l in zxing_output.splitlines(True): if block == CLROutputBlock.UNKNOWN: if l.endswith(b': No barcode found\n'): - return None + return cls(l.rsplit(b':', 1)[0].decode(), None, None, None, None, None) m = re.match(rb"(\S+) \(format:\s*([^,]+),\s*type:\s*([^)]+)\)", l) if m: uri, format, type = m.group(1).decode(), m.group(2).decode(), m.group(3).decode() @@ -160,7 +160,10 @@ def parse(cls, zxing_output): parsed = parsed[:-1].decode() return cls(uri, format, type, raw, parsed, points) - def __init__(self, uri, format, type, raw, parsed, points): + def __bool__(self): + return bool(self.raw) + + def __init__(self, uri, format=None, type=None, raw=None, parsed=None, points=None): self.raw = raw self.parsed = parsed self.uri = uri diff --git a/zxing/__main__.py b/zxing/__main__.py index c337af8..37dc7d9 100644 --- a/zxing/__main__.py +++ b/zxing/__main__.py @@ -31,7 +31,7 @@ def main(): wr.writerow((fn, bc.format, bc.type, bc.raw, bc.parsed) if bc else (fn, 'ERROR', None, None, None)) else: print("%s\n%s" % (fn, '=' * len(fn))) - if bc is None: + if not bc: print(" ERROR: Failed to decode barcode.") else: print(" Decoded %s barcode in %s format." % (bc.type, bc.format))