Name | fakelab JSON |
Version |
0.1.0
JSON |
| download |
home_page | None |
Summary | Fake FontLab Studio 5 for automated tests and external scripting |
upload_time | 2025-08-12 14:37:46 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.11 |
license | None |
keywords |
vfb
fonttools
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# FakeLab
Test-driven development for FontLab Studio 5 Python macros and modules.
FakeLab is a FontLab Studio 5 replacement for testing Python code.
Everything is only implemented so far as to make FontLab objects importable
outside of FontLab Studio 5, and run tests.
It is suggested to install FakeLab in a virtual environment so FontLab won't
accidentally import the fake module when running the scripts in actual FontLab
Studio 5. If you can live with the incompatibilites between Python 2.7 and 3,
you can also run FakeLab in Python 3.
Saving VFBs is not supported, as the VFB format is not public, but you can save
`Font` objects as JSON.
The implementation of FakeLab is based on the invaluable
[Unofficial FontLab/Python API Reference](http://www.e-font.de/flpydoc/), and
from running scripts in FontLab Studio and checking what they do, apart from
crashing the application.
## Installation
When you have activated your virtual environment:
```bash
$ pip install -e .
```
FL is then importable outside of FontLab Studio:
```python
from FL import fl, Font
# Make an empty font
f = Font()
# Add the font to the mock app
fl.Add(f)
# Close the font
fl.Close()
```
If your are running in a virtual environment, and need to make your FontLab
modules importable, add a `.pth` file in your virtual environment's site-packages
directory:
```
# fl5modules.pth
/Users/<yourname>/Code/FLMacros/System/Modules
```
## A word of caution
FakeLab is a work in progress, and only implemented as far as I needed it in my
own scripts. I have to admit that the bulk of them still has no tests.
If you encounter a `NotImplementedError` while writing tests, this is where you
can excel at helping to improve this project ;) I'll be happy to accept your
[pull requests](https://github.com/jenskutilek/fakelab/pulls). Alternatively,
[open an issue](https://github.com/jenskutilek/fakelab/issues),
[buy me a coffee](https://ko-fi.com/jenskutilek), and hope that my kids leave
me alone so I can find some time to work on your issue.
## Writing tests
Developing scripts without automated testing is really only for very small
projects. To be sure of the outcomes of a module or script, you should always
write tests. This is usually done using
[pytest](https://docs.pytest.org/en/stable/).
### Tests example
Let's assume you have a FontLab script to select glyphs containing components.
If you have your own tools collection for FontLab, this script may consist of
two parts: One script that is listed in FontLab's macro toolbar, and one Python
module implementing the logic, which is called by the toolbar script.
```
Studio 5
+- Macros
+- Selection
+- Select Composites.py
+- System
+- Modules
+- fakeLabDemo
+- selection
+- __init__.py
+- composites.py
```
The `Select Composites.py` script looks like this:
```python
#FLM: Select composites
# Studio 5/Macros/Selection/Select Composites.py
from fakeLabDemo.selection.composites import selectComposites
selectComposites(fl.font)
```
And the module:
```python
# Studio 5/Macros/System/Modules/fakeLabDemo/selection/composites.py
from __future__ import absolute_import, division, print_function
from FL import fl
def getFontIndex(font):
"""
Get the index of the supplied font.
We must iterate through the open fonts and compare file names,
because the == operator can not compare the font objects directly.
(FL font objects get a different id() each time they are called)
:param font: A title for the dialog.
:type font: :py:class:`FL.Font`
"""
for i in range(len(fl)):
cf = fl[i]
if cf.file_name == font.file_name:
if font.file_name is None:
if (
cf.family_name == font.family_name
and cf.style_name == font.style_name
):
return (cf, i)
else:
return (cf, i)
# Font was not found, probably there are no open fonts
return (None, -1)
def setSelection(font, glyph_names):
"""
Set glyphs from the glyph_names list as selected in the font window.
"""
f, i = getFontIndex(font)
if i > -1:
fl.ifont = i
fl.Unselect()
for n in glyph_names:
fl.Select(n)
def selectComposites(font):
"""
Select composites in font.
"""
setSelection(
font,
[
glyph.name
for glyph in font.glyphs
if glyph.components
]
)
```
How can we be sure this script does what it is supposed to do? For pytest, we
add another parallel folder structure to the existing structure:
```
Studio 5
+- Macros
+- Selection
+- Select Composites.py
+- System
+- Modules
+- fakeLabDemo
+- selection
+- __init__.py
+- composites.py
+- tests
+- fakeLabDemo
+- selection
+- composites_test.py
```
The file `composites_test.py`, which is named analogous to the module file it
relates to, is where we will implement our tests:
```python
# Studio 5/Macros/System/Modules/tests/fakeLabDemo/selection/composites_test.py
import pytest
from FL import fl, Component, Font, Glyph, Point
from fakeLabDemo.selection.composites import selectComposites
def test_selectComposites():
# Construct a fake FontLab font object
font = Font()
g = Glyph(1)
g.name = "A"
g.width = 500
g.unicode = 0x41
font.glyphs.append(g)
g = Glyph(1)
g.name = "dieresis"
g.width = 500
g.unicode = 0xA8
font.glyphs.append(g)
g = Glyph(1)
g.name = "Adieresis"
g.width = 500
g.unicode = 0xC4
g.components.append(Component(0))
g.components.append(Component(1, Point(0, 300)))
font.glyphs.append(g)
# Add the font to the FL object
fl.Add(font)
fl.UpdateFont()
# Run our script to be tested on the font
selectComposites(fl.font)
# You could save the fake font to JSON instead of VFB.
# fl.font.Save("test_composites.vfb.json")
# Test if the correct glyphs have been selected
assert fl.Selected(0) == 0
assert fl.Selected(1) == 0
assert fl.Selected(2) == 1
# Close the fake font
fl.Close()
```
As you see, you can use the objects just as you would inside FontLab. You just
can not open a font from an existing VFB, which would be much easier. But the
VFB file format is not public.
Instead, you have to construct a test font using the
[FL Python API](http://www.e-font.de/flpydoc/).
Invoke the test script in a Terminal window while your virtual environment is
active:
```bash
cd "Studio 5/Macros/System/Modules"
python -m pytest tests/fakeLabDemo/selection/composites_test.py
```
If everything works out, you will see some output like this:
```
============================ test session starts ===============================
platform darwin -- Python 3.8.7, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/jens/Code/fakelab
collected 1 item
tests/fakeLabDemo/selection/composites_test.py . [100%]
============================= 1 passed in 0.02s ================================
```
Raw data
{
"_id": null,
"home_page": null,
"name": "fakelab",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "vfb, fonttools",
"author": null,
"author_email": "Jens Kut\u00edlek <webmail@kutilek.de>",
"download_url": "https://files.pythonhosted.org/packages/05/65/eedd0fe83ce91510a1f1cb793f0cf78631763158b5a3398c4e26d952856b/fakelab-0.1.0.tar.gz",
"platform": null,
"description": "# FakeLab\n\nTest-driven development for FontLab Studio 5 Python macros and modules.\n\nFakeLab is a FontLab Studio 5 replacement for testing Python code.\n\nEverything is only implemented so far as to make FontLab objects importable\noutside of FontLab Studio 5, and run tests.\n\nIt is suggested to install FakeLab in a virtual environment so FontLab won't\naccidentally import the fake module when running the scripts in actual FontLab\nStudio 5. If you can live with the incompatibilites between Python 2.7 and 3,\nyou can also run FakeLab in Python 3.\n\nSaving VFBs is not supported, as the VFB format is not public, but you can save\n`Font` objects as JSON.\n\nThe implementation of FakeLab is based on the invaluable\n[Unofficial FontLab/Python API Reference](http://www.e-font.de/flpydoc/), and\nfrom running scripts in FontLab Studio and checking what they do, apart from\ncrashing the application.\n\n## Installation\n\nWhen you have activated your virtual environment:\n\n```bash\n$ pip install -e .\n```\n\nFL is then importable outside of FontLab Studio:\n\n```python\nfrom FL import fl, Font\n\n# Make an empty font\nf = Font()\n\n# Add the font to the mock app\nfl.Add(f)\n\n# Close the font\nfl.Close()\n```\n\nIf your are running in a virtual environment, and need to make your FontLab\nmodules importable, add a `.pth` file in your virtual environment's site-packages\ndirectory:\n\n```\n# fl5modules.pth\n/Users/<yourname>/Code/FLMacros/System/Modules\n```\n\n## A word of caution\n\nFakeLab is a work in progress, and only implemented as far as I needed it in my\nown scripts. I have to admit that the bulk of them still has no tests.\n\nIf you encounter a `NotImplementedError` while writing tests, this is where you\ncan excel at helping to improve this project ;) I'll be happy to accept your\n[pull requests](https://github.com/jenskutilek/fakelab/pulls). Alternatively,\n[open an issue](https://github.com/jenskutilek/fakelab/issues),\n[buy me a coffee](https://ko-fi.com/jenskutilek), and hope that my kids leave\nme alone so I can find some time to work on your issue.\n\n## Writing tests\n\nDeveloping scripts without automated testing is really only for very small\nprojects. To be sure of the outcomes of a module or script, you should always\nwrite tests. This is usually done using\n[pytest](https://docs.pytest.org/en/stable/).\n\n### Tests example\n\nLet's assume you have a FontLab script to select glyphs containing components.\nIf you have your own tools collection for FontLab, this script may consist of\ntwo parts: One script that is listed in FontLab's macro toolbar, and one Python\nmodule implementing the logic, which is called by the toolbar script.\n\n```\nStudio 5\n+- Macros\n +- Selection\n +- Select Composites.py\n +- System\n +- Modules\n +- fakeLabDemo\n +- selection\n +- __init__.py\n +- composites.py\n```\n\nThe `Select Composites.py` script looks like this:\n\n```python\n#FLM: Select composites\n# Studio 5/Macros/Selection/Select Composites.py\nfrom fakeLabDemo.selection.composites import selectComposites\nselectComposites(fl.font)\n```\n\nAnd the module:\n```python\n# Studio 5/Macros/System/Modules/fakeLabDemo/selection/composites.py\nfrom __future__ import absolute_import, division, print_function\n\nfrom FL import fl\n\n\ndef getFontIndex(font):\n \"\"\"\n Get the index of the supplied font.\n We must iterate through the open fonts and compare file names,\n because the == operator can not compare the font objects directly.\n (FL font objects get a different id() each time they are called)\n\n :param font: A title for the dialog.\n :type font: :py:class:`FL.Font`\n \"\"\"\n for i in range(len(fl)):\n cf = fl[i]\n if cf.file_name == font.file_name:\n if font.file_name is None:\n if (\n cf.family_name == font.family_name\n and cf.style_name == font.style_name\n ):\n return (cf, i)\n else:\n return (cf, i)\n # Font was not found, probably there are no open fonts\n return (None, -1)\n\n\ndef setSelection(font, glyph_names):\n \"\"\"\n Set glyphs from the glyph_names list as selected in the font window.\n \"\"\"\n f, i = getFontIndex(font)\n if i > -1:\n fl.ifont = i\n fl.Unselect()\n for n in glyph_names:\n fl.Select(n)\n\n\ndef selectComposites(font):\n \"\"\"\n Select composites in font.\n \"\"\"\n setSelection(\n font,\n [\n glyph.name\n for glyph in font.glyphs\n if glyph.components\n ]\n )\n```\n\nHow can we be sure this script does what it is supposed to do? For pytest, we\nadd another parallel folder structure to the existing structure:\n\n```\nStudio 5\n+- Macros\n +- Selection\n +- Select Composites.py\n +- System\n +- Modules\n +- fakeLabDemo\n +- selection\n +- __init__.py\n +- composites.py\n +- tests\n +- fakeLabDemo\n +- selection\n +- composites_test.py\n```\n\nThe file `composites_test.py`, which is named analogous to the module file it\nrelates to, is where we will implement our tests:\n\n```python\n# Studio 5/Macros/System/Modules/tests/fakeLabDemo/selection/composites_test.py\nimport pytest\n\nfrom FL import fl, Component, Font, Glyph, Point\nfrom fakeLabDemo.selection.composites import selectComposites\n\n\ndef test_selectComposites():\n # Construct a fake FontLab font object\n font = Font()\n g = Glyph(1)\n g.name = \"A\"\n g.width = 500\n g.unicode = 0x41\n font.glyphs.append(g)\n\n g = Glyph(1)\n g.name = \"dieresis\"\n g.width = 500\n g.unicode = 0xA8\n font.glyphs.append(g)\n\n g = Glyph(1)\n g.name = \"Adieresis\"\n g.width = 500\n g.unicode = 0xC4\n g.components.append(Component(0))\n g.components.append(Component(1, Point(0, 300)))\n font.glyphs.append(g)\n\n # Add the font to the FL object\n fl.Add(font)\n fl.UpdateFont()\n\n # Run our script to be tested on the font\n selectComposites(fl.font)\n\n # You could save the fake font to JSON instead of VFB.\n # fl.font.Save(\"test_composites.vfb.json\")\n\n # Test if the correct glyphs have been selected\n assert fl.Selected(0) == 0\n assert fl.Selected(1) == 0\n assert fl.Selected(2) == 1\n\n # Close the fake font\n fl.Close()\n```\n\nAs you see, you can use the objects just as you would inside FontLab. You just\ncan not open a font from an existing VFB, which would be much easier. But the\nVFB file format is not public.\n\nInstead, you have to construct a test font using the\n[FL Python API](http://www.e-font.de/flpydoc/).\n\nInvoke the test script in a Terminal window while your virtual environment is\nactive:\n\n```bash\ncd \"Studio 5/Macros/System/Modules\"\npython -m pytest tests/fakeLabDemo/selection/composites_test.py\n```\n\nIf everything works out, you will see some output like this:\n\n```\n============================ test session starts ===============================\nplatform darwin -- Python 3.8.7, pytest-6.0.1, py-1.9.0, pluggy-0.13.1\nrootdir: /Users/jens/Code/fakelab\ncollected 1 item\n\ntests/fakeLabDemo/selection/composites_test.py . [100%]\n\n============================= 1 passed in 0.02s ================================\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "Fake FontLab Studio 5 for automated tests and external scripting",
"version": "0.1.0",
"project_urls": {
"Bug Tracker": "https://github.com/jenskutilek/fakelab/issues",
"Changelog": "https://github.com/jenskutilek/fakelab/blob/develop/docs/CHANGELOG.md",
"Documentation": "https://fakelab.readthedocs.io/",
"Homepage": "https://github.com/jenskutilek/fakelab"
},
"split_keywords": [
"vfb",
" fonttools"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "68f0c7d779f2d139540970f70836787ef99105a793936d09791aee01146ece9a",
"md5": "bbcd24b67a3b7c21fabb1d24dfce60cc",
"sha256": "e710e79730874450d490ed02aa2281dc4280da3f3ced152d3ad26e251c3c247d"
},
"downloads": -1,
"filename": "fakelab-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bbcd24b67a3b7c21fabb1d24dfce60cc",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 144665,
"upload_time": "2025-08-12T14:37:45",
"upload_time_iso_8601": "2025-08-12T14:37:45.554449Z",
"url": "https://files.pythonhosted.org/packages/68/f0/c7d779f2d139540970f70836787ef99105a793936d09791aee01146ece9a/fakelab-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "0565eedd0fe83ce91510a1f1cb793f0cf78631763158b5a3398c4e26d952856b",
"md5": "1d786283295ef3b0ac535808b5dccef7",
"sha256": "07e32acb061c9e61882289b2f2e148e28674fccd81b36479b0f3d76a6955db06"
},
"downloads": -1,
"filename": "fakelab-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "1d786283295ef3b0ac535808b5dccef7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 382563,
"upload_time": "2025-08-12T14:37:46",
"upload_time_iso_8601": "2025-08-12T14:37:46.963182Z",
"url": "https://files.pythonhosted.org/packages/05/65/eedd0fe83ce91510a1f1cb793f0cf78631763158b5a3398c4e26d952856b/fakelab-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-12 14:37:46",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jenskutilek",
"github_project": "fakelab",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "fakelab"
}