[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1058388: fontmake: FTBFS: dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13



Source: fontmake
Version: 2.4.1-2
Severity: serious
Justification: FTBFS
Tags: trixie sid ftbfs
User: lucas@debian.org
Usertags: ftbfs-20231212 ftbfs-trixie

Hi,

During a rebuild of all packages in sid, your package failed to build
on amd64.


Relevant part (hopefully):
> make[1]: Entering directory '/<<PKGBUILDDIR>>'
> dh_auto_test
> I: pybuild base:310: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build; python3.12 -m pytest tests
> ============================= test session starts ==============================
> platform linux -- Python 3.12.1, pytest-7.4.3, pluggy-1.3.0
> rootdir: /<<PKGBUILDDIR>>
> configfile: setup.cfg
> collected 52 items
> 
> tests/test_instantiator.py ............................                  [ 53%]
> tests/test_main.py FFFF.....FFFFF.FFFF..F..                              [100%]
> 
> =================================== FAILURES ===================================
> ______________________________ test_interpolation ______________________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07c0a930>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07c76360>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
> >               self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
> 
> fontmake/font_project.py:946: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:1006: in _run_from_designspace_static
>     ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
>     from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
> >               project.run_from_designspace(designspace_path, **args)
> 
> fontmake/__main__.py:501: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07c0a930>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07c76360>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
>                 self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
>             if interp_outputs:
>                 self._run_from_designspace_interpolatable(
>                     designspace,
>                     outputs=interp_outputs,
>                     feature_writers=feature_writers,
>                     filters=filters,
>                     **kwargs,
>                 )
>         except FontmakeError as e:
>             # Some called functions already added the Designspace file to the source
>             # trail.
>             if e.source_trail[-1] != designspace.path:
>                 e.source_trail.append(designspace.path)
>             raise
>         except Exception as e:
> >           raise FontmakeError(
>                 "Generating fonts from Designspace failed", designspace.path
>             ) from e
> E           fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/font_project.py:974: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation0')
> 
>     def test_interpolation(data_dir, tmp_path):
>         shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-m",
>                 str(tmp_path / "sources" / "DesignspaceTest.designspace"),
>                 "-i",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:14: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/__main__.py:523: SystemExit
> ________________________ test_interpolation_mutatormath ________________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07be5f40>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07b08770>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
> >               self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
> 
> fontmake/font_project.py:946: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:998: in _run_from_designspace_static
>     self.interpolate_instance_ufos_mutatormath(
> fontmake/font_project.py:832: in interpolate_instance_ufos_mutatormath
>     from glyphsLib.interpolation import apply_instance_data
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
> >               project.run_from_designspace(designspace_path, **args)
> 
> fontmake/__main__.py:501: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07be5f40>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07b08770>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
>                 self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
>             if interp_outputs:
>                 self._run_from_designspace_interpolatable(
>                     designspace,
>                     outputs=interp_outputs,
>                     feature_writers=feature_writers,
>                     filters=filters,
>                     **kwargs,
>                 )
>         except FontmakeError as e:
>             # Some called functions already added the Designspace file to the source
>             # trail.
>             if e.source_trail[-1] != designspace.path:
>                 e.source_trail.append(designspace.path)
>             raise
>         except Exception as e:
> >           raise FontmakeError(
>                 "Generating fonts from Designspace failed", designspace.path
>             ) from e
> E           fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/font_project.py:974: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0')
> 
>     def test_interpolation_mutatormath(data_dir, tmp_path):
>         shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-m",
>                 str(tmp_path / "sources" / "DesignspaceTest.designspace"),
>                 "-i",
>                 "--use-mutatormath",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:48: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/__main__.py:523: SystemExit
> _________________ test_interpolation_mutatormath_source_layer __________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07be4620>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d079576e0>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
> >               self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
> 
> fontmake/font_project.py:946: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:998: in _run_from_designspace_static
>     self.interpolate_instance_ufos_mutatormath(
> fontmake/font_project.py:832: in interpolate_instance_ufos_mutatormath
>     from glyphsLib.interpolation import apply_instance_data
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
> >               project.run_from_designspace(designspace_path, **args)
> 
> fontmake/__main__.py:501: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07be4620>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d079576e0>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
>                 self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
>             if interp_outputs:
>                 self._run_from_designspace_interpolatable(
>                     designspace,
>                     outputs=interp_outputs,
>                     feature_writers=feature_writers,
>                     filters=filters,
>                     **kwargs,
>                 )
>         except FontmakeError as e:
>             # Some called functions already added the Designspace file to the source
>             # trail.
>             if e.source_trail[-1] != designspace.path:
>                 e.source_trail.append(designspace.path)
>             raise
>         except Exception as e:
> >           raise FontmakeError(
>                 "Generating fonts from Designspace failed", designspace.path
>             ) from e
> E           fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/font_project.py:974: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1')
> 
>     def test_interpolation_mutatormath_source_layer(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "MutatorSans" / "MutatorSans.designspace",
>             tmp_path / "MutatorSans.designspace",
>         )
>     
>         with pytest.raises(SystemExit, match="sources with 'layer'"):
> >           fontmake.__main__.main(
>                 [
>                     "-m",
>                     str(tmp_path / "MutatorSans.designspace"),
>                     "-i",
>                     "--use-mutatormath",
>                     "--output-dir",
>                     str(tmp_path),
>                 ]
>             )
> 
> tests/test_main.py:87: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/__main__.py:523: SystemExit
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1')
> 
>     def test_interpolation_mutatormath_source_layer(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "MutatorSans" / "MutatorSans.designspace",
>             tmp_path / "MutatorSans.designspace",
>         )
>     
> >       with pytest.raises(SystemExit, match="sources with 'layer'"):
> E       AssertionError: Regex pattern did not match.
> E        Regex: "sources with 'layer'"
> E        Input: "fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'"
> 
> tests/test_main.py:86: AssertionError
> _________________ test_interpolation_and_masters_as_instances __________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d077633e0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = True
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077e4620>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
> >               self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
> 
> fontmake/font_project.py:946: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:1006: in _run_from_designspace_static
>     ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
>     from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
> >               project.run_from_designspace(designspace_path, **args)
> 
> fontmake/__main__.py:501: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d077633e0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = True
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077e4620>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
>                 self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
>             if interp_outputs:
>                 self._run_from_designspace_interpolatable(
>                     designspace,
>                     outputs=interp_outputs,
>                     feature_writers=feature_writers,
>                     filters=filters,
>                     **kwargs,
>                 )
>         except FontmakeError as e:
>             # Some called functions already added the Designspace file to the source
>             # trail.
>             if e.source_trail[-1] != designspace.path:
>                 e.source_trail.append(designspace.path)
>             raise
>         except Exception as e:
> >           raise FontmakeError(
>                 "Generating fonts from Designspace failed", designspace.path
>             ) from e
> E           fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/font_project.py:974: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0')
> 
>     def test_interpolation_and_masters_as_instances(data_dir, tmp_path):
>         shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-m",
>                 str(tmp_path / "sources" / "DesignspaceTest.designspace"),
>                 "-i",
>                 "-M",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:102: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/__main__.py:523: SystemExit
> ____________________________ test_ufo_interpolation ____________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_ufo_interpolation0')
> 
>     def test_ufo_interpolation(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
>         )
>     
>         instance_dir = tmp_path / "instance_ufos"
> >       fontmake.__main__.main(
>             [
>                 "-g",
>                 str(tmp_path / "GlyphsUnitTestSans.glyphs"),
>                 "--master-dir",
>                 str(tmp_path / "master_ufos"),
>                 "--instance-dir",
>                 str(instance_dir),
>                 "-i",
>                 "-o",
>                 "ufo",
>             ]
>         )
> 
> tests/test_main.py:256: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> _______________________ test_ufo_interpolation_specific ________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_ufo_interpolation_specifi0')
> 
>     def test_ufo_interpolation_specific(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
>         )
>     
>         instance_dir = tmp_path / "instance_ufos"
> >       fontmake.__main__.main(
>             [
>                 "-g",
>                 str(tmp_path / "GlyphsUnitTestSans.glyphs"),
>                 "--master-dir",
>                 str(tmp_path / "master_ufos"),
>                 "--instance-dir",
>                 str(instance_dir),
>                 "-i",
>                 r".*Light.*",
>                 "-o",
>                 "ufo",
>             ]
>         )
> 
> tests/test_main.py:288: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_subsetting[default] ___________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_subsetting_default_0')
> write_skipexportglyphs = True
> 
>     @pytest.mark.parametrize(
>         "write_skipexportglyphs",
>         [
>             pytest.param(True, id="default"),
>             pytest.param(False, id="no-write-skipexportglyphs"),
>         ],
>     )
>     def test_subsetting(data_dir, tmp_path, write_skipexportglyphs):
>         shutil.copyfile(data_dir / "TestSubset.glyphs", tmp_path / "TestSubset.glyphs")
>     
>         args = [
>             "-g",
>             str(tmp_path / "TestSubset.glyphs"),
>             "--master-dir",
>             str(tmp_path / "master_ufos"),
>             "--instance-dir",
>             str(tmp_path / "instance_ufos"),
>             "-i",
>             "Test Subset Regular",
>             "-o",
>             "ttf",
>             "otf",
>             "--output-dir",
>             str(tmp_path),
>         ]
>         if not write_skipexportglyphs:
>             args.append("--no-write-skipexportglyphs")
>     
> >       fontmake.__main__.main(args)
> 
> tests/test_main.py:337: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> __________________ test_subsetting[no-write-skipexportglyphs] __________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_subsetting_no_write_skipe0')
> write_skipexportglyphs = False
> 
>     @pytest.mark.parametrize(
>         "write_skipexportglyphs",
>         [
>             pytest.param(True, id="default"),
>             pytest.param(False, id="no-write-skipexportglyphs"),
>         ],
>     )
>     def test_subsetting(data_dir, tmp_path, write_skipexportglyphs):
>         shutil.copyfile(data_dir / "TestSubset.glyphs", tmp_path / "TestSubset.glyphs")
>     
>         args = [
>             "-g",
>             str(tmp_path / "TestSubset.glyphs"),
>             "--master-dir",
>             str(tmp_path / "master_ufos"),
>             "--instance-dir",
>             str(tmp_path / "instance_ufos"),
>             "-i",
>             "Test Subset Regular",
>             "-o",
>             "ttf",
>             "otf",
>             "--output-dir",
>             str(tmp_path),
>         ]
>         if not write_skipexportglyphs:
>             args.append("--no-write-skipexportglyphs")
>     
> >       fontmake.__main__.main(args)
> 
> tests/test_main.py:337: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ________________________ test_shared_features_expansion ________________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d077a14c0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace'
> output = ['ttf'], interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = True
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077a1280>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
> >               self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
> 
> fontmake/font_project.py:946: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:1006: in _run_from_designspace_static
>     ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
>     from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
> >               project.run_from_designspace(designspace_path, **args)
> 
> fontmake/__main__.py:501: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d077a14c0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace'
> output = ['ttf'], interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = True
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077a1280>
> 
>     def run_from_designspace(
>         self,
>         designspace_path,
>         output=(),
>         interpolate=False,
>         masters_as_instances=False,
>         interpolate_binary_layout=False,
>         round_instances=False,
>         feature_writers=None,
>         filters=None,
>         expand_features_to_instances=False,
>         use_mutatormath=False,
>         **kwargs,
>     ):
>         """Run toolchain from a DesignSpace document to produce either static
>         instance fonts (ttf or otf), interpolatable or variable fonts.
>     
>         Args:
>             designspace_path: Path to designspace document.
>             interpolate: If True output all instance fonts, otherwise just
>                 masters. If the value is a string, only build instance(s) that
>                 match given name. The string is compiled into a regular
>                 expression and matched against the "name" attribute of
>                 designspace instances using `re.fullmatch`.
>             masters_as_instances: If True, output master fonts as instances.
>             interpolate_binary_layout: Interpolate layout tables from compiled
>                 master binaries.
>             round_instances: apply integer rounding when interpolating with
>                 MutatorMath.
>             kwargs: Arguments passed along to run_from_ufos.
>     
>         Raises:
>             TypeError: "variable" or "interpolatable" outputs are incompatible
>                 with arguments "interpolate", "masters_as_instances", and
>                 "interpolate_binary_layout".
>         """
>     
>         interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
>         static_outputs = set(output).difference(interp_outputs)
>         if interp_outputs:
>             for argname in (
>                 "interpolate",
>                 "masters_as_instances",
>                 "interpolate_binary_layout",
>             ):
>                 if locals()[argname]:
>                     raise TypeError(
>                         '"%s" argument incompatible with output %r'
>                         % (argname, ", ".join(sorted(interp_outputs)))
>                     )
>     
>         try:
>             designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
>         except Exception as e:
>             raise FontmakeError("Reading Designspace failed", designspace_path) from e
>     
>         # if no --feature-writers option was passed, check in the designspace's
>         # <lib> element if user supplied a custom featureWriters configuration;
>         # if so, use that for all the UFOs built from this designspace
>         if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
>             feature_writers = loadFeatureWriters(designspace)
>     
>         if filters is None and FILTERS_KEY in designspace.lib:
>             preFilters, postFilters = loadFilters(designspace)
>             filters = preFilters + postFilters
>     
>         try:
>             if static_outputs:
>                 self._run_from_designspace_static(
>                     designspace,
>                     outputs=static_outputs,
>                     interpolate=interpolate,
>                     masters_as_instances=masters_as_instances,
>                     interpolate_binary_layout=interpolate_binary_layout,
>                     round_instances=round_instances,
>                     feature_writers=feature_writers,
>                     expand_features_to_instances=expand_features_to_instances,
>                     use_mutatormath=use_mutatormath,
>                     filters=filters,
>                     **kwargs,
>                 )
>             if interp_outputs:
>                 self._run_from_designspace_interpolatable(
>                     designspace,
>                     outputs=interp_outputs,
>                     feature_writers=feature_writers,
>                     filters=filters,
>                     **kwargs,
>                 )
>         except FontmakeError as e:
>             # Some called functions already added the Designspace file to the source
>             # trail.
>             if e.source_trail[-1] != designspace.path:
>                 e.source_trail.append(designspace.path)
>             raise
>         except Exception as e:
> >           raise FontmakeError(
>                 "Generating fonts from Designspace failed", designspace.path
>             ) from e
> E           fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/font_project.py:974: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0')
> 
>     def test_shared_features_expansion(data_dir, tmp_path):
>         shutil.copytree(data_dir / "DesignspaceTestSharedFeatures", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-m",
>                 str(tmp_path / "sources" / "DesignspaceTestSharedFeatures.designspace"),
>                 "-i",
>                 "--expand-features-to-instances",
>                 "-o",
>                 "ttf",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:348: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
> 
> fontmake/__main__.py:523: SystemExit
> _______________________________ test_mti_sources _______________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_mti_sources0')
> 
>     def test_mti_sources(data_dir, tmp_path):
>         shutil.copytree(data_dir / "InterpolateLayoutTest", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-g",
>                 str(tmp_path / "sources" / "InterpolateLayoutTest.glyphs"),
>                 "--designspace-path",
>                 str(tmp_path / "InterpolateLayoutTest.designspace"),
>                 "--master-dir",
>                 str(tmp_path / "master_ufos"),
>                 "--instance-dir",
>                 str(tmp_path / "instance_ufos"),
>                 "--mti-source",
>                 str(tmp_path / "sources" / "InterpolateLayoutTest.plist"),
>                 "--no-production-names",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:391: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_interpolate_layout ____________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolate_layout0')
> 
>     def test_interpolate_layout(data_dir, tmp_path):
>         shutil.copytree(data_dir / "InterpolateLayoutTest", tmp_path / "sources")
>     
> >       fontmake.__main__.main(
>             [
>                 "-g",
>                 str(tmp_path / "sources" / "InterpolateLayoutTest.glyphs"),
>                 "--designspace-path",
>                 str(tmp_path / "InterpolateLayoutTest.designspace"),
>                 "--master-dir",
>                 str(tmp_path / "master_ufos"),
>                 "--instance-dir",
>                 str(tmp_path / "instance_ufos"),
>                 "--mti-source",
>                 str(tmp_path / "sources" / "InterpolateLayoutTest.plist"),
>                 "--no-production-names",
>                 "-o",
>                 "ttf",
>                 "--output-dir",
>                 str(tmp_path / "master_ttf"),
>             ]
>         )
> 
> tests/test_main.py:445: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> _________________________ test_write_skipexportglyphs __________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_write_skipexportglyphs0')
> 
>     def test_write_skipexportglyphs(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
>         )
>     
>         args = [
>             "-g",
>             str(tmp_path / "GlyphsUnitTestSans.glyphs"),
>             "--master-dir",
>             str(tmp_path / "master_ufos"),
>             "-o",
>             "ufo",
>         ]
> >       fontmake.__main__.main(args)
> 
> tests/test_main.py:572: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_debug_feature_file ____________________________
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_debug_feature_file0')
> 
>     def test_debug_feature_file(data_dir, tmp_path):
>         shutil.copyfile(
>             data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
>         )
>     
>         debug_feature_path = data_dir / "test.fea"
>     
> >       fontmake.__main__.main(
>             [
>                 "-g",
>                 str(tmp_path / "GlyphsUnitTestSans.glyphs"),
>                 "--master-dir",
>                 "{tmp}",
>                 "--instance-dir",
>                 "{tmp}",
>                 "-i",
>                 "-o",
>                 "ttf",
>                 "--debug-feature-file",
>                 str(debug_feature_path),
>             ]
>         )
> 
> tests/test_main.py:610: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/__main__.py:482: in main
>     project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
>     designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
>     import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
>     from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
>     import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> >   from .parser import load, loads, ParseError
> E   ModuleNotFoundError: No module named 'openstep_plist.parser'
> 
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________ test_static_otf_compreffor_subroutinizer ___________________
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07000800>
> ufos = [<ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>]
> ttf = False, debugFeatureFile = None
> kwargs = {'cffVersion': 1, 'cubicConversionError': None, 'featureWriters': None, 'filters': None, ...}
> options = {'cffVersion': 1, 'featureWriters': None, 'filters': None, 'inplace': True, ...}
> key = 'flattenComponents'
> compile_func = <function compileOTF at 0x7f3d07bd5440>, fmt = 'OTF'
> writeFontName = False
> ufo = <ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>
> name = 'MyFont-Light'
> 
>     def _iter_compile(self, ufos, ttf=False, debugFeatureFile=None, **kwargs):
>         # generator function that calls ufo2ft compiler for each ufo and
>         # yields ttFont instances
>         options = dict(kwargs)
>         if ttf:
>             for key in ("optimizeCFF", "roundTolerance", "subroutinizer", "cffVersion"):
>                 options.pop(key, None)
>             compile_func, fmt = ufo2ft.compileTTF, "TTF"
>         else:
>             for key in (
>                 "cubicConversionError",
>                 "reverseDirection",
>                 "flattenComponents",
>             ):
>                 options.pop(key, None)
>             compile_func, fmt = ufo2ft.compileOTF, "OTF"
>     
>         writeFontName = len(ufos) > 1
>         for ufo in ufos:
>             name = self._font_name(ufo)
>             logger.info(f"Building {fmt} for {name}")
>     
>             if debugFeatureFile and writeFontName:
>                 debugFeatureFile.write(f"\n### {name} ###\n")
>     
>             try:
> >               yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options)
> 
> fontmake/font_project.py:383: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> /usr/lib/python3/dist-packages/ufo2ft/__init__.py:201: in compileOTF
>     return call_postprocessor(
> /usr/lib/python3/dist-packages/ufo2ft/__init__.py:88: in call_postprocessor
>     otf = postProcessor.process(**kwargs)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:98: in process
>     self.process_cff(
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:123: in process_cff
>     self._subroutinize(backend, self.otf, cffOutputVersion)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:331: in _subroutinize
>     subroutinize(otf, cffVersion)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:335: in _subroutinize_with_compreffor
>     from compreffor import compress
> /usr/lib/python3/dist-packages/compreffor/__init__.py:68: in <module>
>     from compreffor import cxxCompressor, pyCompressor
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
>     #!/usr/bin/env python
>     #
>     # Copyright 2015 Google Inc. All Rights Reserved.
>     #
>     # Licensed under the Apache License, Version 2.0 (the "License");
>     # you may not use this file except in compliance with the License.
>     # You may obtain a copy of the License at
>     #
>     #     http://www.apache.org/licenses/LICENSE-2.0
>     #
>     # Unless required by applicable law or agreed to in writing, software
>     # distributed under the License is distributed on an "AS IS" BASIS,
>     # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>     # See the License for the specific language governing permissions and
>     # limitations under the License.
>     
>     """
>     Tool to subroutinize a CFF OpenType font. Backed by a C++ binary.
>     
>     This file is a bootstrap for the C++ edition of the FontTools compreffor.
>     It prepares the input data for the extension and reads back in the results,
>     applying them to the input font.
>     
>     Usage (command line):
>     >> ./cxxCompressor.py /path/to/font.otf
>     # font written to /path/to/font.compressed.otf
>     
>     Usage (python):
>     >> font = TTFont("/path/to/font.otf")
>     >> cxxCompressor.compreff(font)
>     >> font.save("/path/to/output.otf")
>     """
>     
>     import array
>     from io import BytesIO
>     import struct
>     import logging
>     from compreffor.pyCompressor import (
>         Compreffor, CandidateSubr, tokenCost)
> >   from compreffor import _compreffor as lib, timer
> E   ImportError: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
> 
> /usr/lib/python3/dist-packages/compreffor/cxxCompressor.py:40: ImportError
> 
> The above exception was the direct cause of the following exception:
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
> >           project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
> 
> fontmake/__main__.py:516: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> fontmake/font_project.py:1095: in run_from_ufos
>     self.build_otfs(ufos, cff_version=cff_version, **kwargs)
> fontmake/font_project.py:223: in build_otfs
>     self.save_otfs(ufos, **kwargs)
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
>     return func(*args, **kwds)
> fontmake/font_project.py:521: in save_otfs
>     for font, ufo in zip(fonts, ufos):
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <fontmake.font_project.FontProject object at 0x7f3d07000800>
> ufos = [<ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>]
> ttf = False, debugFeatureFile = None
> kwargs = {'cffVersion': 1, 'cubicConversionError': None, 'featureWriters': None, 'filters': None, ...}
> options = {'cffVersion': 1, 'featureWriters': None, 'filters': None, 'inplace': True, ...}
> key = 'flattenComponents'
> compile_func = <function compileOTF at 0x7f3d07bd5440>, fmt = 'OTF'
> writeFontName = False
> ufo = <ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>
> name = 'MyFont-Light'
> 
>     def _iter_compile(self, ufos, ttf=False, debugFeatureFile=None, **kwargs):
>         # generator function that calls ufo2ft compiler for each ufo and
>         # yields ttFont instances
>         options = dict(kwargs)
>         if ttf:
>             for key in ("optimizeCFF", "roundTolerance", "subroutinizer", "cffVersion"):
>                 options.pop(key, None)
>             compile_func, fmt = ufo2ft.compileTTF, "TTF"
>         else:
>             for key in (
>                 "cubicConversionError",
>                 "reverseDirection",
>                 "flattenComponents",
>             ):
>                 options.pop(key, None)
>             compile_func, fmt = ufo2ft.compileOTF, "OTF"
>     
>         writeFontName = len(ufos) > 1
>         for ufo in ufos:
>             name = self._font_name(ufo)
>             logger.info(f"Building {fmt} for {name}")
>     
>             if debugFeatureFile and writeFontName:
>                 debugFeatureFile.write(f"\n### {name} ###\n")
>     
>             try:
>                 yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options)
>             except Exception as e:
> >               raise FontmakeError("Compiling UFO failed", ufo.path) from e
> E               fontmake.errors.FontmakeError: In 'tests/data/DesignspaceTest/MyFont-Light.ufo': Compiling UFO failed: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
> 
> fontmake/font_project.py:385: FontmakeError
> 
> During handling of the above exception, another exception occurred:
> 
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_static_otf_compreffor_sub0')
> 
>     def test_static_otf_compreffor_subroutinizer(data_dir, tmp_path):
> >       fontmake.__main__.main(
>             [
>                 "-u",
>                 str(data_dir / "DesignspaceTest" / "MyFont-Light.ufo"),
>                 "-o",
>                 "otf",
>                 "--subroutinizer",
>                 "compreffor",
>                 "--output-dir",
>                 str(tmp_path),
>             ]
>         )
> 
> tests/test_main.py:666: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> 
>     def main(args=None):
>         parser = ArgumentParser()
>         parser.add_argument("--version", action="version", version=__version__)
>         inputGroup = parser.add_argument_group(
>             title="Input arguments",
>             description="The following arguments are mutually exclusive (pick only one):",
>         )
>         xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
>         xInputGroup.add_argument(
>             "-g", "--glyphs-path", metavar="GLYPHS", help="Path to .glyphs source file"
>         )
>         xInputGroup.add_argument(
>             "-u",
>             "--ufo-paths",
>             nargs="+",
>             metavar="UFO",
>             help="One or more paths to UFO files",
>         )
>         xInputGroup.add_argument(
>             "-m",
>             "--mm-designspace",
>             metavar="DESIGNSPACE",
>             help="Path to .designspace file",
>         )
>     
>         outputGroup = parser.add_argument_group(title="Output arguments")
>         outputGroup.add_argument(
>             "-o",
>             "--output",
>             nargs="+",
>             default=("otf", "ttf"),
>             metavar="FORMAT",
>             help="Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
>             "(No file paths).",
>             choices=(
>                 "ufo",
>                 "otf",
>                 "otf-cff2",
>                 "ttf",
>                 "ttf-interpolatable",
>                 "otf-interpolatable",
>                 "variable",
>                 "variable-cff2",
>             ),
>         )
>         outputSubGroup = outputGroup.add_mutually_exclusive_group()
>         outputSubGroup.add_argument(
>             "--output-path",
>             default=None,
>             help="Output font file path. Only valid when the output is a single "
>             "file (e.g. input is a single UFO or output is variable font)",
>         )
>         outputSubGroup.add_argument(
>             "--output-dir",
>             default=None,
>             help="Output folder. By default, output folders are created in the "
>             "current working directory, grouping output fonts by format.",
>         )
>         outputGroup.add_argument(
>             "-i",
>             "--interpolate",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="INSTANCE_NAME",
>             help="Interpolate masters and generate all the instances defined. "
>             "To only interpolate a specific instance (or instances) that "
>             'match a given "name" attribute, you can pass as argument '
>             "the full instance name or a regular expression. "
>             'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
>             "(for Glyphs or MutatorMath sources only). ",
>         )
>         outputGroup.add_argument(
>             "--use-mutatormath",
>             action="store_true",
>             help=(
>                 "Use MutatorMath to generate instances (supports extrapolation and "
>                 "anisotropic locations)."
>             ),
>         )
>         outputGroup.add_argument(
>             "-M",
>             "--masters-as-instances",
>             action="store_true",
>             help="Output masters as instances",
>         )
>         outputGroup.add_argument(
>             "--family-name",
>             help="Family name to use for masters, and to filter output instances",
>         )
>         outputGroup.add_argument(
>             "--round-instances",
>             dest="round_instances",
>             action="store_true",
>             help="Apply integer rounding to all geometry when interpolating",
>         )
>         outputGroup.add_argument(
>             "--designspace-path",
>             default=None,
>             help="Path to output designspace file (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--master-dir",
>             default=None,
>             help='Directory where to write master UFO. Default: "./master_ufo". '
>             'If value is "{tmp}", a temporary directory is created and '
>             "removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--instance-dir",
>             default=None,
>             help="Directory where to write instance UFOs. Default: "
>             '"./instance_ufo". If value is "{tmp}", a temporary directory '
>             "is created and removed at the end (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--no-write-skipexportglyphs",
>             action="store_false",
>             dest="write_skipexportglyphs",
>             help="Do not store the glyph export flags in the 'public.skipExportGlyphs' "
>             "key of designspace/UFO lib, but use the old private glyph lib key "
>             "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
>         )
>         outputGroup.add_argument(
>             "--validate-ufo",
>             action="store_true",
>             help="Enable ufoLib validation on reading/writing UFO files. It is "
>             "disabled by default",
>         )
>         outputGroup.add_argument(
>             "--expand-features-to-instances",
>             action="store_true",
>             help="Resolves all include()s in the master feature file and writes "
>             "the full feature file to all instance UFOs. Only valid when "
>             "interpolating. Use if you share feature files of masters in "
>             "external files, as instances can end up elsewhere.",
>         )
>         outputGroup.add_argument(
>             "--no-generate-GDEF",
>             dest="generate_GDEF",
>             action="store_false",
>             help="Do not auto-generate a GDEF table, but keep an existing one intact.",
>         )
>     
>         contourGroup = parser.add_argument_group(title="Handling of contours")
>         contourGroup.add_argument(
>             "--keep-overlaps",
>             dest="remove_overlaps",
>             action="store_false",
>             help="Do not remove any overlap.",
>         )
>         contourGroup.add_argument(
>             "--overlaps-backend",
>             dest="overlaps_backend",
>             metavar="BACKEND",
>             choices=("booleanOperations", "pathops"),
>             default="booleanOperations",
>             help="Select library to remove overlaps. Choose between: %(choices)s "
>             "(default: %(default)s)",
>         )
>         contourGroup.add_argument(
>             "--keep-direction",
>             dest="reverse_direction",
>             action="store_false",
>             help="Do not reverse contour direction when output is ttf or "
>             "ttf-interpolatable",
>         )
>         contourGroup.add_argument(
>             "-e",
>             "--conversion-error",
>             type=float,
>             default=None,
>             metavar="ERROR",
>             help="Maximum approximation error for cubic to quadratic conversion "
>             "measured in EM",
>         )
>         contourGroup.add_argument(
>             "-f",
>             "--flatten-components",
>             dest="flatten_components",
>             action="store_true",
>             help="Flatten nested components to single level.",
>         )
>         contourGroup.add_argument(
>             "-a",
>             "--autohint",
>             nargs="?",
>             const="",
>             help="Run ttfautohint. Can provide arguments, quoted",
>         )
>         contourGroup.add_argument(
>             "--cff-round-tolerance",
>             type=float,
>             default=None,
>             metavar="FLOAT",
>             help="Restrict rounding of point coordinates in CFF table to only "
>             "those floats whose absolute difference from their integral part "
>             "is less than or equal to the tolerance. By default, all floats "
>             "are rounded to integer (tolerance 0.5); 0 disables rounding.",
>         )
>         contourGroup.add_argument(
>             "--optimize-cff",
>             type=lambda s: CFFOptimization(int(s)),
>             default=CFFOptimization.SUBROUTINIZE,
>             help="0 disables all optimizations; 1 specializes the CFF charstring "
>             "operators; 2 (default) also enables subroutinization",
>         )
>         contourGroup.add_argument(
>             "--subroutinizer",
>             default=None,
>             choices=["compreffor", "cffsubr"],
>             help="name of the library to use for compressing CFF charstrings. "
>             "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
>             "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
>         )
>         contourGroup.add_argument(
>             "--no-optimize-gvar",
>             dest="optimize_gvar",
>             action="store_false",
>             help="Do not perform IUP optimization on variable font's 'gvar' table. "
>             "(only works with 'variable' TrueType-flavored output)",
>         )
>         contourGroup.add_argument(
>             "--filter",
>             metavar="CLASS",
>             action="append",
>             dest="filter_specs",
>             help="string specifying a filter class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each filter class. The option overrides the filters specified "
>             "in the UFO lib.",
>         )
>     
>         layoutGroup = parser.add_argument_group(title="Handling of OpenType Layout")
>         layoutGroup.add_argument(
>             "--interpolate-binary-layout",
>             nargs="?",
>             default=False,
>             const=True,
>             metavar="MASTER_DIR",
>             help="Interpolate layout tables from compiled master binaries. "
>             "Requires Glyphs or MutatorMath source.",
>         )
>         layoutGroup.add_argument(
>             "--feature-writer",
>             metavar="CLASS",
>             action="append",
>             dest="feature_writer_specs",
>             help="string specifying a feature writer class to load, either "
>             "built-in or from an external module, optionally initialized with "
>             "the given keyword arguments. The class and module names are "
>             "separated by '::'. The option can be repeated multiple times "
>             "for each writer class. A special value of 'None' will disable "
>             "all automatic feature generation. The option overrides both the "
>             "default ufo2ft writers and those specified in the UFO lib.",
>         )
>         layoutGroup.add_argument(
>             "--debug-feature-file",
>             metavar="FILE",
>             type=FileType("w", encoding="utf-8"),
>             default=None,
>             help=(
>                 "Path were to dump OpenType features text to debug auto-generated "
>                 "features (kern, mark, mkmk, etc.)."
>             ),
>         )
>     
>         feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
>         feaCompilerGroup.add_argument(
>             "--mti-source",
>             help="mtiLib feature definition .plist file path (use instead of FEA)",
>         )
>     
>         glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
>         glyphnamesGroup.add_argument(
>             "--production-names",
>             dest="use_production_names",
>             action="store_true",
>             help="Rename glyphs with production names if available otherwise use "
>             "uninames.",
>         )
>         glyphnamesGroup.add_argument(
>             "--no-production-names", dest="use_production_names", action="store_false"
>         )
>     
>         subsetGroup = parser.add_mutually_exclusive_group(required=False)
>         subsetGroup.add_argument(
>             "--subset",
>             dest="subset",
>             action="store_true",
>             help="Subset font using export flags set by glyphsLib",
>         )
>         subsetGroup.add_argument("--no-subset", dest="subset", action="store_false")
>     
>         subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
>         subroutinizeGroup.add_argument(
>             "-s",
>             "--subroutinize",
>             action="store_true",
>             help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
>             "--optimize-cff option instead]",
>         )
>         subroutinizeGroup.add_argument(
>             "-S", "--no-subroutinize", dest="subroutinize", action="store_false"
>         )
>     
>         parser.set_defaults(use_production_names=None, subset=None, subroutinize=None)
>     
>         logGroup = parser.add_argument_group(title="Logging arguments")
>         logGroup.add_argument(
>             "--timing", action="store_true", help="Print the elapsed time for each steps"
>         )
>         logGroup.add_argument(
>             "--verbose",
>             default="INFO",
>             metavar="LEVEL",
>             choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
>             help="Configure the logger verbosity level. Choose between: "
>             "%(choices)s. Default: INFO",
>         )
>         args = vars(parser.parse_args(args))
>     
>         specs = args.pop("feature_writer_specs")
>         if specs is not None:
>             args["feature_writers"] = _loadFeatureWriters(parser, specs)
>     
>         specs = args.pop("filter_specs")
>         if specs is not None:
>             args["filters"] = _loadFilters(parser, specs)
>     
>         glyphs_path = args.pop("glyphs_path")
>         ufo_paths = args.pop("ufo_paths")
>         designspace_path = args.pop("mm_designspace")
>         input_format = (
>             "Glyphs" if glyphs_path else "designspace" if designspace_path else "UFO"
>         ) + " source"
>     
>         if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
>             if not (glyphs_path or designspace_path):
>                 parser.error("Glyphs or designspace source required for variable font")
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "masters_as_instances",
>                     "interpolate_binary_layout",
>                     "use_mutatormath",
>                 ],
>                 "variable output",
>             )
>         else:
>             exclude_args(parser, args, ["optimize_gvar"], "static output", positive=False)
>     
>         if args.get("use_mutatormath"):
>             for module in ("defcon", "mutatorMath"):
>                 try:
>                     __import__(module)
>                 except ImportError:
>                     parser.error(
>                         f"{module} module not found; reinstall fontmake with the "
>                         "[mutatormath] extra"
>                     )
>     
>         PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
>         try:
>             project = FontProject(
>                 timing=args.pop("timing"),
>                 verbose=args.pop("verbose"),
>                 validate_ufo=args.pop("validate_ufo"),
>             )
>     
>             if glyphs_path:
>                 with _make_tempdirs(parser, args):
>                     project.run_from_glyphs(glyphs_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "family_name",
>                     "mti_source",
>                     "designspace_path",
>                     "master_dir",
>                     "instance_dir",
>                 ],
>                 input_format,
>             )
>             exclude_args(
>                 parser, args, ["write_skipexportglyphs"], input_format, positive=False
>             )
>             if designspace_path:
>                 project.run_from_designspace(designspace_path, **args)
>                 return
>     
>             exclude_args(
>                 parser,
>                 args,
>                 [
>                     "interpolate",
>                     "use_mutatormath",
>                     "interpolate_binary_layout",
>                     "round_instances",
>                     "expand_features_to_instances",
>                 ],
>                 input_format,
>             )
>             project.run_from_ufos(
>                 ufo_paths, is_instance=args.pop("masters_as_instances"), **args
>             )
>         except FontmakeError as e:
>             if PRINT_TRACEBACK:
>                 logging.exception(e)
>                 sys.exit(1)
> >           sys.exit(f"fontmake: Error: {str(e)}")
> E           SystemExit: fontmake: Error: In 'tests/data/DesignspaceTest/MyFont-Light.ufo': Compiling UFO failed: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
> 
> fontmake/__main__.py:523: SystemExit
> =============================== warnings summary ===============================
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
>   /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
>   /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/fs/opener/__init__.py:6
>   /usr/lib/python3/dist-packages/fs/opener/__init__.py:6: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs.opener')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350
>   /usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     declare_namespace(parent)
> 
> .pybuild/cpython3_3.12/build/tests/test_main.py: 19 warnings
>   /usr/lib/python3/dist-packages/ufo2ft/fontInfoData.py:108: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
>     t = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"]))
> 
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_variable_otf
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_no_interpolation
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_no_interpolation
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_ufo_to_static_otf_cff2
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_static_otf_cffsubr_subroutinizer
>   /usr/lib/python3/dist-packages/cffsubr/__init__.py:63: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
>     with path(__name__, TX_EXE) as tx_cli:
> 
> -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
> =========================== short test summary info ============================
> FAILED tests/test_main.py::test_interpolation - SystemExit: fontmake: Error: ...
> FAILED tests/test_main.py::test_interpolation_mutatormath - SystemExit: fontm...
> FAILED tests/test_main.py::test_interpolation_mutatormath_source_layer - Asse...
> FAILED tests/test_main.py::test_interpolation_and_masters_as_instances - Syst...
> FAILED tests/test_main.py::test_ufo_interpolation - ModuleNotFoundError: No m...
> FAILED tests/test_main.py::test_ufo_interpolation_specific - ModuleNotFoundEr...
> FAILED tests/test_main.py::test_subsetting[default] - ModuleNotFoundError: No...
> FAILED tests/test_main.py::test_subsetting[no-write-skipexportglyphs] - Modul...
> FAILED tests/test_main.py::test_shared_features_expansion - SystemExit: fontm...
> FAILED tests/test_main.py::test_mti_sources - ModuleNotFoundError: No module ...
> FAILED tests/test_main.py::test_interpolate_layout - ModuleNotFoundError: No ...
> FAILED tests/test_main.py::test_write_skipexportglyphs - ModuleNotFoundError:...
> FAILED tests/test_main.py::test_debug_feature_file - ModuleNotFoundError: No ...
> FAILED tests/test_main.py::test_static_otf_compreffor_subroutinizer - SystemE...
> ================== 14 failed, 38 passed, 28 warnings in 2.99s ==================
> E: pybuild pybuild:395: test: plugin distutils failed with: exit code=1: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build; python3.12 -m pytest tests
> I: pybuild base:310: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.11/build; python3.11 -m pytest tests
> ============================= test session starts ==============================
> platform linux -- Python 3.11.7, pytest-7.4.3, pluggy-1.3.0
> rootdir: /<<PKGBUILDDIR>>
> configfile: setup.cfg
> collected 52 items
> 
> tests/test_instantiator.py ............................                  [ 53%]
> tests/test_main.py ........................                              [100%]
> 
> =============================== warnings summary ===============================
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
>   /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
>   /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/fs/opener/__init__.py:6
>   /usr/lib/python3/dist-packages/fs/opener/__init__.py:6: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs.opener')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     __import__("pkg_resources").declare_namespace(__name__)  # type: ignore
> 
> ../../../../../../usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350
>   /usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
>   Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
>     declare_namespace(parent)
> 
> .pybuild/cpython3_3.11/build/tests/test_main.py: 14 warnings
>   /usr/lib/python3/dist-packages/cffsubr/__init__.py:63: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
>     with path(__name__, TX_EXE) as tx_cli:
> 
> .pybuild/cpython3_3.11/build/tests/test_main.py::test_ufo_interpolation
>   /usr/lib/python3/dist-packages/glyphsLib/glyphdata.py:108: DeprecationWarning: open_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
>     with open_binary("glyphsLib.data", "GlyphData.xml") as f1:
> 
> .pybuild/cpython3_3.11/build/tests/test_main.py::test_ufo_interpolation
>   /usr/lib/python3/dist-packages/glyphsLib/glyphdata.py:109: DeprecationWarning: open_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
>     with open_binary("glyphsLib.data", "GlyphData_Ideographs.xml") as f2:
> 
> -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
> ======================= 52 passed, 20 warnings in 2.85s ========================
> dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13


The full build log is available from:
http://qa-logs.debian.net/2023/12/12/fontmake_2.4.1-2_unstable.log

All bugs filed during this archive rebuild are listed at:
https://bugs.debian.org/cgi-bin/pkgreport.cgi?tag=ftbfs-20231212;users=lucas@debian.org
or:
https://udd.debian.org/bugs/?release=na&merged=ign&fnewerval=7&flastmodval=7&fusertag=only&fusertagtag=ftbfs-20231212&fusertaguser=lucas@debian.org&allbugs=1&cseverity=1&ctags=1&caffected=1#results

A list of current common problems and possible solutions is available at
http://wiki.debian.org/qa.debian.org/FTBFS . You're welcome to contribute!

If you reassign this bug to another package, please mark it as 'affects'-ing
this package. See https://www.debian.org/Bugs/server-control#affects

If you fail to reproduce this, please provide a build log and diff it with mine
so that we can identify if something relevant changed in the meantime.


Reply to: