Updating Python scripts and plug-ins for Glyphs 4

Many scripts and plug-ins continue to run in Glyphs 4 just as they did in Glyphs 3. However, some code needs to be updated to work in Glyphs 4 as well.

Python version

Glyphs 4 supports Python 3.14 and later.

Checking the app version

Use Glyphs.versionNumber to conditionally run code depending on the Glyphs app version:

from GlyphsApp import Glyphs

if Glyphs.versionNumber >= 4.0:
    # ...
else:
    # ...

Avoid wildcard imports

In general, avoid importing using the wildcard * keyword. Instead, use explicit imports:

# avoid wildcards like this one:
from GlyphsApp import *
# prefer explicit imports:
from GlyphsApp import Glyphs, GSFont, GSGlyph, GSLayer

# ...

Such explicit imports work in Glyphs 3 as well.

Redrawing Edit View

If you want to manually redraw an Edit View tab, do so using the redraw() API:

if Glyphs.versionNumber >= 4.0:
    tab.redraw()
else:
    tab.forceRedraw()

API changes

GSInstance.fileName(format) now requires an explicit format constant as a parameter.

Intermediate (brace) and Alternate (bracket) layers API that was exposed as methods in Glyphs 3 is now exposed as properties:

# Glyphs 3:
someLayer.isBraceLayer() || someLayer.isBracketLayer()
# Glyphs 4:
someLayer.isBraceLayer || someLayer.isBracketLayer

Smart Glyphs

Smart Glyphs now use axes, just like the variation axes of a font. Access them as someGlyph.axes.

Glyphs 3 Glyphs 4
glyph.smartComponentAxes with GSPartProperty glyph.axes with GSAxis
layer.smartComponentPoleMapping[property.name] layer.coordinates()[axis.axisId]
component.smartComponentValues[property.id] component.smartComponentValues[axis.axisId]

Use someLayer.axesValues to get the axis coordinates of the layer (font axis coordinates first, for layers of Smart Glyphs the coordinates of the glyph axis second).

Constants

The old direction constants BIDI, LTR, RTL, RTLTTB, and LTRTTB are not exported by Glyphs 4. Use GSBIDI, GSLTR, GSRTL, GSVertical, and GSVerticalToRight.

GSAlignmentDisable is GSAlignmentDisabled in Glyphs 4.

VARIABLE changed its value, ensure you are using the constant and not its old value "variable" directly.

Using code checkers

Glyphs 4 provides .pyi files that provide type hints for type checking tools like Pylance or pyright.
If you use Visual Studio Code, you can add the following to your .vscode/settings.json file:

	"flake8.args": [
		"--ignore=W191,E266,E501,E265,E722,W503,E303,E262,E701,E741,E261",
		"--verbose"
	],
	"python.analysis.extraPaths": [
		"/Applications/Glyphs 4.app/Contents/Scripts/typing/",
		"/Applications/Glyphs 4.app/Contents/Scripts/GlyphsApp/",
		"/Applications/Glyphs 4.app/Contents/Scripts/",
		"~/Library/Application Support/Glyphs 4/Scripts/"
	]

(adjust the --ignore entries to your liking.)

Or, if you use pyright and ruff (e.g. in the Scripting Window), put this into pyrightconfig.json

{
	"extraPaths": [
		"/Applications/Glyphs 4.app/Contents/Scripts/typing/",
		"/Applications/Glyphs 4.app/Contents/Scripts/GlyphsApp/",
		"/Applications/Glyphs 4.app/Contents/Scripts/",
		"~/Library/Application Support/Glyphs 4/Scripts/"
	],
	"typeCheckingMode": "basic",
	"reportDeprecated": "warning"
}

and ruff.toml:

[lint]
preview = true
select = [
	"E",    # pycodestyle errors (stable rules)
	"W",    # pycodestyle warnings (stable rules)
	"F",    # pyflakes (unused imports/variables, undefined names, …)
	# preview-gated pycodestyle rules flake8 would check:
	"E111", # indentation is not a multiple of the indent size
	"E112", # expected an indented block
	"E113", # unexpectedly indented
	"E114", # indentation is not a multiple of the indent size (comment)
	"E115", # expected an indented block (comment)
	"E116", # unexpected indentation (comment)
	"E117", # over-indented
	"E124", # closing bracket does not match visual indentation
	"E128", # continuation line under-indented for visual indent
	"E201", # whitespace after '('
	"E202", # whitespace before ')'
	"E203", # whitespace before ':' ',' ';'
	"E211", # whitespace before '(' or '['
	"E221", # multiple spaces before operator
	"E222", # multiple spaces after operator
	"E223", # tab before operator
	"E224", # tab after operator
	"E225", # missing whitespace around operator
	"E226", # missing whitespace around arithmetic operator
	"E227", # missing whitespace around bitwise or shift operator
	"E228", # missing whitespace around modulo operator
	"E231", # missing whitespace after ',' ';' or ':'
	"E241", # multiple spaces after comma
	"E242", # tab after comma
	"E251", # unexpected spaces around keyword / parameter equals
	"E252", # missing whitespace around parameter equals
	"E301", # expected 1 blank line between methods
	"E302", # expected 2 blank lines before top-level definition
	"E303", # too many blank lines (ignored below, kept for completeness)
	"E304", # blank lines after function decorator
	"E305", # expected 2 blank lines after class or function definition
	"E306", # expected 1 blank line before nested definition
	"E502", # redundant backslash between brackets
	"W391", # blank line at end of file
]
ignore = [
	"W191", # tab indentation
	"E266", # too many leading # for block comment
	"E501", # line too long
	"E265", # block comment should start with '# '
	"E722", # bare except
	"E303", # too many blank lines
	"E262", # inline comment should start with '# '
	"E701", # multiple statements on one line (colon)
	"E741", # ambiguous variable name
	"E261", # two spaces before inline comment
]
3 Likes

Will the Glyphs-packaged Python for G3 stay at 3.11? So for plugins that should work in both versions, we’d need to make sure they work in 3.11 and 3.14. Or will there be separate branches for G3 and G4 in the glyphs-packages repo at some point?

The Python version downloaded from the Plugin Manager will remain at 3.11 for Glyphs 3. Scripts and plug-ins that should work in both app versions should continue to use macOS 10.12 and Python 3.11 as their minimum.

Oh, I see there’s also the minVersion/maxVersion mechanism in the packages.plist which can be used to separate G3/G4 versions of a plugin when necessary.

I am not sure if branching the Glyphs Package Index is necessary. For now, both Glyphs 3 and Glyphs 4 use the same index with min/max version guards.

I’ll post a topic specifically about packaging for Glyphs 4 and what is new there, but that will probably be later this week. If you are interested, have a look at my recent commits on the Package Index, in particular where I added updated versions of my plug-ins that are now using direct download instead of downloading/updating using Git:

(the Git-generated diff is a bit confusing; I added four plug-ins and added maxVersion = "3799"; to the old ones)

If I’ll update the GlyphsPython for Glyphs 3, it would make it difficult to run older versions of Glyphs 3 (some people do for compatible exports).

So the python version is stuck.

But as Florian said, you can make different versions of your plugins for Glyphs 3 and 4 and add both to the package file.