这两天学习用 fontforge 制作字体。fontforge 的 python api 很方便,奈何它没有 单独的 python 包,而是 用 python C api 调用其 C 源代码,最后编译得到.so
文件,而非一般的 python module,因此 pylance 无法使用语法高亮。通过询问 gpt 我得知可以用 python index file .pyi
文件供 pylance 索引,它的功能和 C 语言中的头文件 .h
类似,只描述函数签名、参数类型、返回类型等接口形状,很适合用于静态分析。与此前在 Typescript 中接触的 .d.ts
文件有异曲同工之妙。
开始我想有没有一种自动化工具可以直接解析.so
库生成.pyi
文件,随即找到 mypy,运行以下代码:
# sudo apt install python3-fontforge #这会将 fontforge 安装到 ubuntu 系统的顶级 python 解释器(/bin/python) 环境 (dist-package) 中
pip install mypy
stubgen -m fontforge -o . # 前提是从 apt 上安装了 python3-fontforge 包,而不是打包好的应用程序
生成代码(节选)如下:
from _typeshed import Incomplete
from typing import Any
__date__: str
__version__: str
hooks: dict
spiroCorner: int
spiroG2: int
spiroG4: int
class font:
activeLayer: Incomplete
ascent: Incomplete
bitmapSizes: Incomplete
capHeight: Incomplete
changed: Incomplete
@classmethod
def __init__(cls, *args, **kwargs) -> None: ...
def addAnchorClass(self, *args, **kwargs): ...
def addContextualSubtable(self, *args, **kwargs): ...
def addExtrema(self, *args, **kwargs): ...
def addInflections(self, *args, **kwargs): ...
def addKerningClass(self, *args, **kwargs): ...
def addLookup(self, *args, **kwargs): ...
def addLookupSubtable(self, *args, **kwargs): ...
class glyph:
activeLayer: Incomplete
altuni: Incomplete
anchorPoints: Incomplete
anchorPointsWithSel: Incomplete
background: Incomplete
changed: Incomplete
def addAnchorPoint(self, *args, **kwargs): ...
def addExtrema(self, *args, **kwargs): ...
def addHint(self, *args, **kwargs): ...
def addInflections(self, *args, **kwargs): ...
def addPosSub(self, *args, **kwargs): ...
def addReference(self, *args, **kwargs): ...
def appendAccent(self, *args, **kwargs): ...
def autoHint(self, *args, **kwargs): ...
def autoInstr(self, *args, **kwargs): ...
def autoTrace(self, *args, **kwargs): ...
def balance(self, *args, **kwargs): ...
def boundingBox(self, *args, **kwargs): ...
def build(self, *args, **kwargs): ...
def canonicalContours(self, *args, **kwargs): ...
def canonicalStart(self, *args, **kwargs): ...
def changeWeight(self, *args, **kwargs): ...
def clear(self, *args, **kwargs): ...
def cluster(self, *args, **kwargs): ...
def condenseExtend(self, *args, **kwargs): ...
def correctDirection(self, *args, **kwargs): ...
def doUndoLayer(self, *args, **kwargs): ...
def draw(self, *args, **kwargs): ...
def exclude(self, *args, **kwargs): ...
def export(self, *args, **kwargs): ...
def genericGlyphChange(self, *args, **kwargs): ...
def getPosSub(self, *args, **kwargs): ...
def glyphPen(self, *args, **kwargs): ...
嗯,模块级属性、方法,类级属性、方法,该有的都有,但是没有我最想要的参数名提示、类型提示、docstring。毕竟,fontforge api 的文档 里写的可比这详细多了。于是我在想,这个文档是不是自动生成的?如果是的话,是不是可以用生成文档的办法生成 .pyi
?
果然经过一番搜索,fontforge 使用了 sphinx 生成文档。然而,在源码仓库里看不懂 调用 sphinx 的代码。对于正常的 python package,sphinx 有一套方便的流程,但恕我实在搞不定 fontforge 这个大型项目。等哪一天 LLM 可以直接分析整个代码库,我一定要问问 fontforge 的 sphinx 文档是怎么自动生成的。
最后万般无奈,我还是从 fontforge 文档提供的函数签名一点点改写 .pyi
文件,好在我要使用的功能不多,所以工作量不大。只需要把该文件放在工作目录下,vscode 的 pylance 就会自动找到,更保险的做法是配置 settings.json
如下:
{
"python.analysis.extraPaths": [
"path/to/your/type_stubs"
],
"python.analysis.typeCheckingMode": "strict"
}