From 4fa7110957fb688503e4b81281b41e4f01dcc2b6 Mon Sep 17 00:00:00 2001 From: Ricky Atkins Date: Thu, 8 Aug 2024 10:25:52 +0100 Subject: [PATCH 1/2] Support multiple UFOs in a single invocation Incompatible with -o/--output Invalid/Non-existent paths are skipped, but won't cause the program to exit prematurely Add helper for tests to assert logs contain a string --- src/ufonormalizer/__init__.py | 52 ++++++++++++++++++++++------------- tests/test_ufonormalizer.py | 23 ++++++++++------ 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/ufonormalizer/__init__.py b/src/ufonormalizer/__init__.py index 2c8b5c5..cb4f133 100644 --- a/src/ufonormalizer/__init__.py +++ b/src/ufonormalizer/__init__.py @@ -12,6 +12,7 @@ from collections import OrderedDict from io import open import logging +import sys try: from ._version import __version__ @@ -46,7 +47,7 @@ def main(args=None): parser = argparse.ArgumentParser(description=description) parser.add_argument("input", help="Path to a UFO to normalize.", - nargs="?") + nargs="+") parser.add_argument("-t", "--test", help="Run the normalizer's internal tests.", action="store_true") @@ -85,16 +86,17 @@ def main(args=None): logLevel = "DEBUG" if args.verbose else "ERROR" if args.quiet else "INFO" logging.basicConfig(level=logLevel, format="%(message)s") - if args.input is None: - parser.error("No input path was specified.") - inputPath = os.path.normpath(args.input) - outputPath = args.output - onlyModified = not args.all - if not os.path.exists(inputPath): - parser.error(f'Input path does not exist: "{ inputPath }".') - if os.path.splitext(inputPath)[-1].lower() != ".ufo": - parser.error(f'Input path is not a UFO: "{ inputPath }".') + if len(args.input) > 1 and args.output: + parser.error("can't use -o/--output with multiple input UFOs") + elif len(args.input) == 1: + inputPath = os.path.normpath(args.input[0]) + inputPaths = [inputPath] + outputPaths = [args.output or inputPath] + else: + inputPaths = [os.path.normpath(inputPath) for inputPath in args.input] + outputPaths = inputPaths + onlyModified = not args.all if args.float_precision >= 0: floatPrecision = args.float_precision elif args.float_precision == -1: @@ -104,16 +106,28 @@ def main(args=None): writeModTimes = not args.no_mod_times + allSkipped = True message = 'Normalizing "%s".' - if not onlyModified: - message += " Processing all files." - log.info(message, os.path.basename(inputPath)) - start = time.time() - normalizeUFO(inputPath, outputPath=outputPath, onlyModified=onlyModified, - floatPrecision=floatPrecision, writeModTimes=writeModTimes) - runtime = time.time() - start - log.info("Normalization complete (%.4f seconds).", runtime) - + for inputPath, outputPath in zip(inputPaths, outputPaths): + if not os.path.exists(inputPath): + log.error(f'Skipping non-existent input path: "{ inputPath }".') + continue + if os.path.splitext(inputPath)[-1].lower() != ".ufo": + log.error(f'Skipping input path that isn\'t a UFO: "{ inputPath }".') + continue + allSkipped = False + + if not onlyModified: + message += " Processing all files." + log.info(message, os.path.basename(inputPath)) + start = time.time() + normalizeUFO(inputPath, outputPath=outputPath, onlyModified=onlyModified, + floatPrecision=floatPrecision, writeModTimes=writeModTimes) + runtime = time.time() - start + log.info("Normalization complete (%.4f seconds).", runtime) + + if allSkipped: + sys.exit(2) # --------- # Internals diff --git a/tests/test_ufonormalizer.py b/tests/test_ufonormalizer.py index b256e9f..49dbd39 100644 --- a/tests/test_ufonormalizer.py +++ b/tests/test_ufonormalizer.py @@ -33,6 +33,8 @@ from io import StringIO from tempfile import TemporaryDirectory +import ufonormalizer + GLIFFORMAT1 = '''\ @@ -196,6 +198,11 @@ def __init__(self, methodName): if not hasattr(self, "assertRaisesRegex"): self.assertRaisesRegex = self.assertRaisesRegexp + def assertLogsContain(self, logsContext, phrase): + self.assertTrue( + any(phrase in logLine for logLine in logsContext.output) + ) + def _test_normalizeGlyphsDirectoryNames(self, oldLayers, expectedLayers): directory = tempfile.mkdtemp() for _layerName, subDirectory in oldLayers: @@ -1413,23 +1420,21 @@ def test_main_no_path(self): with self.assertRaisesRegex(SystemExit, '2'): with redirect_stderr(stream): main([]) - self.assertTrue("No input path" in stream.getvalue()) + self.assertIn("the following arguments are required: input", stream.getvalue()) def test_main_input_does_not_exist(self): - stream = StringIO() - with self.assertRaisesRegex(SystemExit, '2'): - with redirect_stderr(stream): + with self.assertLogs(ufonormalizer.__name__, level="ERROR") as logs: + with self.assertRaisesRegex(SystemExit, '2'): main(['foobarbazquz']) - self.assertTrue("Input path does not exist" in stream.getvalue()) + self.assertLogsContain(logs, "Skipping non-existent input path") def test_main_input_not_ufo(self): # I use the path to the test module itself existing_not_ufo_file = os.path.realpath(__file__) - stream = StringIO() - with self.assertRaisesRegex(SystemExit, '2'): - with redirect_stderr(stream): + with self.assertLogs(ufonormalizer.__name__, level="ERROR") as logs: + with self.assertRaisesRegex(SystemExit, '2'): main([existing_not_ufo_file]) - self.assertTrue("Input path is not a UFO" in stream.getvalue()) + self.assertLogsContain(logs, "Skipping input path that isn't a UFO") def test_main_invalid_float_precision(self): stream = StringIO() From a0c951fbbf14763fb7e67ea21e741171c3d93b2d Mon Sep 17 00:00:00 2001 From: Ricky Atkins Date: Thu, 8 Aug 2024 11:48:40 +0100 Subject: [PATCH 2/2] Test incompatibility of -o/--output with multiple inputs --- tests/test_ufonormalizer.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_ufonormalizer.py b/tests/test_ufonormalizer.py index 49dbd39..b8ad8f1 100644 --- a/tests/test_ufonormalizer.py +++ b/tests/test_ufonormalizer.py @@ -1436,6 +1436,13 @@ def test_main_input_not_ufo(self): main([existing_not_ufo_file]) self.assertLogsContain(logs, "Skipping input path that isn't a UFO") + def test_main_multiple_inputs_and_output(self): + stream = StringIO() + with self.assertRaisesRegex(SystemExit, '2'): + with redirect_stderr(stream): + main(['--output', 'foo', 'bar', 'baz']) + self.assertIn("can't use -o/--output with multiple input UFOs", stream.getvalue()) + def test_main_invalid_float_precision(self): stream = StringIO() with TemporaryDirectory(suffix=".ufo") as tmp: