Skip to content

Commit

Permalink
Correctly identify offending file in multi-decoding error messages, a…
Browse files Browse the repository at this point in the history
…nd use Exception.__cause__

Identify the offending file in ZXing CommandLineRunner output requires
parsing some really fugly Java tracebooks, but it addresses the FIXME from
e7974a9 "re-add add multi-decoding".

Also, we should use Exception.__cause__ rather than rolling our own
BarCodeReaderException.underlying.
  • Loading branch information
dlenski committed Oct 6, 2021
1 parent 6b2fa44 commit 561fa4b
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 23 deletions.
57 changes: 35 additions & 22 deletions zxing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,24 @@
import pathlib
import re
import subprocess as sp
import urllib.parse
import zipfile
from enum import Enum

from .version import __version__ # noqa: F401


def file_uri_to_path(s):
uri = urllib.parse.urlparse(s)
if (uri.scheme, uri.netloc, uri.query, uri.fragment) != ('file', '', '', ''):
raise ValueError(uri)
return urllib.parse.unquote_plus(uri.path)


class BarCodeReaderException(Exception):
def __init__(self, message, filename=None, underlying=None):
self.message, self.filename, self.underlying = message, filename, underlying
super().__init__(message, filename, underlying)
def __init__(self, message, filename=None):
self.message, self.filename = message, filename
super().__init__(message, filename)


class BarCodeReader(object):
Expand Down Expand Up @@ -65,35 +73,40 @@ def decode(self, filenames, try_harder=False, possible_formats=None, pure_barcod

try:
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.STDOUT, universal_newlines=False)
except FileNotFoundError as e:
raise BarCodeReaderException("Java binary specified (%s) does not exist" % self.java, self.java, e)
except PermissionError as e:
raise BarCodeReaderException("Java binary specified (%s) is not executable" % self.java, self.java, e)
except OSError as e:
raise BarCodeReaderException("Could not execute specified Java binary", self.java) from e
stdout, stderr = p.communicate()

if stdout.startswith((b'Error: Could not find or load main class com.google.zxing.client.j2se.CommandLineRunner',
b'Exception in thread "main" java.lang.NoClassDefFoundError:')):
raise BarCodeReaderException("Java JARs not found in classpath (%s)" % self.classpath, self.classpath)
elif stdout.startswith((b'''Exception in thread "main" javax.imageio.IIOException: Can't get input stream from URL!''',
b'''Exception in thread "main" java.util.concurrent.ExecutionException: javax.imageio.IIOException: Can't get input stream from URL!''')):
raise BarCodeReaderException("Could not find image path: %s" % filenames, filenames)
# Find the line that looks like: "Caused by: java.io.FileNotFoundException: $FILENAME ({No such file or directory,Permission denied,*)"
fn, err = next((map(bytes.decode, l[42:].rsplit(b' (', 1)) for l in stdout.splitlines() if l.startswith(b"Caused by: java.io.FileNotFoundException: ")), ('', ''))
if err == 'No such file or directory)':
err = FileNotFoundError(fn)
elif err == 'Permission denied)':
err = PermissionError(fn)
else:
err = OSError(err[:-1])
raise BarCodeReaderException("Java library could not read image", fn) from err
elif stdout.startswith(b'''Exception in thread "main" java.io.IOException: Could not load '''):
raise BarCodeReaderException("Java library could not read image; is it in a supported format?", filenames)
# First line ends with file:// URI
fn = file_uri_to_path(stdout.splitlines()[0][63:].decode())
raise BarCodeReaderException("Java library could not read image (is it in a supported format?)", fn)
elif stdout.startswith(b'''Exception '''):
raise BarCodeReaderException("Unknown Java exception: %s" % stdout)
raise BarCodeReaderException("Unknown Java exception", self.java) from RuntimeError(stdout)
elif p.returncode:
raise BarCodeReaderException("Unexpected Java subprocess return code %d" % p.returncode, self.java)

if p.returncode:
codes = [None for fn in filenames]
else:
file_results = []
for line in stdout.splitlines(True):
if line.startswith((b'file:///', b'Exception')):
file_results.append(line)
else:
file_results[-1] += line
codes = [BarCode.parse(result) for result in file_results]
raise BarCodeReaderException("Unexpected Java subprocess return code", self.java) from sp.CalledProcessError(p.returncode, cmd, stdout)

file_results = []
for line in stdout.splitlines(True):
if line.startswith((b'file:///', b'Exception')):
file_results.append(line)
else:
file_results[-1] += line
codes = [BarCode.parse(result) for result in file_results]

if one_file:
return codes[0]
Expand Down
2 changes: 1 addition & 1 deletion zxing/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def main():
try:
bc = bcr.decode(fn, try_harder=args.try_harder)
except BarCodeReaderException as e:
p.error(e.message + (('\n\t' + e.filename) if e.filename else ''))
p.error(e.message + ((': ' + e.filename) if e.filename else '') + (('\n\tCaused by: ' + repr(e.__cause__) if e.__cause__ else '')))
if args.csv:
wr.writerow((fn, bc.format, bc.type, bc.raw, bc.parsed) if bc else (fn, 'ERROR', None, None, None))
else:
Expand Down

0 comments on commit 561fa4b

Please sign in to comment.