# Source code for fontTools.varLib.errors

    import textwrap

    class VarLibError(Exception):
        """Base exception for the varLib module."""

    class VarLibValidationError(VarLibError):
        """Raised when input data is invalid from varLib's point of view."""

    class VarLibMergeError(VarLibError):
        """Raised when input data cannot be merged into a variable font."""

        def __init__(self, merger=None, **kwargs):
            self.merger = merger
            if not kwargs:
                kwargs = {}
            if "stack" in kwargs:
                self.stack = kwargs["stack"]
                del kwargs["stack"]
            else:
                self.stack = []
            self.cause = kwargs

        @property
        def reason(self):
            return self.__doc__

        def _master_name(self, ix):
            if self.merger is not None:
                ttf = self.merger.ttfs[ix]
                if "name" in ttf and ttf["name"].getBestFullName():
                    return ttf["name"].getBestFullName()
                elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
                    return ttf.reader.file.name
            return f"master number {ix}"

        @property
        def offender(self):
            if "expected" in self.cause and "got" in self.cause:
                index = [x == self.cause["expected"] for x in self.cause["got"]].index(
                    False
                )
                master_name = self._master_name(index)
                if "location" in self.cause:
                    master_name = f"{master_name} ({self.cause['location']})"
                return index, master_name
            return None, None

        @property
        def details(self):
            if "expected" in self.cause and "got" in self.cause:
                offender_index, offender = self.offender
                got = self.cause["got"][offender_index]
                return f"Expected to see {self.stack[0]}=={self.cause['expected']!r}, instead saw {got!r}\n"
            return ""

        def __str__(self):
            offender_index, offender = self.offender
            location = ""
            if offender:
                location = f"\n\nThe problem is likely to be in {offender}:\n"
            context = "".join(reversed(self.stack))
            basic = textwrap.fill(
                f"Couldn't merge the fonts, because {self.reason}. "
                f"This happened while performing the following operation: {context}",
                width=78,
            )
            return "\n\n" + basic + location + self.details

    class ShouldBeConstant(VarLibMergeError):
        """some values were different, but should have been the same"""

        @property
        def details(self):
            basic_message = super().details

            if self.stack[0] != ".FeatureCount" or self.merger is None:
                return basic_message

            assert self.stack[0] == ".FeatureCount"
            offender_index, _ = self.offender
            bad_ttf = self.merger.ttfs[offender_index]
            good_ttf = next(
                ttf
                for ttf in self.merger.ttfs
                if self.stack[-1] in ttf
                and ttf[self.stack[-1]].table.FeatureList.FeatureCount
                == self.cause["expected"]
            )

            good_features = [
                x.FeatureTag
                for x in good_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
            ]
            bad_features = [
                x.FeatureTag
                for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
            ]
            return basic_message + (
                "\nIncompatible features between masters.\n"
                f"Expected: {', '.join(good_features)}.\n"
                f"Got: {', '.join(bad_features)}.\n"
            )

    class FoundANone(VarLibMergeError):
        """one of the values in a list was empty when it shouldn't have been"""

        @property
        def offender(self):
            index = [x is None for x in self.cause["got"]].index(True)
            return index, self._master_name(index)

        @property
        def details(self):
            cause, stack = self.cause, self.stack
            return f"{stack[0]}=={cause['got']}\n"

    class NotANone(VarLibMergeError):
        """one of the values in a list was not empty when it should have been"""

        @property
        def offender(self):
            index = [x is not None for x in self.cause["got"]].index(True)
            return index, self._master_name(index)

        @property
        def details(self):
            cause, stack = self.cause, self.stack
            return f"{stack[0]}=={cause['got']}\n"

    class MismatchedTypes(VarLibMergeError):
        """data had inconsistent types"""

    class LengthsDiffer(VarLibMergeError):
        """a list of objects had inconsistent lengths"""

    class KeysDiffer(VarLibMergeError):
        """a list of objects had different keys"""

    class InconsistentGlyphOrder(VarLibMergeError):
        """the glyph order was inconsistent between masters"""

    class InconsistentExtensions(VarLibMergeError):
        """the masters use extension lookups in inconsistent ways"""

    class UnsupportedFormat(VarLibMergeError):
        """an OpenType subtable (%s) had a format I didn't expect"""

        def __init__(self, merger=None, **kwargs):
            super().__init__(merger, **kwargs)
            if not self.stack:
                self.stack = [".Format"]

        @property
        def reason(self):
            s = self.__doc__ % self.cause["subtable"]
            if "value" in self.cause:
                s += f" ({self.cause['value']!r})"
            return s

    class InconsistentFormats(UnsupportedFormat):
        """an OpenType subtable (%s) had inconsistent formats between masters"""

    class VarLibCFFMergeError(VarLibError):
        pass

    class VarLibCFFDictMergeError(VarLibCFFMergeError):
        """Raised when a CFF PrivateDict cannot be merged."""

        def __init__(self, key, value, values):
            error_msg = (
                f"For the Private Dict key '{key}', the default font value list:"
                f"\n\t{value}\nhad a different number of values than a region font:"
            )
            for region_value in values:
                error_msg += f"\n\t{region_value}"
            self.args = (error_msg,)

    class VarLibCFFPointTypeMergeError(VarLibCFFMergeError):
        """Raised when a CFF glyph cannot be merged because of point type differences."""

        def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
            error_msg = (
                f"Glyph '{glyph_name}': '{point_type}' at point index {pt_index} in "
                f"master index {m_index} differs from the default font point type "
                f"'{default_type}'"
            )
            self.args = (error_msg,)

    class VarLibCFFHintTypeMergeError(VarLibCFFMergeError):
        """Raised when a CFF glyph cannot be merged because of hint type differences."""

        def __init__(self, hint_type, cmd_index, m_index, default_type, glyph_name):
            error_msg = (
                f"Glyph '{glyph_name}': '{hint_type}' at index {cmd_index} in "
                f"master index {m_index} differs from the default font hint type "
                f"'{default_type}'"
            )
            self.args = (error_msg,)

    class VariationModelError(VarLibError):
        """Raised when a variation model is faulty."""