From ba6329bbe14c832d42773dee2fe30bd7669ca255 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 2 Mar 2023 08:58:29 +0100 Subject: various minor fixes --- .pylintrc | 17 +++++++-- bsie/apps/__init__.py | 2 +- bsie/apps/_loader.py | 4 +-- bsie/extractor/image/photometrics.py | 37 +++++++++----------- doc/source/installation.rst | 2 ++ test/apps/test_main.py | 57 +++++++++++++++++++++++++++++++ test/extractor/image/test_photometrics.py | 10 ++++++ test/reader/preview/test_pg.py | 1 + 8 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 test/apps/test_main.py diff --git a/.pylintrc b/.pylintrc index 1b34854..576e81a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -145,6 +145,21 @@ logging-format-style=old +[MESSAGES CONTROL] + +# disable similarities check +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + duplicate-code + + + [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. @@ -188,6 +203,4 @@ ignore-none=no callbacks=clbk,callback - - # Disable: R1735 (use-dict-literal) diff --git a/bsie/apps/__init__.py b/bsie/apps/__init__.py index cec8f84..2fe4795 100644 --- a/bsie/apps/__init__.py +++ b/bsie/apps/__init__.py @@ -33,7 +33,7 @@ def main(argv=None): parser = argparse.ArgumentParser(description=main.__doc__, prog='bsie') # version parser.add_argument('--version', action='version', - version='%(prog)s version {}.{}.{}'.format(*bsie.version_info)) + version='%(prog)s version {}.{}.{}'.format(*bsie.version_info)) # pylint: disable=C0209 # application selection parser.add_argument('app', choices=apps.keys(), help='Select the application to run.') diff --git a/bsie/apps/_loader.py b/bsie/apps/_loader.py index 36dd8a6..6411f10 100644 --- a/bsie/apps/_loader.py +++ b/bsie/apps/_loader.py @@ -16,8 +16,8 @@ DEFAULT_CONFIG_FILE = 'default_config.yaml' # exports __all__: typing.Sequence[str] = ( - 'load', 'DEFAULT_CONFIG_FILE', + 'load_pipeline', ) @@ -26,7 +26,7 @@ __all__: typing.Sequence[str] = ( def load_pipeline(path: str) -> Pipeline: """Load a pipeline according to a config at *path*.""" # load config file - with open(path, 'rt') as ifile: + with open(path, 'rt', encoding='utf-8') as ifile: cfg = yaml.safe_load(ifile) # reader builder diff --git a/bsie/extractor/image/photometrics.py b/bsie/extractor/image/photometrics.py index 525f207..c5254ab 100644 --- a/bsie/extractor/image/photometrics.py +++ b/bsie/extractor/image/photometrics.py @@ -20,7 +20,7 @@ __all__: typing.Sequence[str] = ( def _gps_to_dec(coords: typing.Tuple[float, float, float]) -> float: """Convert GPS coordinates from exif to float.""" # unpack args - deg, min, sec = coords + deg, min, sec = coords # pylint: disable=redefined-builtin # min # convert to float deg = float(Fraction(deg)) min = float(Fraction(min)) @@ -29,9 +29,8 @@ def _gps_to_dec(coords: typing.Tuple[float, float, float]) -> float: if float(sec) > 0: # format is deg+min+sec return (float(deg) * 3600 + float(min) * 60 + float(sec)) / 3600 - else: - # format is deg+min - return float(deg) + float(min) / 60 + # format is deg+min + return float(deg) + float(min) / 60 class Exif(base.Extractor): @@ -124,20 +123,19 @@ class Exif(base.Extractor): # produce triple yield subject, pred, value - def _date(self, content: dict): # FIXME: Return type annotation - raise NotImplementedError() - #date_keys = ( - # 'Exif.Photo.DateTimeOriginal', - # 'Exif.Photo.DateTimeDigitized', - # 'Exif.Image.DateTime', - # ) - #for key in date_keys: - # if key in content: - # dt = content[key].value - # if dt.tzinfo is None: - # dt = dt.replace(tzinfo=ttime.NoTimeZone) - # return dt - #return None + #def _date(self, content: dict): # FIXME: Return type annotation + # date_keys = ( + # 'Exif.Photo.DateTimeOriginal', + # 'Exif.Photo.DateTimeDigitized', + # 'Exif.Image.DateTime', + # ) + # for key in date_keys: + # if key in content: + # dt = content[key].value + # if dt.tzinfo is None: + # dt = dt.replace(tzinfo=ttime.NoTimeZone) + # return dt + # return None ## photometrics @@ -189,8 +187,7 @@ class Exif(base.Extractor): if width is not None and height is not None and ori is not None: if ori <= 4: return 'landscape' if width >= height else 'portrait' - else: - return 'portrait' if width >= height else 'landscape' + return 'portrait' if width >= height else 'landscape' return None diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 42b1e4e..b634457 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -40,6 +40,8 @@ For development, you also need to install some additional dependencies:: # code style discipline pip install mypy coverage pylint + # external type annotations for pyyaml + pip install types-PyYAML # documentation pip install sphinx sphinx-copybutton furo diff --git a/test/apps/test_main.py b/test/apps/test_main.py new file mode 100644 index 0000000..a1d8a49 --- /dev/null +++ b/test/apps/test_main.py @@ -0,0 +1,57 @@ + +# standard imports +import contextlib +import io +import json +import os +import tempfile +import unittest +import yaml + +# objects to test +from bsie.apps import main + + +## code ## + +class TestMain(unittest.TestCase): + def setUp(self): + config = { + 'ReaderBuilder': {}, + 'ExtractorBuilder': [ + {'bsie.extractor.generic.stat.Stat': {}}, + {'bsie.extractor.generic.path.Path': {}}, + ] + } + # create config file + _, self.config_path = tempfile.mkstemp(prefix='bsie-test-', suffix='.yaml') + with open(self.config_path, 'wt') as cfile: + yaml.dump(config, cfile) + + def tearDown(self): + if os.path.exists(self.config_path): + os.unlink(self.config_path) + + def test_main(self): + # must at least pass an app + with contextlib.redirect_stderr(io.StringIO()): + self.assertRaises(SystemExit, main, []) + # app takes over + with contextlib.redirect_stderr(io.StringIO()): + self.assertRaises(SystemExit, main, ['info']) + outbuf = io.StringIO() + with contextlib.redirect_stdout(outbuf): + main(['info', '--config', self.config_path, 'predicates']) + self.assertEqual(set(outbuf.getvalue().strip().split('\n')), { + 'http://bsfs.ai/schema/Entity#filename', + 'http://bsfs.ai/schema/Entity#filesize', + 'http://bsfs.ai/schema/Predicate', + }) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/extractor/image/test_photometrics.py b/test/extractor/image/test_photometrics.py index 0e0261b..fb219e2 100644 --- a/test/extractor/image/test_photometrics.py +++ b/test/extractor/image/test_photometrics.py @@ -95,6 +95,16 @@ class TestExif(unittest.TestCase): self.assertSetEqual(set(ext.extract(node, content, {ext.schema.predicate(ns.bse.longitude)})), {(node, ext.schema.predicate(ns.bse.longitude), 7.875)}) + # check orientation label + self.assertSetEqual(set(ext.extract( + node, { + 'Exif.Photo.PixelXDimension': '4288', + 'Exif.Photo.PixelYDimension': '2848', + 'Exif.Image.Orientation': '5', + }, + {ext.schema.predicate(ns.bse.orientation_label)})), + {(node, ext.schema.predicate(ns.bse.orientation_label), 'portrait')}) + # can pass multiple principals self.assertSetEqual(set(ext.extract(node, content, { ext.schema.predicate(ns.bse.exposure), diff --git a/test/reader/preview/test_pg.py b/test/reader/preview/test_pg.py index 381344f..30095c5 100644 --- a/test/reader/preview/test_pg.py +++ b/test/reader/preview/test_pg.py @@ -52,6 +52,7 @@ class TestPreviewGeneratorReader(unittest.TestCase): self.assertEqual(sum(img.getdata()), 20258) # cleanup img.close() + del rdr # can define a cache dir pg_dir = tempfile.mkdtemp(prefix='bsie-test') -- cgit v1.2.3