googlefonts / ufo2ft

A bridge from UFOs to FontTools objects (and therefore, OTFs and TTFs).
MIT License
152 stars 43 forks source link

unwrapped import from `ufoLib2` in dottedCircleFilter.py, leading to `ModuleNotFoundError` with defcon backend #644

Closed stenson closed 2 years ago

stenson commented 2 years ago

I'm using ufo2ft with an embedded python (python3.10 in Blender 3.2), using defcon as the backend rather than ufoLib2, and when run this line from ufo2ft.featureCompiler import FeatureCompiler, I get an ModuleNotFoundError with this traceback:

Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
  File "<>/Desktop/Blenders/Blender3.2.1.app/Contents/Resources/3.2/python/lib/python3.10/site-packages/ufo2ft/__init__.py", line 19, in <module>
    from ufo2ft.preProcessor import (
  File "<>/Desktop/Blenders/Blender3.2.1.app/Contents/Resources/3.2/python/lib/python3.10/site-packages/ufo2ft/preProcessor.py", line 8, in <module>
    from ufo2ft.filters import isValidFilter, loadFilters
  File "<>/Desktop/Blenders/Blender3.2.1.app/Contents/Resources/3.2/python/lib/python3.10/site-packages/ufo2ft/filters/__init__.py", line 12, in <module>
    from .dottedCircleFilter import DottedCircleFilter
  File "<>/Desktop/Blenders/Blender3.2.1.app/Contents/Resources/3.2/python/lib/python3.10/site-packages/ufo2ft/filters/dottedCircleFilter.py", line 51, in <module>
    from ufoLib2.objects import Glyph
ModuleNotFoundError: No module named 'ufoLib2'

If I install ufoLib2 into the embedded python, obviously the issue goes away completely, but the weirder thing is, if I don't have ufoLib2 installed and I run the same import twice (from ufo2ft.featureCompiler import FeatureCompiler), there is no ModuleNotFoundError and everything works as it should (though presumably the function that actually uses ufoLib2.objects.Glyph in DottedCircleFilter does not work). So an easy workaround here is to try/except from ufo2ft.featureCompiler import FeatureCompiler before running the rest of my code, and everything seems to work. (I'd love to know why this happens if anyone can shed some light on that? 🤔)

All that said, I wrote a patch for DottedCircleFilter to use the defcon equivalent of the ufoLib2 Glyph class when ufoLib2 isn't available (pasted below) and would be happy to submit a PR, though I think it might run counter to some of the code patterns in this library:

diff --git a/Lib/ufo2ft/filters/dottedCircleFilter.py b/Lib/ufo2ft/filters/dottedCircleFilter.py
index 0b9771d..0e902c2 100644
--- a/Lib/ufo2ft/filters/dottedCircleFilter.py
+++ b/Lib/ufo2ft/filters/dottedCircleFilter.py
@@ -48,7 +48,21 @@ import math
 from statistics import mean

 from fontTools.misc.fixedTools import otRound
-from ufoLib2.objects import Glyph
+
+try:
+    from ufoLib2.objects import Glyph
+    
+    def new_glyph(name, unicodes):
+        return Glyph(name=name, unicodes=unicodes)
+
+except ImportError:
+    from defcon import Glyph
+    
+    def new_glyph(name, unicodes):
+        glyph = Glyph()
+        glyph.name = name
+        glyph.unicodes = unicodes
+        return glyph

 from ufo2ft.constants import OPENTYPE_CATEGORIES_KEY
 from ufo2ft.featureCompiler import parseLayoutFeatures
@@ -149,7 +163,7 @@ class DottedCircleFilter(BaseFilter):
         """Add a new dotted circle glyph, drawing its outlines"""
         font = self.context.font
         logger.debug("Adding dotted circle glyph")
-        glyph = Glyph(name="uni25CC", unicodes=[0x25CC])
+        glyph = new_glyph("uni25CC", [0x25CC])
         pen = glyph.getPen()

         bigradius = (font.info.xHeight - 2 * self.options.margin) / 2
anthrotype commented 2 years ago

thanks. we should try not import directly ufoLib2 or defcon, but use something like the _getNewGlyphFactory method from ufo2ft.util module: https://github.com/googlefonts/ufo2ft/blob/036cb54e44e7cbdd1889d83c5f1bf02dee51a842/Lib/ufo2ft/util.py#L101-L118

stenson commented 2 years ago

thanks for the fix!