Source code for fontbakery.fonts_profile

FontBakery CheckRunner is the driver of a fontbakery suite of checks.
import glob
import logging
import argparse
from dataclasses import dataclass
import os

from fontbakery.callable import (
    FontBakeryExpectedValue as ExpectedValue,
from fontbakery.profile import Profile
from fontbakery.errors import ValueValidationError

[docs]@dataclass class FileDescription: name: str extensions: list singular: str description: str
[docs]class FontsProfile(Profile): accepted_files = [ FileDescription( name="fonts", singular="font", extensions=[".otf", ".ttf"], description="OpenType binary", ), FileDescription( name="ufos", singular="ufo", extensions=[".ufo"], description="UFO source" ), FileDescription( name="designspaces", singular="designspace", extensions=[".designspace"], description="Designspace", ), FileDescription( name="glyphs_files", singular="glyphs_file", extensions=[".glyphs"], description="Glyphs source", ), FileDescription( name="readme_md", singular="readme_md", extensions=[""], description="Project's README markdown file", ), FileDescription( name="metadata_pb", singular="metadata_pb", extensions=["METADATA.pb"], description="Project's METADATA protobuf file", ), ]
[docs] def setup_argparse(self, argument_parser): """ Set up custom arguments needed for this profile. """ profile = self def get_files(pattern): if os.path.exists(pattern): # not a pattern return [pattern] files_to_check = [] # use glob.glob to accept *.ttf # Everything goes in for now, gets sorted in the Merge for fullpath in glob.glob(pattern): files_to_check.append(fullpath) return files_to_check class MergeAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if namespace.list_checks: # -L/--list-checks option was used; don't try to validate file # inputs because this option doesn't require them return for file_description in profile.accepted_files: setattr(namespace,, []) # flatten the 'values' list: [['a'], ['b']] => ['a', 'b'] target = [item for sublist in values for item in sublist] any_accepted = False for file in target: accepted = False for file_description in profile.accepted_files: if any( [ file.endswith(extension) for extension in file_description.extensions ] ): setattr( namespace,, getattr(namespace, + [file], ) accepted = True any_accepted = True if not accepted: "Skipping '{}' as it does not" " seem to be accepted by this profile.", file, ) if not any_accepted: raise ValueValidationError("No applicable files found") argument_parser.add_argument( "files", nargs="*", # allow no input files; needed for -L/--list-checks option type=get_files, action=MergeAction, help="file path(s) to check. Wildcards like *.ttf are allowed.", ) return tuple( for x in self.accepted_files)
[docs] def get_family_checks(self): family_checks = self.get_checks_by_dependencies("fonts") family_checks.extend(self.get_checks_by_dependencies("ttFonts")) return family_checks
@classmethod def _expected_values(cls): return { ExpectedValue(, default=[], description=f"A list of the {val.description} file paths to check", force=True, ) for val in cls.accepted_files } @classmethod def _iterargs(cls): return {val.singular: for val in cls.accepted_files}
[docs]def profile_factory(**kwds): profile = FontsProfile( iterargs=FontsProfile._iterargs(), derived_iterables={"ttFonts": ("ttFont", True)}, expected_values=FontsProfile._expected_values(), **kwds, ) return profile