From 3ff2ee61e18add5ecf3ca30fb4913412a782e7b2 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 16 Dec 2023 20:58:51 -0700 Subject: [PATCH] Move lazy datastructures to misc.lazyTools --- Lib/fontTools/misc/lazyTools.py | 42 ++++++++++++++++++++++ Lib/fontTools/ttLib/tables/_g_v_a_r.py | 18 ++-------- Lib/fontTools/ttLib/tables/otConverters.py | 37 ++----------------- Tests/ttLib/tables/otConverters_test.py | 21 ++++++----- 4 files changed, 60 insertions(+), 58 deletions(-) create mode 100644 Lib/fontTools/misc/lazyTools.py diff --git a/Lib/fontTools/misc/lazyTools.py b/Lib/fontTools/misc/lazyTools.py new file mode 100644 index 000000000..a3cc52e53 --- /dev/null +++ b/Lib/fontTools/misc/lazyTools.py @@ -0,0 +1,42 @@ +from collections import UserDict, UserList + +__all__ = ["LazyDict", "LazyList"] + + +class LazyDict(UserDict): + def __init__(self, data): + super().__init__() + self.data = data + + def __getitem__(self, k): + v = self.data[k] + if callable(v): + v = v(self, k) + self.data[k] = v + return v + + +class LazyList(UserList): + def __getitem__(self, k): + if isinstance(k, slice): + indices = range(*k.indices(len(self))) + return [self[i] for i in indices] + v = self.data[k] + if callable(v): + v = v(self, k) + self.data[k] = v + return v + + def __add__(self, other): + if isinstance(other, LazyList): + other = list(other) + elif isinstance(other, list): + pass + else: + return NotImplemented + return list(self) + other + + def __radd__(self, other): + if not isinstance(other, list): + return NotImplemented + return other + list(self) diff --git a/Lib/fontTools/ttLib/tables/_g_v_a_r.py b/Lib/fontTools/ttLib/tables/_g_v_a_r.py index 70b655f60..a44f72b66 100644 --- a/Lib/fontTools/ttLib/tables/_g_v_a_r.py +++ b/Lib/fontTools/ttLib/tables/_g_v_a_r.py @@ -1,7 +1,8 @@ -from collections import UserDict, deque +from collections import deque from functools import partial from fontTools.misc import sstruct from fontTools.misc.textTools import safeEval +from fontTools.misc.lazyTools import LazyDict from . import DefaultTable import array import itertools @@ -39,19 +40,6 @@ GVAR_HEADER_FORMAT = """ GVAR_HEADER_SIZE = sstruct.calcsize(GVAR_HEADER_FORMAT) -class _LazyDict(UserDict): - def __init__(self, data): - super().__init__() - self.data = data - - def __getitem__(self, k): - v = self.data[k] - if callable(v): - v = v(self, k) - self.data[k] = v - return v - - def _decompileVarGlyph(self, glyphName): gid = self.reverseGlyphMap[glyphName] offsetSize = 2 if self.tableFormat == 0 else 4 @@ -143,7 +131,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable): offsetToData = self.offsetToGlyphVariationData glyf = ttFont["glyf"] - l = _LazyDict( + l = LazyDict( {glyphs[gid]: _decompileVarGlyph for gid in range(self.glyphCount)} ) l.reverseGlyphMap = ttFont.getReverseGlyphMap() diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py index ce55ed55b..b46eeb522 100644 --- a/Lib/fontTools/ttLib/tables/otConverters.py +++ b/Lib/fontTools/ttLib/tables/otConverters.py @@ -8,6 +8,7 @@ from fontTools.misc.fixedTools import ( ) from fontTools.misc.roundTools import nearestMultipleShortestRepr, otRound from fontTools.misc.textTools import bytesjoin, tobytes, tostr, pad, safeEval +from fontTools.misc.lazyTools import LazyList from fontTools.ttLib import getSearchRange from .otBase import ( CountReference, @@ -108,38 +109,6 @@ def buildConverters(tableSpec, tableNamespace): return converters, convertersByName -try: - from collections import UserList -except ImportError: - from UserList import UserList - - -class _LazyList(UserList): - def __getitem__(self, k): - if isinstance(k, slice): - indices = range(*k.indices(len(self))) - return [self[i] for i in indices] - v = self.data[k] - if callable(v): - v = v(self, k) - self.data[k] = v - return v - - def __add__(self, other): - if isinstance(other, _LazyList): - other = list(other) - elif isinstance(other, list): - pass - else: - return NotImplemented - return list(self) + other - - def __radd__(self, other): - if not isinstance(other, list): - return NotImplemented - return other + list(self) - - def _base_converter_read_item(self, i): self.reader.seek(self.pos + i * self.recordSize) return self.conv.read(self.reader, self.font, {}) @@ -192,7 +161,7 @@ class BaseConverter(object): l.append(self.read(reader, font, tableDict)) return l else: - l = _LazyList(_base_converter_read_item for i in range(count)) + l = LazyList(_base_converter_read_item for i in range(count)) l.reader = reader.copy() l.pos = l.reader.pos l.font = font @@ -1893,7 +1862,7 @@ class CFF2Index(BaseConverter): lastOffset = offset return items else: - l = _LazyList([cff2_index_read_item] * count) + l = LazyList([cff2_index_read_item] * count) l.reader = reader.copy() l.offset_pos = l.reader.pos l.data_pos = l.offset_pos + (count + 1) * offSize diff --git a/Tests/ttLib/tables/otConverters_test.py b/Tests/ttLib/tables/otConverters_test.py index 37ae467d0..f1be8ea23 100644 --- a/Tests/ttLib/tables/otConverters_test.py +++ b/Tests/ttLib/tables/otConverters_test.py @@ -427,9 +427,12 @@ class AATLookupTest(unittest.TestCase): ) +from fontTools.misc.lazyTools import LazyList + + class LazyListTest(unittest.TestCase): def test_slice(self): - ll = otConverters._LazyList([10, 11, 12, 13]) + ll = LazyList([10, 11, 12, 13]) sl = ll[:] self.assertIsNot(sl, ll) @@ -439,8 +442,8 @@ class LazyListTest(unittest.TestCase): self.assertEqual([11, 12], ll[1:3]) def test_add_both_LazyList(self): - ll1 = otConverters._LazyList([1]) - ll2 = otConverters._LazyList([2]) + ll1 = LazyList([1]) + ll2 = LazyList([2]) l3 = ll1 + ll2 @@ -448,7 +451,7 @@ class LazyListTest(unittest.TestCase): self.assertEqual([1, 2], l3) def test_add_LazyList_and_list(self): - ll1 = otConverters._LazyList([1]) + ll1 = LazyList([1]) l2 = [2] l3 = ll1 + l2 @@ -458,13 +461,13 @@ class LazyListTest(unittest.TestCase): def test_add_not_implemented(self): with self.assertRaises(TypeError): - otConverters._LazyList() + 0 + LazyList() + 0 with self.assertRaises(TypeError): - otConverters._LazyList() + tuple() + LazyList() + tuple() def test_radd_list_and_LazyList(self): l1 = [1] - ll2 = otConverters._LazyList([2]) + ll2 = LazyList([2]) l3 = l1 + ll2 @@ -473,9 +476,9 @@ class LazyListTest(unittest.TestCase): def test_radd_not_implemented(self): with self.assertRaises(TypeError): - 0 + otConverters._LazyList() + 0 + LazyList() with self.assertRaises(TypeError): - tuple() + otConverters._LazyList() + tuple() + LazyList() if __name__ == "__main__":