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

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



Your message dated Tue, 5 Mar 2024 01:26:16 +0530
with message-id <20240304195616.z4ti62xg6awjzvah@office.mailbox.org>
and subject line Re: fontmake: FTBFS: dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13
has caused the Debian Bug report #1058388,
regarding fontmake: FTBFS: dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1058388: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1058388
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
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.

--- End Message ---
--- Begin Message ---
This was likely an issue with compreffor package. fontmake builds now as confirmed with salsa CI as well.

	https://salsa.debian.org/fonts-team/fontmake/-/jobs/5403600

Closing.

Best,
Nilesh

Attachment: signature.asc
Description: PGP signature


--- End Message ---

Reply to: