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..b8ad8f1 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,28 @@ 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__) + with self.assertLogs(ufonormalizer.__name__, level="ERROR") as logs: + with self.assertRaisesRegex(SystemExit, '2'): + 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([existing_not_ufo_file]) - self.assertTrue("Input path is not a UFO" in stream.getvalue()) + 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()