Repository: googlefonts/glyphsLib

Issue:
can't roundtrip glyphs2ufo and ufo2glyphs for a particular Rubik-Italic.glyphs file
When I run glyphs2ufo on this file, and then run ufo2glyphs again without changing anything I get an error (see below). Usually this process works without issue, but just one this one file I have a problem. For whatever reason this only happens on the Italic and not on the Roman. I've hit this error while working on making variable font versions of the family for Google Fonts and Dave and Marc said I should report it here, since it looks like a bug. Thanks! 

Full traceback is:
~~~
Traceback (most recent call last):
  File "/Users/cjdunn/env_gtools/bin/ufo2glyphs", line 10, in <module>
    sys.exit(_ufo2glyphs_entry_point())
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/cli.py", line 268, in _ufo2glyphs_entry_point
    return main(args)
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/cli.py", line 169, in main
    return options.func(options)
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/cli.py", line 244, in ufo2glyphs
    object_to_read, minimize_ufo_diffs=options.no_preserve_glyphsapp_metadata
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/__init__.py", line 139, in to_glyphs
    return builder.font
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/builders.py", line 654, in font
    self.to_glyphs_features()
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/features.py", line 274, in to_glyphs_features
    glyphs_module=self.glyphs_module,
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/features.py", line 287, in _to_glyphs_features
    document = FeaDocument(features_text, glyph_names)
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/features.py", line 342, in __init__
    self._build_end_locations()
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/features.py", line 372, in _build_end_locations
    self._build_end_locations_rec(self._doc)
  File "/Users/cjdunn/env_gtools/lib/python3.7/site-packages/glyphsLib/builder/features.py", line 396, in _build_end_locations_rec
    previous_in_block = st.statements[-1]
IndexError: list index out of range

~~~

Here's the file: 
[Rubik_Italic.glyphs.zip](https://github.com/googlefonts/glyphsLib/files/3799060/Rubik_Italic.glyphs.zip)



Discussion:

Patch:
diff --git a/Lib/glyphsLib/builder/features.py b/Lib/glyphsLib/builder/features.py
index a3295dd9..f8d3f1c2 100644
--- a/Lib/glyphsLib/builder/features.py
+++ b/Lib/glyphsLib/builder/features.py
@@ -393,7 +393,7 @@ class FeaDocument:
                 previous_in_block = None
             previous = st
             if hasattr(st, "statements"):
-                previous_in_block = st.statements[-1]
+                previous_in_block = st.statements[-1] if st.statements else None
 
     WHITESPACE_RE = re.compile("\\s")
     WHITESPACE_OR_NAME_RE = re.compile("\\w|\\s")
diff --git a/Lib/glyphsLib/classes.py b/Lib/glyphsLib/classes.py
index 8b1f2f84..4e89f5c4 100755
--- a/Lib/glyphsLib/classes.py
+++ b/Lib/glyphsLib/classes.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 
+import copy
 import re
 import math
 import inspect
@@ -314,7 +315,7 @@ class GSBase:
                     # instead of None when no image
                     value = []
                 elif key in self._defaultsForName:
-                    value = self._defaultsForName.get(key)
+                    value = copy.deepcopy(self._defaultsForName.get(key))
                 else:
                     value = klass()
                 key = self._wrapperKeysTranslate.get(key, key)
diff --git a/requirements-dev.in b/requirements-dev.in
index 880b40b9..b7965080 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -1,5 +1,4 @@
 coverage
 pytest>=2.8
-pytest-randomly
 ufoNormalizer>=0.3.2
 xmldiff>=2.2
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 4a66a1ed..72de3cf9 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -5,23 +5,21 @@
 #    pip-compile requirements-dev.in
 #
 atomicwrites==1.3.0       # via pytest
-attrs==19.3.0             # via pytest
+attrs==19.1.0             # via packaging, pytest
 coverage==4.5.4
-entrypoints==0.3          # via pytest-randomly
-importlib-metadata==0.23  # via pluggy, pytest
+importlib-metadata==0.22  # via pluggy, pytest
 lxml==4.4.1               # via xmldiff
 more-itertools==7.2.0     # via pytest, zipp
-packaging==19.2           # via pytest
+packaging==19.1           # via pytest
 pluggy==0.13.0            # via pytest
 py==1.8.0                 # via pytest
 pyparsing==2.4.2          # via packaging
-pytest-randomly==3.1.0
-pytest==5.2.2
+pytest==5.1.2
 six==1.12.0               # via packaging, xmldiff
 ufonormalizer==0.3.6
 wcwidth==0.1.7            # via pytest
-xmldiff==2.4
+xmldiff==2.3
 zipp==0.6.0               # via importlib-metadata
 
 # The following packages are considered to be unsafe in a requirements file:
-# setuptools==41.6.0        # via xmldiff
+# setuptools==41.2.0        # via xmldiff


Test patch:
diff --git a/tests/builder/features_test.py b/tests/builder/features_test.py
index edb3474f..67c549b8 100644
--- a/tests/builder/features_test.py
+++ b/tests/builder/features_test.py
@@ -462,3 +462,26 @@ def test_groups_remain_at_top(tmpdir):
 
     fea_rt = rtufo.features.text
     assert fea_rt.index("@FIG_DFLT") < fea_rt.index("lookup") < fea_rt.index("feature")
+
+
+def test_roundtrip_empty_feature():
+    # https://github.com/googlefonts/glyphsLib/issues/562
+    font = to_glyphs([defcon.Font()])
+    feature = classes.GSFeature(name="dlig")
+    feature.code = ""
+    font.features.append(feature)
+
+    ufo, = to_ufos(font)
+    assert ufo.features.text == dedent(
+        """\
+        feature dlig {
+
+        } dlig;
+    """
+    )
+
+    font_r = to_glyphs([ufo])
+    assert len(font_r.features) == 1
+    feature_r = font_r.features[0]
+    assert feature_r.name == "dlig"
+    assert feature_r.code == ""