New Upstream Release - python-vttlib

Ready changes

Summary

Merged new upstream version: 0.12.0+dfsg (was: 0.11.0+dfsg).

Diff

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d45604f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.py text
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..0ebe939
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,64 @@
+name: Continuous Integration
+
+on:
+  push:
+    branches: [master]
+    tags: ["v*"]
+  pull_request:
+    branches: [master]
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python
+      uses: actions/setup-python@v4
+      with:
+        python-version: '3.x'
+    - name: Install packages
+      run: pip install tox
+    - name: Lint
+      run: tox -e lint
+
+  test:
+    runs-on: ${{ matrix.platform }}
+    strategy:
+      matrix:
+        python-version: ['3.7', '3.11']
+        platform: [ubuntu-latest, windows-latest]
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install packages
+      run: pip install tox
+    - name: Run Tox
+      run: tox -e py-cov
+
+  deploy:
+    # only run if the commit is tagged...
+    if: startsWith(github.ref, 'refs/tags/v')
+    # ... and the previous jobs completed successfully
+    needs:
+      - lint
+      - test
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python
+      uses: actions/setup-python@v4
+      with:
+        python-version: '3.x'
+    - name: Install packages
+      run: pip install build twine
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python -m build
+        twine check dist/*
+        twine upload dist/*
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9cd791f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,120 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+pip-wheel-metadata
+_version.py
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# Visual Studio Code
+.vscode
diff --git a/README.md b/README.md
index 0631e65..fe5930f 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,12 @@ The primary use case is version control of hinting data of fonts.
 
 ## Installation and Usage
 
-This package is not yet on PyPI, because... uh...
+Installation requires a Python 3.7+ interpreter.
+
+Install in a virtual environment with:
 
 ```bash
-$ pip install git+https://github.com/daltonmaag/vttLib.git
+$ pip install vttLib
 $ python -m vttLib --help
 ```
 
diff --git a/debian/changelog b/debian/changelog
index c1e8de4..f7741e7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-vttlib (0.12.0+dfsg-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 11 Apr 2023 01:58:56 -0000
+
 python-vttlib (0.11.0+dfsg-3) unstable; urgency=medium
 
   * Update metadata
diff --git a/debian/patches/0001-Remove-test-requiring-non-DFSG-data.patch b/debian/patches/0001-Remove-test-requiring-non-DFSG-data.patch
index 5d7dc0d..d657076 100644
--- a/debian/patches/0001-Remove-test-requiring-non-DFSG-data.patch
+++ b/debian/patches/0001-Remove-test-requiring-non-DFSG-data.patch
@@ -7,11 +7,11 @@ Forwarded: not-needed
  tests/vttLib_test.py | 26 --------------------------
  1 file changed, 26 deletions(-)
 
-diff --git a/tests/vttLib_test.py b/tests/vttLib_test.py
-index 22359a8..449f733 100644
---- a/tests/vttLib_test.py
-+++ b/tests/vttLib_test.py
-@@ -184,29 +184,3 @@ class TestTransformAssembly(object):
+Index: python-vttlib.git/tests/vttLib_test.py
+===================================================================
+--- python-vttlib.git.orig/tests/vttLib_test.py
++++ python-vttlib.git/tests/vttLib_test.py
+@@ -208,29 +208,3 @@ class TestTransformAssembly(object):
  
          assert expected == generated
  
diff --git a/mypy.ini b/mypy.ini
index 6600dd3..0270ba3 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,5 +1,5 @@
 [mypy]
-python_version = 3.6
+python_version = 3.7
 
 # Untyped definitions and calls
 disallow_incomplete_defs = True
diff --git a/pyproject.toml b/pyproject.toml
index 399a2e4..296c75e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,17 +1,30 @@
 [build-system]
 build-backend = "setuptools.build_meta"
-requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]
+requires = ["setuptools>=61", "setuptools_scm[toml]>=6.2"]
+
+[project]
+name = "vttLib"
+authors = [{ name = "Dalton Maag Ltd", email = "info@daltonmaag.com" }]
+description = "Compile Visual TrueType assembly with FontTools."
+readme = "README.md"
+requires-python = ">=3.7"
+license = {text = "MIT"}
+dependencies = [
+    "fonttools[ufo]>=4.0.0",
+    "pyparsing>=2.1.5",
+    "ufoLib2>=0.7.1",
+]
+dynamic = ["version"]
+
+[project.urls]
+Source = "https://github.com/daltonmaag/vttLib"
 
 [tool.setuptools_scm]
 write_to = "src/vttLib/_version.py"
 
 [tool.black]
-target-version = ["py36"]
+target-version = ["py37"]
 
 [tool.isort]
-multi_line_output = 3
-include_trailing_comma = true
-force_grid_wrap = 0
-use_parentheses = true
-line_length = 88
+profile = "black"
 known_first_party = "vttLib"
diff --git a/requirements-dev.in b/requirements-dev.in
index 41e0b68..b36c862 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -1,7 +1,7 @@
 black
 coverage
 flake8
-isort[pyproject]
+isort
 mypy
 pytest
 ufo2ft
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 5b1afdc..a8c05a7 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,45 +1,87 @@
 #
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.8
 # To update, run:
 #
-#    pip-compile requirements-dev.in
+#    pip-compile '.\requirements-dev.in'
 #
+--extra-index-url https://pypi.daltonmaag.com/simple/
+--no-binary damafontlab
 
-appdirs==1.4.4            # via black, fs
-atomicwrites==1.4.0       # via pytest
-attrs==19.3.0             # via black, pytest
-black==19.10b0            # via -r requirements-dev.in
-booleanoperations==0.9.0  # via ufo2ft
-click==7.1.2              # via black
-colorama==0.4.3           # via pytest
-compreffor==0.5.0         # via ufo2ft
-coverage==5.1             # via -r requirements-dev.in
-cu2qu==1.6.7              # via ufo2ft
-flake8==3.8.2             # via -r requirements-dev.in
-fonttools[ufo]==4.10.2    # via booleanoperations, compreffor, cu2qu, ufo2ft
-fs==2.4.11                # via fonttools
-isort[pyproject]==4.3.21  # via -r requirements-dev.in
-mccabe==0.6.1             # via flake8
-more-itertools==8.3.0     # via pytest
-mypy-extensions==0.4.3    # via mypy
-mypy==0.770               # via -r requirements-dev.in
-packaging==20.4           # via pytest
-pathspec==0.8.0           # via black
-pluggy==0.13.1            # via pytest
-py==1.8.1                 # via pytest
-pyclipper==1.1.0.post3    # via booleanoperations
-pycodestyle==2.6.0        # via flake8
-pyflakes==2.2.0           # via flake8
-pyparsing==2.4.7          # via packaging
-pytest==5.4.2             # via -r requirements-dev.in
-pytz==2020.1              # via fs
-regex==2020.5.14          # via black
-six==1.15.0               # via fs, packaging
-toml==0.10.1              # via black, isort
-typed-ast==1.4.1          # via black, mypy
-typing-extensions==3.7.4.2  # via mypy
-ufo2ft==2.14.0            # via -r requirements-dev.in
-wcwidth==0.1.9            # via pytest
+appdirs==1.4.4
+    # via fs
+attrs==22.1.0
+    # via pytest
+black==22.10.0
+    # via -r .\requirements-dev.in
+booleanoperations==0.9.0
+    # via ufo2ft
+cffsubr==0.2.9.post1
+    # via ufo2ft
+click==8.1.3
+    # via black
+colorama==0.4.6
+    # via
+    #   click
+    #   pytest
+coverage==6.5.0
+    # via -r .\requirements-dev.in
+cu2qu==1.6.7.post1
+    # via ufo2ft
+exceptiongroup==1.0.0rc9
+    # via pytest
+flake8==5.0.4
+    # via -r .\requirements-dev.in
+fonttools[ufo]==4.38.0
+    # via
+    #   booleanoperations
+    #   cffsubr
+    #   cu2qu
+    #   ufo2ft
+fs==2.4.16
+    # via fonttools
+iniconfig==1.1.1
+    # via pytest
+isort==5.10.1
+    # via -r .\requirements-dev.in
+mccabe==0.7.0
+    # via flake8
+mypy==0.982
+    # via -r .\requirements-dev.in
+mypy-extensions==0.4.3
+    # via
+    #   black
+    #   mypy
+packaging==21.3
+    # via pytest
+pathspec==0.10.1
+    # via black
+platformdirs==2.5.2
+    # via black
+pluggy==1.0.0
+    # via pytest
+pyclipper==1.3.0.post3
+    # via booleanoperations
+pycodestyle==2.9.1
+    # via flake8
+pyflakes==2.5.0
+    # via flake8
+pyparsing==3.0.9
+    # via packaging
+pytest==7.2.0
+    # via -r .\requirements-dev.in
+six==1.16.0
+    # via fs
+tomli==2.0.1
+    # via
+    #   black
+    #   mypy
+    #   pytest
+typing-extensions==4.4.0
+    # via
+    #   black
+    #   mypy
+ufo2ft==2.28.0
+    # via -r .\requirements-dev.in
 
 # The following packages are considered to be unsafe in a requirements file:
 # setuptools
diff --git a/requirements.txt b/requirements.txt
index dbf2248..2737487 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,18 +1,28 @@
 #
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.8
 # To update, run:
 #
-#    pip-compile --output-file=requirements.txt setup.py
+#    pip-compile --output-file=requirements.txt pyproject.toml
 #
+--extra-index-url https://pypi.daltonmaag.com/simple/
+--no-binary damafontlab
 
-appdirs==1.4.4            # via fs
-attrs==19.3.0             # via ufolib2
-fonttools[ufo]==4.10.2    # via ufolib2, vttLib (.\setup.py)
-fs==2.4.11                # via fonttools
-pyparsing==2.4.7          # via vttLib (.\setup.py)
-pytz==2020.1              # via fs
-six==1.15.0               # via fs
-ufolib2==0.7.1            # via vttLib (.\setup.py)
+appdirs==1.4.4
+    # via fs
+attrs==22.1.0
+    # via ufolib2
+fonttools[ufo]==4.38.0
+    # via
+    #   ufolib2
+    #   vttLib (pyproject.toml)
+fs==2.4.16
+    # via fonttools
+pyparsing==3.0.9
+    # via vttLib (pyproject.toml)
+six==1.16.0
+    # via fs
+ufolib2==0.13.1
+    # via vttLib (pyproject.toml)
 
 # The following packages are considered to be unsafe in a requirements file:
 # setuptools
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 93b405e..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,26 +0,0 @@
-[metadata]
-name = vttLib
-author = Dalton Maag Ltd
-author_email = info@daltonmaag.com
-home-page = https://github.com/daltonmaag/vttLib
-description = Compile Visual TrueType assembly with FontTools.
-license = MIT
-
-[options]
-python_requires = >=3.6
-packages = find:
-package_dir =
-    =src
-install_requires =
-    fonttools[ufo]>=4.0.0
-    pyparsing>=2.1.5
-    ufoLib2>=0.7.1
-setup_requires =
-    setuptools_scm[toml]>=3.4
-    wheel
-
-[options.packages.find]
-where=src
-
-[sdist]
-formats = zip
diff --git a/setup.py b/setup.py
deleted file mode 100644
index b908cbe..0000000
--- a/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import setuptools
-
-setuptools.setup()
diff --git a/src/vttLib/__init__.py b/src/vttLib/__init__.py
index 5d24cfa..21be54f 100644
--- a/src/vttLib/__init__.py
+++ b/src/vttLib/__init__.py
@@ -6,8 +6,8 @@ import re
 from collections import OrderedDict, defaultdict, deque, namedtuple
 
 import ufoLib2
-from fontTools.misc.arrayTools import Vector
 from fontTools.misc.fixedTools import otRound
+from fontTools.misc.vector import Vector
 from fontTools.ttLib import TTFont, TTLibError, newTable
 from fontTools.ttLib.tables._g_l_y_f import (
     ROUND_XY_TO_GRID,
@@ -288,6 +288,7 @@ def transform(tokens, components=None):
             for point_index, rel_ppem, step_no in reversed(t.deltas):
                 deltas.setdefault(point_index, []).append((rel_ppem, step_no))
 
+            pairs = []
             for point_index, delta_specs in deltas.items():
                 for rel_ppem, step_no in sorted(delta_specs, reverse=True):
                     if mnemonic.startswith(("DELTAP", "DELTAC")):
@@ -302,10 +303,17 @@ def transform(tokens, components=None):
                             delta_base = 41
                         # subtract the default 'delta base'
                         rel_ppem -= delta_base
-                    stack.appendleft(point_index)
                     # -8: 0, ... -1: 7, 1: 8, ... 8: 15
                     selector = (step_no + 7) if step_no > 0 else (step_no + 8)
-                    stack.appendleft((rel_ppem << 4) | selector)
+                    pairs.append(((rel_ppem << 4) | selector, point_index))
+
+            # Reorder by exception spec, workaround for an issue where only the
+            # first delta is active when the spec/index pairs are ordered in a
+            # different way. This results in an order identical to what VTT does.
+            for spec, point_index in sorted(pairs, reverse=True):
+                stack.appendleft(point_index)
+                stack.appendleft(spec)
+
             if mnemonic.startswith("DLT"):
                 mnemonic = mnemonic.replace("DLT", "DELTA")
         elif mnemonic.startswith("#") and mnemonic.endswith(":"):
diff --git a/src/vttLib/parser.py b/src/vttLib/parser.py
index 871b161..c266391 100644
--- a/src/vttLib/parser.py
+++ b/src/vttLib/parser.py
@@ -82,7 +82,10 @@ delta_spec = (
     + Optional(Literal("/8")).suppress()
 )
 
-delta = nestedExpr("(", ")", delta_spec, ignoreExpr=None)
+# NOTE: The type-ignore is weird, the docs at
+# https://pyparsing-docs.readthedocs.io/en/latest/pyparsing.html?highlight=nestedExpr#pyparsing.nested_expr
+# say that passing None is okay, but the typing says something else.
+delta = nestedExpr("(", ")", delta_spec, ignoreExpr=None)  # type: ignore
 
 deltas = Group(OneOrMore(delta)).setResultsName("deltas")
 
diff --git a/tests/vttLib_test.py b/tests/vttLib_test.py
index 22359a8..64109b3 100644
--- a/tests/vttLib_test.py
+++ b/tests/vttLib_test.py
@@ -175,6 +175,30 @@ class TestTransformAssembly(object):
             ).strip()
         )
 
+    def test_delta_args_sorting(self):
+
+        vtt_assembly = dedent(
+            """
+            DLTC1[(4 @4 8) (4 @8 8) (4 @11 8) (4 @15 8) (5 @4 8) (5 @8 8) (5 @11 8) (5 @15 8) (12 @1 8) (12 @4 8) (12 @5 8) (12 @8 8) (12 @9 8) (12 @13 8) (12 @15 8) (12 @0 8) (13 @1 8) (13 @4 8) (13 @5 8) (13 @8 8) (13 @9 8) (13 @13 8) (13 @15 8) (13 @0 8) (14 @11 8) (14 @13 8) (14 @15 8) (15 @11 8) (15 @13 8) (15 @15 8)]
+            DLTC2[(4 @3 8) (4 @6 8) (4 @7 8) (4 @10 8) (4 @14 8) (5 @3 8) (5 @6 8) (5 @7 8) (5 @10 8) (5 @14 8) (12 @1 8) (12 @3 8) (12 @4 8) (12 @5 8) (12 @9 8) (12 @13 8) (13 @1 8) (13 @3 8) (13 @4 8) (13 @5 8) (13 @9 8) (13 @13 8) (14 @1 8) (14 @3 8) (14 @5 8) (14 @7 8) (14 @9 8) (14 @11 8) (14 @13 8) (15 @1 8) (15 @3 8) (15 @5 8) (15 @7 8) (15 @9 8) (15 @11 8) (15 @13 8)]
+            DLTC3[(4 @1 8) (4 @2 8) (4 @5 8) (5 @1 8) (5 @2 8) (5 @5 8) (12 @1 8) (12 @4 8) (13 @1 8) (13 @4 8) (14 @0 8) (14 @2 8) (14 @4 8) (15 @0 8) (15 @2 8) (15 @4 8)]
+            """
+        )
+
+        ft_assembly = transform_assembly(vtt_assembly)
+
+        assert (
+            ft_assembly
+            == dedent(
+                """
+                PUSH[] 15 14 15 15 31 4 31 5 31 12 31 13 47 4 47 5 47 14 47 15 79 12 79 13 79 14 79 15 95 4 95 5 16 31 12 31 13 31 14 31 15 63 4 63 5 63 12 63 13 63 14 63 15 79 12 79 13 95 12 95 13 95 14 95 15 111 4 111 5 127 4 127 5 127 14 127 15 159 12 159 13 159 14 159 15 175 4 175 5 191 14 191 15 223 12 223 13 223 14 223 15 239 4 239 5 36 15 12 15 13 31 12 31 13 79 4 79 5 79 12 79 13 95 12 95 13 143 4 143 5 143 12 143 13 159 12 159 13 191 4 191 5 191 14 191 15 223 12 223 13 223 14 223 15 255 4 255 5 255 12 255 13 255 14 255 15 30
+                DELTAC1[]
+                DELTAC2[]
+                DELTAC3[]
+                """
+            ).strip()
+        )
+
     def test_end_to_end(self, input_and_expected):
         vtt_assembly, expected = input_and_expected
 
diff --git a/tox.ini b/tox.ini
index cec2b83..c2269f4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,7 @@
 [tox]
-envlist =  lint, py3{6,8}-cov, htmlcov
+envlist =  lint, py3{7,8,9,10,11}-cov, htmlcov
+skip_missing_interpreters = true
+isolated_build = true
 
 [testenv]
 deps =
@@ -26,8 +28,8 @@ deps =
     -r requirements-dev.txt
 commands =
     black --check --diff .
-    isort --check-only --diff --recursive src tests
-    mypy src tests .
+    isort --check-only --diff src tests
+    mypy src tests
     flake8
 
 [flake8]

More details

Full run details

Historical runs