Merge pull request #2422 from fonttools/ufolib-speed-up-filename-clash-checking

Use a set for file names for clash checking
This commit is contained in:
Nikolaus Waxweiler 2021-10-07 15:22:32 +01:00 committed by GitHub
commit a5173b218a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 18 additions and 12 deletions

View File

@ -15,10 +15,9 @@ class NameTranslationError(Exception):
pass pass
def userNameToFileName(userName: str, existing=[], prefix="", suffix=""): def userNameToFileName(userName: str, existing=(), prefix="", suffix=""):
""" """
existing should be a case-insensitive list `existing` should be a set-like object.
of all existing file names.
>>> userNameToFileName("a") == "a" >>> userNameToFileName("a") == "a"
True True

View File

@ -10,6 +10,8 @@ in a folder. It offers two ways to read glyph data, and one way to write
glyph data. See the class doc string for details. glyph data. See the class doc string for details.
""" """
from __future__ import annotations
import logging import logging
import enum import enum
from warnings import warn from warnings import warn
@ -205,7 +207,7 @@ class GlyphSet(_UFOBaseIO):
self.glyphNameToFileName = glyphNameToFileNameFunc self.glyphNameToFileName = glyphNameToFileNameFunc
self._validateRead = validateRead self._validateRead = validateRead
self._validateWrite = validateWrite self._validateWrite = validateWrite
self._existingFileNames = None self._existingFileNames: set[str] | None = None
self._reverseContents = None self._reverseContents = None
self.rebuildContents() self.rebuildContents()
@ -455,12 +457,12 @@ class GlyphSet(_UFOBaseIO):
fileName = self.contents.get(glyphName) fileName = self.contents.get(glyphName)
if fileName is None: if fileName is None:
if self._existingFileNames is None: if self._existingFileNames is None:
self._existingFileNames = {} self._existingFileNames = {
for fileName in self.contents.values(): fileName.lower() for fileName in self.contents.values()
self._existingFileNames[fileName] = fileName.lower() }
fileName = self.glyphNameToFileName(glyphName, self._existingFileNames.values()) fileName = self.glyphNameToFileName(glyphName, self._existingFileNames)
self.contents[glyphName] = fileName self.contents[glyphName] = fileName
self._existingFileNames[fileName] = fileName.lower() self._existingFileNames.add(fileName.lower())
if self._reverseContents is not None: if self._reverseContents is not None:
self._reverseContents[fileName.lower()] = glyphName self._reverseContents[fileName.lower()] = glyphName
data = _writeGlyphToBytes( data = _writeGlyphToBytes(
@ -485,9 +487,9 @@ class GlyphSet(_UFOBaseIO):
fileName = self.contents[glyphName] fileName = self.contents[glyphName]
self.fs.remove(fileName) self.fs.remove(fileName)
if self._existingFileNames is not None: if self._existingFileNames is not None:
del self._existingFileNames[fileName] self._existingFileNames.remove(fileName.lower())
if self._reverseContents is not None: if self._reverseContents is not None:
del self._reverseContents[self.contents[glyphName].lower()] del self._reverseContents[fileName.lower()]
del self.contents[glyphName] del self.contents[glyphName]
# dict-like support # dict-like support
@ -573,9 +575,12 @@ class GlyphSet(_UFOBaseIO):
def glyphNameToFileName(glyphName, existingFileNames): def glyphNameToFileName(glyphName, existingFileNames):
""" """
Wrapper around the userNameToFileName function in filenames.py Wrapper around the userNameToFileName function in filenames.py
Note that existingFileNames should be a set for large glyphsets
or performance will suffer.
""" """
if existingFileNames is None: if existingFileNames is None:
existingFileNames = [] existingFileNames = set()
return userNameToFileName(glyphName, existing=existingFileNames, suffix=".glif") return userNameToFileName(glyphName, existing=existingFileNames, suffix=".glif")
# ----------------------- # -----------------------

View File

@ -159,6 +159,8 @@ class FileNameTest:
dst.writeGlyph("a", glyph) dst.writeGlyph("a", glyph)
dst.writeGlyph("A", glyph) dst.writeGlyph("A", glyph)
dst.writeGlyph("a_", glyph) dst.writeGlyph("a_", glyph)
dst.deleteGlyph("a_")
dst.writeGlyph("a_", glyph)
dst.writeGlyph("A_", glyph) dst.writeGlyph("A_", glyph)
dst.writeGlyph("i_j", glyph) dst.writeGlyph("i_j", glyph)