Coverage for /usr/lib/python3/dist-packages/fontTools/varLib/errors.py: 40%
113 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1import textwrap
4class VarLibError(Exception):
5 """Base exception for the varLib module."""
8class VarLibValidationError(VarLibError):
9 """Raised when input data is invalid from varLib's point of view."""
12class VarLibMergeError(VarLibError):
13 """Raised when input data cannot be merged into a variable font."""
15 def __init__(self, merger=None, **kwargs):
16 self.merger = merger
17 if not kwargs:
18 kwargs = {}
19 if "stack" in kwargs:
20 self.stack = kwargs["stack"]
21 del kwargs["stack"]
22 else:
23 self.stack = []
24 self.cause = kwargs
26 @property
27 def reason(self):
28 return self.__doc__
30 def _master_name(self, ix):
31 if self.merger is not None:
32 ttf = self.merger.ttfs[ix]
33 if "name" in ttf and ttf["name"].getBestFullName():
34 return ttf["name"].getBestFullName()
35 elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
36 return ttf.reader.file.name
37 return f"master number {ix}"
39 @property
40 def offender(self):
41 if "expected" in self.cause and "got" in self.cause:
42 index = [x == self.cause["expected"] for x in self.cause["got"]].index(
43 False
44 )
45 master_name = self._master_name(index)
46 if "location" in self.cause:
47 master_name = f"{master_name} ({self.cause['location']})"
48 return index, master_name
49 return None, None
51 @property
52 def details(self):
53 if "expected" in self.cause and "got" in self.cause:
54 offender_index, offender = self.offender
55 got = self.cause["got"][offender_index]
56 return f"Expected to see {self.stack[0]}=={self.cause['expected']!r}, instead saw {got!r}\n"
57 return ""
59 def __str__(self):
60 offender_index, offender = self.offender
61 location = ""
62 if offender:
63 location = f"\n\nThe problem is likely to be in {offender}:\n"
64 context = "".join(reversed(self.stack))
65 basic = textwrap.fill(
66 f"Couldn't merge the fonts, because {self.reason}. "
67 f"This happened while performing the following operation: {context}",
68 width=78,
69 )
70 return "\n\n" + basic + location + self.details
73class ShouldBeConstant(VarLibMergeError):
74 """some values were different, but should have been the same"""
76 @property
77 def details(self):
78 basic_message = super().details
80 if self.stack[0] != ".FeatureCount" or self.merger is None:
81 return basic_message
83 assert self.stack[0] == ".FeatureCount"
84 offender_index, _ = self.offender
85 bad_ttf = self.merger.ttfs[offender_index]
86 good_ttf = next(
87 ttf
88 for ttf in self.merger.ttfs
89 if self.stack[-1] in ttf
90 and ttf[self.stack[-1]].table.FeatureList.FeatureCount
91 == self.cause["expected"]
92 )
94 good_features = [
95 x.FeatureTag
96 for x in good_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
97 ]
98 bad_features = [
99 x.FeatureTag
100 for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
101 ]
102 return basic_message + (
103 "\nIncompatible features between masters.\n"
104 f"Expected: {', '.join(good_features)}.\n"
105 f"Got: {', '.join(bad_features)}.\n"
106 )
109class FoundANone(VarLibMergeError):
110 """one of the values in a list was empty when it shouldn't have been"""
112 @property
113 def offender(self):
114 index = [x is None for x in self.cause["got"]].index(True)
115 return index, self._master_name(index)
117 @property
118 def details(self):
119 cause, stack = self.cause, self.stack
120 return f"{stack[0]}=={cause['got']}\n"
123class NotANone(VarLibMergeError):
124 """one of the values in a list was not empty when it should have been"""
126 @property
127 def offender(self):
128 index = [x is not None for x in self.cause["got"]].index(True)
129 return index, self._master_name(index)
131 @property
132 def details(self):
133 cause, stack = self.cause, self.stack
134 return f"{stack[0]}=={cause['got']}\n"
137class MismatchedTypes(VarLibMergeError):
138 """data had inconsistent types"""
141class LengthsDiffer(VarLibMergeError):
142 """a list of objects had inconsistent lengths"""
145class KeysDiffer(VarLibMergeError):
146 """a list of objects had different keys"""
149class InconsistentGlyphOrder(VarLibMergeError):
150 """the glyph order was inconsistent between masters"""
153class InconsistentExtensions(VarLibMergeError):
154 """the masters use extension lookups in inconsistent ways"""
157class UnsupportedFormat(VarLibMergeError):
158 """an OpenType subtable (%s) had a format I didn't expect"""
160 def __init__(self, merger=None, **kwargs):
161 super().__init__(merger, **kwargs)
162 if not self.stack:
163 self.stack = [".Format"]
165 @property
166 def reason(self):
167 s = self.__doc__ % self.cause["subtable"]
168 if "value" in self.cause:
169 s += f" ({self.cause['value']!r})"
170 return s
173class InconsistentFormats(UnsupportedFormat):
174 """an OpenType subtable (%s) had inconsistent formats between masters"""
177class VarLibCFFMergeError(VarLibError):
178 pass
181class VarLibCFFDictMergeError(VarLibCFFMergeError):
182 """Raised when a CFF PrivateDict cannot be merged."""
184 def __init__(self, key, value, values):
185 error_msg = (
186 f"For the Private Dict key '{key}', the default font value list:"
187 f"\n\t{value}\nhad a different number of values than a region font:"
188 )
189 for region_value in values:
190 error_msg += f"\n\t{region_value}"
191 self.args = (error_msg,)
194class VarLibCFFPointTypeMergeError(VarLibCFFMergeError):
195 """Raised when a CFF glyph cannot be merged because of point type differences."""
197 def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
198 error_msg = (
199 f"Glyph '{glyph_name}': '{point_type}' at point index {pt_index} in "
200 f"master index {m_index} differs from the default font point type "
201 f"'{default_type}'"
202 )
203 self.args = (error_msg,)
206class VarLibCFFHintTypeMergeError(VarLibCFFMergeError):
207 """Raised when a CFF glyph cannot be merged because of hint type differences."""
209 def __init__(self, hint_type, cmd_index, m_index, default_type, glyph_name):
210 error_msg = (
211 f"Glyph '{glyph_name}': '{hint_type}' at index {cmd_index} in "
212 f"master index {m_index} differs from the default font hint type "
213 f"'{default_type}'"
214 )
215 self.args = (error_msg,)
218class VariationModelError(VarLibError):
219 """Raised when a variation model is faulty."""