Glyphs Python Interpreter

Is there a way to somehow select Glyphs python as the python interpreter in VSCode?

I get a lot of warnings from Pylance about missing classes/imports when using Glyphs builtin objects, which annoy me. Unfortunately Pylance does not offer any muting for certain items. I thought one solution could be to select the G Python as the interpreter (maybe that could also enforce autocompletion to a certain degree, but not needed much here).

When asking for the location of the Python, I just get the Glyphs binary, which I can’t use as the interpreter in VSC.

Or is there another trick to shut linters up on things like GlyphsApp, Glyphs, Font, AppKit, vanilla, etc?

Somehow related, I want to implement some unit testing that I could run outside of Glyphs. For this I’d also need to somehow access the Glyphs builtin modules and classes or at least mock them. Is there any way, to get an external IDE to “know” about those?
Or is there another way to execute the unit tests of a python plugin, maybe from inside of Glyphs then?

Thanks so much!

There is no way to get direct access to the interpreter that runs inside glyphs. The object model and API is only available while Glyphs is running. You would need to run the editor and the tools from within Glyphs.

There is the script that scan access Glyphs but that runs through a distributed objects tunnel. That doesn’t even allow to apply the wrapper so I doubt it will help you.

Maybe the tools you are using have an API/plugins that would awake it possible to write a plug-in for Glyphs that could communicate with you setup. But I don’t know anything about that.

Thanks a lot! I was expecting that. Fair enough.

In the mean time I managed to write a little wrapper to execute a unittest on a python plugin, which I can run from Glyphs. That’s probably even better, as I can use an open file instead of creating some mock objects.

So the Linting is something likely insolvable, the Unit Testing is solved. Thanks so much for your input. That helps a lot to better stop researching :smile:

You can add extra paths to the VSCode configuration which will be used to resolve imports etc. For example, I have:

    "python.autoComplete.extraPaths": [
        "/Users/kuti/Library/Application Support/FontLab/Studio 5/Macros/System/Modules",
    "python.analysis.extraPaths": [
        "/Users/kuti/Library/Application Support/FontLab/Studio 5/Macros/System/Modules",

The GlyphsSDK folder is a clone of the git repo.

You still need to add

from GlyphsApp import Glyphs

to your scripts to not get the “undefined name” error, but it does not hurt :wink:

Thanks a lot @jkutilek!

I almost had that, but ".../GlyphsSDK/ObjectWrapper/GlyphsApp" instead of ".../GlyphsSDK/ObjectWrapper". No Idea where that came from.

Now I changed it to end with ObjectWrapper, and it partially works.
When I add from GlyphsApp import Glyphs or even from GlyphsApp import *, the Glyphs class is not complaining, but things like Font still are not recognized. Is that the same for you? I see it has access to everything that is stated in the __all__ of the object wrapper.

There is also some autocompletion working, but not much.

Yeah, it’s the same for me. Support is partial at best … there may be some things missing from the object wrapper, e.g. Font and Layer (they can not be imported in Glyphs either, but are “just there”). And the missing autocomplete and docs are probably due to the unusual way the wrapper is built, mostly referencing objc classes and methods?

Thanks Jens!

I don’t have much experience with this, but do you think that type stubs could be used in this case?

Thanks Florian. Interesting find! Maybe that could help. I’ll have a look and let you know :smiley:

This works like a charm :heart:

Steps to recreate, if anyone is interested:

  • Requires Pylance
  • Create a _GlyphsStubs.pyi file (any name you want) and put it in the top level scripts directory (or anywhere, where it makes sense to you).
  • Then in your VSCode settings.json add "python.analysis.stubPath": "[...]/_GlyphsStubs.pyi", (where the path points to that .pyi file)
  • In any python script add from _GlyphsStubs import *

Now depending on the content of the .pyi file, the linter accepts these objects like Glyphs or Font and the cool thing is, it offers autocompletion and even documentation (if there is any in the stub file).

Depending of how much my stub file might grow, it could be a nice addition to the object wrapper :thinking:

1 Like

Thanks for trying it! It’s probably best if there is one shared stub file instead of everyone having to redo the same work over and over again. Either released bundled together with Glyphs or published on GitHub.

The type stub system is more general; other software (like mypy, or PyCharm, I believe) can also read these .pyi files, in case someone is not using Visual Studio Code.

1 Like

That’s what I meant. It could be part of the object wrapper in the Glyphs SDK. I am currently adding a few things and see how it goes. Then I could add it there for improvement and completion.

When we are at this. How much would it help to add type annotation to the wrapper?

What do you mean you’re at this? Florian just proposed type stubs to have a look at, so I did :slight_smile: Primarily I just want to get rid of false negative linting in my own, personal python environment, and it does not need to be complete for me. So I would just add the few classes/objects that VSCode complains about to solve that for me. I don’t think that it might be too useful for more than a handful other users, but would be happy to share a WIP file somewhere.

Hard to say. For example when I add constants to the stubs file like so, they are recognized. Those constants are also in the wrapper (and even properly declared there), but are not recognized.

# ...
# ...

Likewise, in the stubs there could be sth like that to be recognized:

class GSLayer:
	def __init__(self): ...
class Layer(GSLayer):
	The currently active Layer
	def __init__(self): ...
	def components(self) -> List[GSComponent]:
		"""Proxy for Layer Components"""

So it’s basically more like a header file. The ellipsis are literally three dots and required, or if a docstring is added to a method, it needs a pass after that instead.

Glyphs is recognized from the wrapper, when adding from GlyphsApp import Glyphs or from GlyphsApp import *, but things like Font or Layer are not.

Not sure how I’d need to add type annotations to the wrapper in order to make it work.

import from the wrapper:

import from the stubs file:

I ment this: typing — Support for type hints — Python 3.11.2 documentation

Yes, I know. That’s what I was talking about.

The type hints can be added in the stub file methods (I just left them out in that example, because those are not needed, but it’s better when they are there. I updated the example with the components above).

Adding the type hints directly in the object wrapper: I don’t know if it could work. For me here it didn’t.

But what did you mean with “we are at this”? :nerd_face:

Anyway, let’s not get too crazy with it. I think I got what I needed and am happy now :robot:

I think Georg meant “While we are at it” …

I think because of the way most of the Python API is just linked to methods in objc classes, “normal” static analysis doesn’t work well here.

Adding stubs may be the way to go. I already have a small collection here: Python-Stubs/__init__.pyi at main · jenskutilek/Python-Stubs · GitHub

Hi Jens, thank you for clarifying.

We have already established that stubs are the way to go. I was talking in my recent posts about them :wink:

But thanks for pointing to your version. It seems similar to what I made here for myself now. :+1:

@jkutilek Just had a look at your stubs file. Why did you add the GSHint.setOriginIndex_ ? shouldn’t be the hint.originIndex property be enough?

I was using those methods in my code. I’ve updated the code to use the Python API and also updated the stubs. But I need the setters there as well, so the code doesn’t get shorter :wink:

class GSHint:
    def copy(self) -> GSHint: ...
    def isTrueType(self) -> bool: ...
    def stem(self) -> int: ...
    def stem(self, value: int) -> None: ...
    def type(self) -> int: ...
    def originIndex(self) -> NSIndexPath: ...
    def originIndex(self, value: NSIndexPath): ...
    def targetIndex(self) -> NSIndexPath: ...
    def targetIndex(self, value: NSIndexPath): ...
    def otherIndex1(self) -> NSIndexPath: ...
    def otherIndex1(self, value: NSIndexPath): ...
    def otherIndex2(self) -> NSIndexPath: ...
    def otherIndex2(self, value: NSIndexPath): ...

(In this case I needed the stubs because I was trying to compile the Python module to c with mypyc.)