Merge pull request #103 from adrientetar/optimize

Some optimizations
This commit is contained in:
Cosimo Lupo 2017-11-17 12:39:14 +00:00 committed by GitHub
commit 37c4777fab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 81 deletions

View File

@ -43,6 +43,8 @@ __all__ = [
class GlifLibError(Exception): pass class GlifLibError(Exception): pass
class MissingPlistError(GlifLibError): pass
# --------- # ---------
# Constants # Constants
# --------- # ---------
@ -128,17 +130,17 @@ class GlyphSet(object):
Rebuild the contents dict by loading contents.plist. Rebuild the contents dict by loading contents.plist.
""" """
contentsPath = os.path.join(self.dirName, "contents.plist") contentsPath = os.path.join(self.dirName, "contents.plist")
if not os.path.exists(contentsPath): try:
contents = self._readPlist(contentsPath)
except MissingPlistError:
# missing, consider the glyphset empty. # missing, consider the glyphset empty.
contents = {} contents = {}
else:
contents = self._readPlist(contentsPath)
# validate the contents # validate the contents
invalidFormat = False invalidFormat = False
if not isinstance(contents, dict): if not isinstance(contents, dict):
invalidFormat = True invalidFormat = True
else: else:
for name, fileName in list(contents.items()): for name, fileName in contents.items():
if not isinstance(name, basestring): if not isinstance(name, basestring):
invalidFormat = True invalidFormat = True
if not isinstance(fileName, basestring): if not isinstance(fileName, basestring):
@ -179,14 +181,15 @@ class GlyphSet(object):
def readLayerInfo(self, info): def readLayerInfo(self, info):
path = os.path.join(self.dirName, LAYERINFO_FILENAME) path = os.path.join(self.dirName, LAYERINFO_FILENAME)
if not os.path.exists(path): try:
infoDict = self._readPlist(path)
except MissingPlistError:
return return
infoDict = self._readPlist(path)
if not isinstance(infoDict, dict): if not isinstance(infoDict, dict):
raise GlifLibError("layerinfo.plist is not properly formatted.") raise GlifLibError("layerinfo.plist is not properly formatted.")
infoDict = validateLayerInfoVersion3Data(infoDict) infoDict = validateLayerInfoVersion3Data(infoDict)
# populate the object # populate the object
for attr, value in list(infoDict.items()): for attr, value in infoDict.items():
try: try:
setattr(info, attr, value) setattr(info, attr, value)
except AttributeError: except AttributeError:
@ -197,7 +200,7 @@ class GlyphSet(object):
raise GlifLibError("layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersion) raise GlifLibError("layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersion)
# gather data # gather data
infoData = {} infoData = {}
for attr in list(layerInfoVersion3ValueData.keys()): for attr in layerInfoVersion3ValueData.keys():
if hasattr(info, attr): if hasattr(info, attr):
try: try:
value = getattr(info, attr) value = getattr(info, attr)
@ -242,10 +245,13 @@ class GlyphSet(object):
needRead = True needRead = True
if needRead: if needRead:
fileName = self.contents[glyphName] fileName = self.contents[glyphName]
if not os.path.exists(path): try:
raise KeyError(glyphName) with open(path, "rb") as f:
with open(path, "rb") as f: text = f.read()
text = f.read() except IOError as e:
if e.errno == 2: # aka. FileNotFoundError
raise KeyError(glyphName)
raise
self._glifCache[glyphName] = (text, os.path.getmtime(path)) self._glifCache[glyphName] = (text, os.path.getmtime(path))
return self._glifCache[glyphName][0] return self._glifCache[glyphName][0]
@ -413,7 +419,7 @@ class GlyphSet(object):
""" """
components = {} components = {}
if glyphNames is None: if glyphNames is None:
glyphNames = list(self.contents.keys()) glyphNames = self.contents.keys()
for glyphName in glyphNames: for glyphName in glyphNames:
text = self.getGLIF(glyphName) text = self.getGLIF(glyphName)
components[glyphName] = _fetchComponentBases(text) components[glyphName] = _fetchComponentBases(text)
@ -428,7 +434,7 @@ class GlyphSet(object):
""" """
images = {} images = {}
if glyphNames is None: if glyphNames is None:
glyphNames = list(self.contents.keys()) glyphNames = self.contents.keys()
for glyphName in glyphNames: for glyphName in glyphNames:
text = self.getGLIF(glyphName) text = self.getGLIF(glyphName)
images[glyphName] = _fetchImageFileName(text) images[glyphName] = _fetchImageFileName(text)
@ -441,7 +447,9 @@ class GlyphSet(object):
with open(path, "rb") as f: with open(path, "rb") as f:
data = readPlist(f) data = readPlist(f)
return data return data
except: except Exception as e:
if isinstance(e, IOError) and e.errno == 2:
raise MissingPlistError(path)
raise GlifLibError("The file %s could not be read." % path) raise GlifLibError("The file %s could not be read." % path)
@ -454,9 +462,9 @@ def glyphNameToFileName(glyphName, glyphSet):
Wrapper around the userNameToFileName function in filenames.py Wrapper around the userNameToFileName function in filenames.py
""" """
if glyphSet: if glyphSet:
existing = [name.lower() for name in list(glyphSet.contents.values())] existing = frozenset(name.lower() for name in glyphSet.contents.values())
else: else:
existing = [] existing = frozenset()
if not isinstance(glyphName, unicode): if not isinstance(glyphName, unicode):
try: try:
new = unicode(glyphName) new = unicode(glyphName)
@ -595,13 +603,13 @@ def _writeAdvance(glyphObject, writer):
if not isinstance(width, (int, float)): if not isinstance(width, (int, float)):
raise GlifLibError("width attribute must be int or float") raise GlifLibError("width attribute must be int or float")
if width == 0: if width == 0:
width = None width = None
height = getattr(glyphObject, "height", None) height = getattr(glyphObject, "height", None)
if height is not None: if height is not None:
if not isinstance(height, (int, float)): if not isinstance(height, (int, float)):
raise GlifLibError("height attribute must be int or float") raise GlifLibError("height attribute must be int or float")
if height == 0: if height == 0:
height = None height = None
if width is not None and height is not None: if width is not None and height is not None:
writer.simpletag("advance", width=repr(width), height=repr(height)) writer.simpletag("advance", width=repr(width), height=repr(height))
writer.newline() writer.newline()
@ -789,16 +797,13 @@ def validateLayerInfoVersion3Data(infoData):
a set range of possible values for an attribute, that the a set range of possible values for an attribute, that the
value is in the accepted range. value is in the accepted range.
""" """
validInfoData = {} for attr, value in infoData.items():
for attr, value in list(infoData.items()):
if attr not in layerInfoVersion3ValueData: if attr not in layerInfoVersion3ValueData:
raise GlifLibError("Unknown attribute %s." % attr) raise GlifLibError("Unknown attribute %s." % attr)
isValidValue = validateLayerInfoVersion3ValueForAttribute(attr, value) isValidValue = validateLayerInfoVersion3ValueForAttribute(attr, value)
if not isValidValue: if not isValidValue:
raise GlifLibError("Invalid value for attribute %s (%s)." % (attr, repr(value))) raise GlifLibError("Invalid value for attribute %s (%s)." % (attr, repr(value)))
else: return infoData
validInfoData[attr] = value
return validInfoData
# ----------------- # -----------------
# GLIF Tree Support # GLIF Tree Support
@ -1067,7 +1072,7 @@ def _buildOutlineContourFormat1(pen, contour):
pen.endPath() pen.endPath()
def _buildOutlinePointsFormat1(pen, contour): def _buildOutlinePointsFormat1(pen, contour):
for index, element in enumerate(contour): for element in contour:
x = element.attrib["x"] x = element.attrib["x"]
y = element.attrib["y"] y = element.attrib["y"]
segmentType = element.attrib["segmentType"] segmentType = element.attrib["segmentType"]
@ -1078,8 +1083,9 @@ def _buildOutlinePointsFormat1(pen, contour):
def _buildOutlineComponentFormat1(pen, component): def _buildOutlineComponentFormat1(pen, component):
if len(component): if len(component):
raise GlifLibError("Unknown child elements of component element.") raise GlifLibError("Unknown child elements of component element.")
if set(component.attrib.keys()) - componentAttributesFormat1: for attr in component.attrib.keys():
raise GlifLibError("Unknown attributes in component element.") if attr not in componentAttributesFormat1:
raise GlifLibError("Unknown attribute in component element: %s" % attr)
baseGlyphName = component.get("base") baseGlyphName = component.get("base")
if baseGlyphName is None: if baseGlyphName is None:
raise GlifLibError("The base attribute is not defined in the component.") raise GlifLibError("The base attribute is not defined in the component.")
@ -1105,8 +1111,9 @@ def buildOutlineFormat2(glyphObject, pen, outline, identifiers):
raise GlifLibError("Unknown element in outline element: %s" % element.tag) raise GlifLibError("Unknown element in outline element: %s" % element.tag)
def _buildOutlineContourFormat2(pen, contour, identifiers): def _buildOutlineContourFormat2(pen, contour, identifiers):
if set(contour.attrib.keys()) - contourAttributesFormat2: for attr in contour.attrib.keys():
raise GlifLibError("Unknown attributes in contour element.") if attr not in contourAttributesFormat2:
raise GlifLibError("Unknown attribute in contour element: %s" % attr)
identifier = contour.get("identifier") identifier = contour.get("identifier")
if identifier is not None: if identifier is not None:
if identifier in identifiers: if identifier in identifiers:
@ -1125,7 +1132,7 @@ def _buildOutlineContourFormat2(pen, contour, identifiers):
pen.endPath() pen.endPath()
def _buildOutlinePointsFormat2(pen, contour, identifiers): def _buildOutlinePointsFormat2(pen, contour, identifiers):
for index, element in enumerate(contour): for element in contour:
x = element.attrib["x"] x = element.attrib["x"]
y = element.attrib["y"] y = element.attrib["y"]
segmentType = element.attrib["segmentType"] segmentType = element.attrib["segmentType"]
@ -1147,8 +1154,9 @@ def _buildOutlinePointsFormat2(pen, contour, identifiers):
def _buildOutlineComponentFormat2(pen, component, identifiers): def _buildOutlineComponentFormat2(pen, component, identifiers):
if len(component): if len(component):
raise GlifLibError("Unknown child elements of component element.") raise GlifLibError("Unknown child elements of component element.")
if set(component.attrib.keys()) - componentAttributesFormat2: for attr in component.attrib.keys():
raise GlifLibError("Unknown attributes in component element.") if attr not in componentAttributesFormat2:
raise GlifLibError("Unknown attribute in component element: %s" % attr)
baseGlyphName = component.get("base") baseGlyphName = component.get("base")
if baseGlyphName is None: if baseGlyphName is None:
raise GlifLibError("The base attribute is not defined in the component.") raise GlifLibError("The base attribute is not defined in the component.")
@ -1187,21 +1195,18 @@ def _validateAndMassagePointStructures(contour, pointAttributes, openContourOffC
if element.tag != "point": if element.tag != "point":
raise GlifLibError("Unknown child element (%s) of contour element." % element.tag) raise GlifLibError("Unknown child element (%s) of contour element." % element.tag)
# unknown attributes # unknown attributes
unknownAttributes = [attr for attr in list(element.attrib.keys()) if attr not in pointAttributes] for attr in element.attrib.keys():
if unknownAttributes: if attr not in pointAttributes:
raise GlifLibError("Unknown attributes in point element.") raise GlifLibError("Unknown attribute in point element: %s" % attr)
# search for unknown children # search for unknown children
if len(element): if len(element):
raise GlifLibError("Unknown child elements in point element.") raise GlifLibError("Unknown child elements in point element.")
# x and y are required # x and y are required
x = element.get("x") for attr in ("x", "y"):
y = element.get("y") value = element.get(attr)
if x is None: if value is None:
raise GlifLibError("Required x attribute is missing in point element.") raise GlifLibError("Required %s attribute is missing in point element." % attr)
if y is None: element.attrib[attr] = _number(value)
raise GlifLibError("Required y attribute is missing in point element.")
element.attrib["x"] = _number(x)
element.attrib["y"] = _number(y)
# segment type # segment type
pointType = element.attrib.pop("type", "offcurve") pointType = element.attrib.pop("type", "offcurve")
if pointType not in pointTypeOptions: if pointType not in pointTypeOptions:
@ -1243,8 +1248,7 @@ def _validateAndMassagePointStructures(contour, pointAttributes, openContourOffC
# we only care about how many offCurves there are before an onCurve # we only care about how many offCurves there are before an onCurve
# filter out the trailing offCurves # filter out the trailing offCurves
offCurvesCount = len(contour) - 1 - lastOnCurvePoint offCurvesCount = len(contour) - 1 - lastOnCurvePoint
stripedContour = contour[:-offCurvesCount] if offCurvesCount else contour for element in contour:
for element in stripedContour:
segmentType = element.attrib["segmentType"] segmentType = element.attrib["segmentType"]
if segmentType is None: if segmentType is None:
offCurvesCount += 1 offCurvesCount += 1

View File

@ -28,8 +28,7 @@ def isDictEnough(value):
""" """
if isinstance(value, Mapping): if isinstance(value, Mapping):
return True return True
attrs = ("keys", "values", "items") for attr in ("keys", "values", "items"):
for attr in attrs:
if not hasattr(value, attr): if not hasattr(value, attr):
return False return False
return True return True
@ -48,7 +47,7 @@ def genericIntListValidator(values, validValues):
return False return False
valuesSet = set(values) valuesSet = set(values)
validValuesSet = set(validValues) validValuesSet = set(validValues)
if len(valuesSet - validValuesSet) > 0: if valuesSet - validValuesSet:
return False return False
for value in values: for value in values:
if not isinstance(value, int): if not isinstance(value, int):
@ -83,17 +82,17 @@ def genericDictValidator(value, prototype):
if not isinstance(value, Mapping): if not isinstance(value, Mapping):
return False return False
# missing required keys # missing required keys
for key, (typ, required) in list(prototype.items()): for key, (typ, required) in prototype.items():
if not required: if not required:
continue continue
if key not in value: if key not in value:
return False return False
# unknown keys # unknown keys
for key in list(value.keys()): for key in value.keys():
if key not in prototype: if key not in prototype:
return False return False
# incorrect types # incorrect types
for key, v in list(value.items()): for key, v in value.items():
prototypeType, required = prototype[key] prototypeType, required = prototype[key]
if v is None and not required: if v is None and not required:
continue continue
@ -530,15 +529,16 @@ def guidelinesValidator(value, identifiers=None):
identifiers.add(identifier) identifiers.add(identifier)
return True return True
_guidelineDictPrototype = dict(
x=((int, float), False), y=((int, float), False), angle=((int, float), False),
name=(basestring, False), color=(basestring, False), identifier=(basestring, False)
)
def guidelineValidator(value): def guidelineValidator(value):
""" """
Version 3+. Version 3+.
""" """
dictPrototype = dict( if not genericDictValidator(value, _guidelineDictPrototype):
x=((int, float), False), y=((int, float), False), angle=((int, float), False),
name=(basestring, False), color=(basestring, False), identifier=(basestring, False)
)
if not genericDictValidator(value, dictPrototype):
return False return False
x = value.get("x") x = value.get("x")
y = value.get("y") y = value.get("y")
@ -591,16 +591,17 @@ def anchorsValidator(value, identifiers=None):
identifiers.add(identifier) identifiers.add(identifier)
return True return True
_anchorDictPrototype = dict(
x=((int, float), False), y=((int, float), False),
name=(basestring, False), color=(basestring, False),
identifier=(basestring, False)
)
def anchorValidator(value): def anchorValidator(value):
""" """
Version 3+. Version 3+.
""" """
dictPrototype = dict( if not genericDictValidator(value, _anchorDictPrototype):
x=((int, float), False), y=((int, float), False),
name=(basestring, False), color=(basestring, False),
identifier=(basestring, False)
)
if not genericDictValidator(value, dictPrototype):
return False return False
x = value.get("x") x = value.get("x")
y = value.get("y") y = value.get("y")
@ -726,18 +727,19 @@ def colorValidator(value):
pngSignature = b"\x89PNG\r\n\x1a\n" pngSignature = b"\x89PNG\r\n\x1a\n"
_imageDictPrototype = dict(
fileName=(basestring, True),
xScale=((int, float), False), xyScale=((int, float), False),
yxScale=((int, float), False), yScale=((int, float), False),
xOffset=((int, float), False), yOffset=((int, float), False),
color=(basestring, False)
)
def imageValidator(value): def imageValidator(value):
""" """
Version 3+. Version 3+.
""" """
dictPrototype = dict( if not genericDictValidator(value, _imageDictPrototype):
fileName=(basestring, True),
xScale=((int, float), False), xyScale=((int, float), False),
yxScale=((int, float), False), yScale=((int, float), False),
xOffset=((int, float), False), yOffset=((int, float), False),
color=(basestring, False)
)
if not genericDictValidator(value, dictPrototype):
return False return False
# fileName must be one or more characters # fileName must be one or more characters
if not value["fileName"]: if not value["fileName"]:
@ -818,7 +820,7 @@ def layerContentsValidator(value, ufoPath):
# store # store
contents[layerName] = directoryName contents[layerName] = directoryName
# missing default layer # missing default layer
foundDefault = "glyphs" in list(contents.values()) foundDefault = "glyphs" in contents.values()
if not foundDefault: if not foundDefault:
return False, "The required default glyph set is not in the UFO." return False, "The required default glyph set is not in the UFO."
return True, None return True, None
@ -864,7 +866,7 @@ def groupsValidator(value):
return False, bogusFormatMessage return False, bogusFormatMessage
firstSideMapping = {} firstSideMapping = {}
secondSideMapping = {} secondSideMapping = {}
for groupName, glyphList in list(value.items()): for groupName, glyphList in value.items():
if not isinstance(groupName, (basestring)): if not isinstance(groupName, (basestring)):
return False, bogusFormatMessage return False, bogusFormatMessage
if not isinstance(glyphList, (list, tuple)): if not isinstance(glyphList, (list, tuple)):
@ -914,12 +916,12 @@ def kerningValidator(data):
bogusFormatMessage = "The kerning data is not in the correct format." bogusFormatMessage = "The kerning data is not in the correct format."
if not isinstance(data, Mapping): if not isinstance(data, Mapping):
return False, bogusFormatMessage return False, bogusFormatMessage
for first, secondDict in list(data.items()): for first, secondDict in data.items():
if not isinstance(first, basestring): if not isinstance(first, basestring):
return False, bogusFormatMessage return False, bogusFormatMessage
elif not isinstance(secondDict, Mapping): elif not isinstance(secondDict, Mapping):
return False, bogusFormatMessage return False, bogusFormatMessage
for second, value in list(secondDict.items()): for second, value in secondDict.items():
if not isinstance(second, basestring): if not isinstance(second, basestring):
return False, bogusFormatMessage return False, bogusFormatMessage
elif not isinstance(value, (int, float)): elif not isinstance(value, (int, float)):
@ -930,6 +932,8 @@ def kerningValidator(data):
# lib.plist/lib # lib.plist/lib
# ------------- # -------------
_bogusLibFormatMessage = "The lib data is not in the correct format: %s"
def fontLibValidator(value): def fontLibValidator(value):
""" """
Check the validity of the lib. Check the validity of the lib.
@ -967,11 +971,10 @@ def fontLibValidator(value):
>>> fontLibValidator(lib) >>> fontLibValidator(lib)
(False, 'public.glyphOrder is not properly formatted: expected basestring, found int') (False, 'public.glyphOrder is not properly formatted: expected basestring, found int')
""" """
bogusFormatMessage = "The lib data is not in the correct format: %s"
if not isDictEnough(value): if not isDictEnough(value):
reason = "expected a dictionary, found %s" % type(value).__name__ reason = "expected a dictionary, found %s" % type(value).__name__
return False, bogusFormatMessage % reason return False, _bogusLibFormatMessage % reason
for key, value in list(value.items()): for key, value in value.items():
if not isinstance(key, basestring): if not isinstance(key, basestring):
return False, ( return False, (
"The lib key is not properly formatted: expected basestring, found %s: %r" % "The lib key is not properly formatted: expected basestring, found %s: %r" %
@ -1013,12 +1016,13 @@ def glyphLibValidator(value):
>>> glyphLibValidator(lib) >>> glyphLibValidator(lib)
(False, 'public.markColor is not properly formatted.') (False, 'public.markColor is not properly formatted.')
""" """
bogusFormatMessage = "The lib data is not in the correct format."
if not isDictEnough(value): if not isDictEnough(value):
return False, bogusFormatMessage reason = "expected a dictionary, found %s" % type(value).__name__
for key, value in list(value.items()): return False, _bogusLibFormatMessage % reason
for key, value in value.items():
if not isinstance(key, basestring): if not isinstance(key, basestring):
return False, bogusFormatMessage reason = "key (%s) should be a string" % key
return False, _bogusLibFormatMessage % reason
# public.markColor # public.markColor
if key == "public.markColor": if key == "public.markColor":
if not colorValidator(value): if not colorValidator(value):