Repository: googlefonts/glyphsLib
Pull request #632 (2020-11-06 21:11:52)

Use Proxy for GSFont.features/featurePrefixes
Matches Glyphs App API and for consistency with the rather identical GSFont.classes.


Patch:
diff --git a/Lib/glyphsLib/classes.py b/Lib/glyphsLib/classes.py
index 5958d13a..e9bec014 100755
--- a/Lib/glyphsLib/classes.py
+++ b/Lib/glyphsLib/classes.py
@@ -686,6 +686,8 @@ class FontGlyphsProxy(Proxy):
 
 
 class FontClassesProxy(Proxy):
+    VALUES_ATTR = "_classes"
+
     def __getitem__(self, key):
         if isinstance(key, (slice, int)):
             return self.values().__getitem__(key)
@@ -715,7 +717,13 @@ class FontClassesProxy(Proxy):
                 if klass.name == key:
                     del self.values()[index]
 
-    # FIXME: (jany) def __contains__
+    def __contains__(self, item):
+        if isinstance(item, str):
+            for klass in self.values():
+                if klass.name == item:
+                    return True
+            return False
+        return item in self.values()
 
     def append(self, item):
         self.values().append(item)
@@ -734,16 +742,24 @@ class FontClassesProxy(Proxy):
         self.values().remove(item)
 
     def values(self):
-        return self._owner._classes
+        return getattr(self._owner, self.VALUES_ATTR)
 
     def setter(self, values):
         if isinstance(values, Proxy):
             values = list(values)
-        self._owner._classes = values
+        setattr(self._owner, self.VALUES_ATTR, values)
         for value in values:
             value._parent = self._owner
 
 
+class FontFeaturesProxy(FontClassesProxy):
+    VALUES_ATTR = "_features"
+
+
+class FontFeaturePrefixesProxy(FontClassesProxy):
+    VALUES_ATTR = "_featurePrefixes"
+
+
 class GlyphLayerProxy(Proxy):
     def __getitem__(self, key):
         self._ensureMasterLayers()
@@ -3530,7 +3546,9 @@ class GSFont(GSBase):
         "DisplayStrings",
         "_classes",
         "_customParameters",
+        "_featurePrefixes",
         "_features",
+        "_customParameters",
         "_glyphs",
         "_instances",
         "_kerning",
@@ -3545,7 +3563,6 @@ class GSFont(GSBase):
         "disablesAutomaticAlignment",
         "disablesNiceNames",
         "familyName",
-        "featurePrefixes",
         "filepath",
         "grid",
         "gridSubDivisions",
@@ -3595,6 +3612,8 @@ class GSFont(GSBase):
     }
     _defaultsForName = {
         "classes": [],
+        "features": [],
+        "featurePrefixes": [],
         "customParameters": [],
         "disablesAutomaticAlignment": False,
         "disablesNiceNames": False,
@@ -3607,7 +3626,6 @@ class GSFont(GSBase):
 
     def __init__(self, path=None):
         self.DisplayStrings = ""
-        self._features = []
         self._glyphs = []
         self._instances = []
         self._masters = []
@@ -3615,6 +3633,8 @@ class GSFont(GSBase):
         self._versionMinor = 0
         self.appVersion = "895"  # minimum required version
         self.classes = copy.deepcopy(self._defaultsForName["classes"])
+        self.features = copy.deepcopy(self._defaultsForName["features"])
+        self.featurePrefixes = copy.deepcopy(self._defaultsForName["featurePrefixes"])
         self.copyright = ""
         self.customParameters = copy.deepcopy(self._defaultsForName["customParameters"])
         self.date = None
@@ -3625,7 +3645,6 @@ class GSFont(GSBase):
         ]
         self.disablesNiceNames = self._defaultsForName["disablesNiceNames"]
         self.familyName = "Unnamed font"
-        self.featurePrefixes = []
         self.filepath = None
         self.grid = self._defaultsForName["gridLength"]
         self.gridSubDivisions = self._defaultsForName["gridSubDivision"]
@@ -3695,18 +3714,6 @@ class GSFont(GSBase):
             ):
                 glyph._setupLayer(layer, layer.layerId)
 
-    @property
-    def features(self):
-        return self._features
-
-    @features.setter
-    def features(self, value):
-        # FIXME: (jany) why not use Proxy like every other attribute?
-        # FIXME: (jany) do the same for featurePrefixes?
-        self._features = value
-        for g in self._features:
-            g._parent = self
-
     masters = property(
         lambda self: FontFontMasterProxy(self),
         lambda self, value: FontFontMasterProxy(self).setter(value),
@@ -3734,6 +3741,16 @@ class GSFont(GSBase):
         lambda self, value: FontClassesProxy(self).setter(value),
     )
 
+    features = property(
+        lambda self: FontFeaturesProxy(self),
+        lambda self, value: FontFeaturesProxy(self).setter(value),
+    )
+
+    featurePrefixes = property(
+        lambda self: FontFeaturePrefixesProxy(self),
+        lambda self, value: FontFeaturePrefixesProxy(self).setter(value),
+    )
+
     customParameters = property(
         lambda self: CustomParametersProxy(self),
         lambda self, value: CustomParametersProxy(self).setter(value),


Test patch:
diff --git a/tests/classes_test.py b/tests/classes_test.py
index b69fce25..44da5e38 100755
--- a/tests/classes_test.py
+++ b/tests/classes_test.py
@@ -1749,5 +1749,23 @@ class FontGlyphsProxyTest(unittest.TestCase):
             del self.font.glyphs[self.font]
 
 
+class FontClassesProxyTest(unittest.TestCase):
+    def setUp(self):
+        self.font = GSFont(TESTFILE_PATH)
+
+    def test_indxing_by_name(self):
+        assert "Languagesystems" in self.font.featurePrefixes
+        assert "c2sc_source" in self.font.classes
+        assert "aalt" in self.font.features
+
+        assert "XXXX" not in self.font.featurePrefixes
+        assert "XXXX" not in self.font.classes
+        assert "XXXX" not in self.font.features
+
+        assert self.font.featurePrefixes["Languagesystems"] in self.font.featurePrefixes
+        assert self.font.classes["c2sc_source"] in self.font.classes
+        assert self.font.features["aalt"] in self.font.features
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/writer_test.py b/tests/writer_test.py
index d7ba670d..2c956285 100644
--- a/tests/writer_test.py
+++ b/tests/writer_test.py
@@ -87,7 +87,7 @@ class WriterTest(unittest.TestCase, test_helpers.AssertLinesEqual):
         font.features.append(f1)
         # featurePrefixes
         fp1 = classes.GSFeaturePrefix()
-        fp1 = "FP1"
+        fp1.name = "FP1"
         font.featurePrefixes.append(fp1)
         # copyright
         font.copyright = "Copyright Bob"
@@ -173,7 +173,10 @@ class WriterTest(unittest.TestCase, test_helpers.AssertLinesEqual):
             disablesNiceNames = 1;
             familyName = "Sans Rien";
             featurePrefixes = (
-            FP1
+            {
+            code = "";
+            name = FP1;
+            }
             );
             features = (
             {