Make GSFont params more pythonic

Hey,

I know this is not major but it is fairly annoying when scripting Glyphs one runs into issues that crash the app like the below.

The attributes of GSFont should behave either more pythonic or be documented better.

"designer" in Glyphs.fonts[0] will actually crash GlyphsApp, as will trying to loop with for attribute in Glyphs.fonts[0].

Glyphs.fonts[0]["designer"] will return None even when set, because you’d have to use getattr(Glyphs.fonts[0], "designer") to access the value.

Cheers,
Johannes

Font is not a dict. designer is a simple property of GSFont. So you can do font.designer.

(But please don’t do Glyphs.fonts[X] all the time. Either use Font or store Glyphs.fonts[X] in a variable.)

Right, it’s just not overly intuitive to iterate over, nor does the type <objective-c class NSKVONotifying_GSFont at 0x600001158d80> reveal how to iterate it. (key in dir(Font) works, but will have all sort of attributes, not just the API paramaters).

Just used for explicitness in the sample code.

Particularly the fact that trying to access it with "designer" in Font will send Glyphs into limbo is… not ideal :slight_smile:

Just my feedback, since I’ve ran into this quite a few times.

I added a some code that at least print an error instead of crashing.

And I thought about it a bit more. If I would allow dict like access on the font, it would make more sense to return the glyph with that name (at least RoboFab is behaving like this).

Well, it’s a matter of taste and custom, but I find it odd the indices on a font should be the glyphs, not the font’s own properties. I rather like how the Glyphs API is logical about this with Font.glyphs[…]

Why and how would you iterate over an object’s (very diverse) properties? I have never encountered that pattern (also other programming languages, and independent of fonts).

You asked, so e.g. a QA script to make sure what should, should not or may be set by a designer.

required_fields = ["upm", "familyName"]
recommended_fields = ["designer", "designerUrl"]
ignored_fields = ["manufacturer", "manufacturerURL",
                  "versionMajor", "versionMinor", "copyright"]

for field in [r for r in required_fields
              if r in dir(Glyphs.font) and getattr(Glyphs.font, r) is None]:
    logging.error("Font attribute '%s' must not be empty." % (field))

for field in [r for r in recommended_fields
              if r in dir(Glyphs.font) and getattr(Glyphs.font, r) is None]:
    logging.warning("Font attribute '%s' should ideally not be empty." %
                    (field))

for field in [r for r in ignored_fields
              if r in dir(Glyphs.font) and getattr(Glyphs.font, r) is None]:
    logging.warning("Font attribute '%s' should be empty and will be "
                    "overwritten in the production." % (field))

Instead of r in dir(Glyphs.font) and getattr(Glyphs.font, r) is None I’d expect to check r in Glyphs.font or without having to avoid crashing on non-existance, simply Glyphs.font[r] is None.

In contrast to this, GSMaster.customParameters behaves pythonic and like I’d expect it to, e.g. "underlinePosition" in mymaster.customParameters

1 Like

It seems you want to iterate over only a subset of the GSFont object’s properties. Practically all plain string properties. I wouldn’t say that r in Glyphs.font is an intuitive way of iterating over a particular subset of the object’s properties.

Instead, it could be useful if Glyphs provided a property, maybe called fontInfo of metadata, that provides that subset of properties (plain strings) as, say, a Python dict.

Yea, sounds about right. I suppose the documented properties of GSFont are of interest, implicitly getting any other properties is a result of the objective C bridge, I suppose?

So you want to check if a certain property is set?

Why not like this. The list comprehension is not helping with the readability of the code.

required_fields = ["upm", "familyName"]
recommended_fields = ["designer", "designerUrl"]
ignored_fields = ["manufacturer", "manufacturerURL",
                  "versionMajor", "versionMinor", "copyright"]
font = Glyphs.font
for field in required_fields:
    value = getattr(font, field)
    if value is None or len(value) == 0:
        logging.error("Font attribute '%s' must not be empty." % (field))
...
1 Like