Skip to content

Commit

Permalink
If no barcode is detected, return a False-y BarCode object instead of…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
dlenski committed Oct 6, 2021
1 parent e941beb commit 042a682
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 9 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Binary file added test/barcodes/empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions test/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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:
Expand All @@ -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():
Expand All @@ -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:
Expand Down
7 changes: 5 additions & 2 deletions zxing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion zxing/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit 042a682

Please sign in to comment.