pax_global_header00006660000000000000000000000064141402250730014510gustar00rootroot0000000000000052 comment=5f91e9e7394938ce2192bcc254635880af1b70b4 statmake-0.4.1/000077500000000000000000000000001414022507300133235ustar00rootroot00000000000000statmake-0.4.1/.coveragerc000066400000000000000000000014331414022507300154450ustar00rootroot00000000000000[run] # measure 'branch' coverage in addition to 'statement' coverage # See: http://coverage.readthedocs.org/en/coverage-4.0.3/branch.html#branch branch = True parallel = True omit = tests/* # list of directories or packages to measure source = statmake # these are treated as equivalent when combining data [paths] source = statmake */site-packages [report] # Regexes for lines to exclude from consideration exclude_lines = # keywords to use in inline comments to skip coverage pragma: no cover # don't complain if tests don't hit defensive assertion code raise AssertionError raise NotImplementedError # don't complain if non-runnable code isn't run if 0: if __name__ == .__main__.: # ignore source code that can’t be found ignore_errors = True statmake-0.4.1/.editorconfig000066400000000000000000000004061414022507300160000ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.{yaml,yml,json,xml,designspace,stylespace,md}] indent_style = space indent_size = 2 statmake-0.4.1/.gitattributes000066400000000000000000000003251414022507300162160ustar00rootroot00000000000000# 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 statmake-0.4.1/.github/000077500000000000000000000000001414022507300146635ustar00rootroot00000000000000statmake-0.4.1/.github/workflows/000077500000000000000000000000001414022507300167205ustar00rootroot00000000000000statmake-0.4.1/.github/workflows/ci.yml000066400000000000000000000027531414022507300200450ustar00rootroot00000000000000name: Continuous Integration on: push: branches: [master] tags: ['v*.*.*'] pull_request: branches: [master] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Lint run: | pip install poetry tox tox -e lint test: runs-on: ${{ matrix.platform }} strategy: matrix: python-version: ['3.7', '3.10'] platform: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Test with tox run: | pip install poetry tox 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@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies run: | pip install poetry - name: Build and publish env: POETRY_HTTP_BASIC_PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} POETRY_HTTP_BASIC_PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | poetry publish --build statmake-0.4.1/.gitignore000066400000000000000000000022661414022507300153210ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # 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/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # dotenv .env # virtualenv .venv venv/ ENV/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # VS Code .vscode .idea # Fontlab .vfb .bak statmake-0.4.1/.pylintrc000066400000000000000000000004521414022507300151710ustar00rootroot00000000000000[MESSAGES CONTROL] disable= all enable= E, F, anomalous-backslash-in-string, bad-format-string, bad-open-mode binary-op-exception, duplicate-key, global-variable-not-assigned, unnecessary-semicolon, unreachable, unused-variable, unused-import, statmake-0.4.1/LICENSE000066400000000000000000000020541414022507300143310ustar00rootroot00000000000000MIT License Copyright (c) 2019 Dalton Maag Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. statmake-0.4.1/README.md000066400000000000000000000072331414022507300146070ustar00rootroot00000000000000# statmake `statmake` takes a user-written Stylespace that defines [OpenType `STAT` information](https://docs.microsoft.com/en-us/typography/opentype/spec/stat) for an entire font family and then (potentially subsets and) applies it to a specific variable font. This spares users from having to deal with [raw TTX dumps](https://github.com/fonttools/fonttools/) and juggling with nameIDs. ## Installation The easiest way is by installing it with `pip`. You need at least Python 3.7. ``` pip3 install statmake ``` ## Usage ### External Stylespace file, stand-alone or referenced from a Designspace file If you are producing more than one variable font (i.e. you have multiple Designspace files), you can avoid duplicated information by writing a single all-encompassing Stylespace file which statmake will subset for each variable font. **Attention:** A `STAT` table is supposed to describe a font's relationship to the _entire_ family. If you have separate upright and italic variable fonts with a `wght` axis each, you need to mark each font's position on the `ital` axis _in the Designspace lib `org.statmake.additionalLocations` key_. The Designspace `` elements are not supposed to hold this information, so it must be done in a separate lib key. 1. Write a Stylespace file that describes each stop of all axes available in the entire family. See [tests/data/Test.stylespace](tests/data/Test.stylespace) for an annotated example. You can also use it as a starting point. 2. You can have the file stand-alone or use the Designspace lib's `org.statmake.stylespacePath` key to store the path to the Stylespace file relative to the Designspace file. See [tests/data/TestExternalStylespace.designspace](tests/data/TestExternalStylespace.designspace) for an example. 3. If you have one or more Designspace files which do not define all axes available to the family, you have to annotate them with the missing axis locations to get a complete `STAT` table. See the lib key at the bottom of [tests/data/Test_Wght_Upright.designspace](tests/data/Test_Wght_Upright.designspace) and [tests/data/Test_Wght_Italic.designspace](tests/data/Test_Wght_Italic.designspace) for an example. 4. Generate the variable font(s) as normal 5. If... 1. ... you store the Stylespace file stand-alone: run `statmake --designspace variable_font.designspace --stylespace your.stylespace variable_font.ttf`. 2. ... you store the Stylespace inline in the Designspace file or as a stand-alone file and added the relative path to it in the Designspace's `org.statmake.stylespacePath` key: run `statmake --designspace variable_font.designspace variable_font.ttf` Be sure to use the Designspace file that was used to generate the font to get the correct missing axis location definitions. ### Designspace file with inline Stylespace data If you are producing a single variable font containing an entire family, this approach will save you an external file. 1. Write the file as above, point 1. 2. Insert it into the Designspace file's lib under the `org.statmake.stylespace` key. See [tests/data/TestInlineStylespace.designspace](tests/data/TestInlineStylespace.designspace) for an example. 3. Proceed from point 3 above. ## Q: Can I please have something other than a .plist file? Yes, but you have to convert it to `.plist` yourself, as statmake currently only read `.plist` files. One possible converter is Adam Twardoch's [yaplon](https://pypi.org/project/yaplon/). ## Q: I'm getting errors about how statmake doesn't like the way I wrote the Stylespace, but I want the data to be that way? Use a custom script with the https://fonttools.readthedocs.io/en/latest/otlLib/builder.html#fontTools.otlLib.builder.buildStatTable API instead. statmake-0.4.1/mypy.ini000066400000000000000000000013631414022507300150250ustar00rootroot00000000000000[mypy] python_version = 3.7 platform = linux # Untyped definitions and calls disallow_incomplete_defs = True disallow_untyped_defs = True # None and Optional handling no_implicit_optional = True strict_optional = True # Configuring warnings warn_no_return = True warn_redundant_casts = True warn_unreachable = True # Miscellaneous strictness flags strict_equality = True [mypy-tests.*] disallow_untyped_defs = False [mypy-fontTools.*] ignore_missing_imports = True [mypy-cattr] ignore_missing_imports = True [mypy-ufo2ft] ignore_missing_imports = True [mypy-ufoLib2] ignore_missing_imports = True [mypy-pytest] ignore_missing_imports = True [mypy-testutil] ignore_missing_imports = True [mypy-importlib_metadata] ignore_missing_imports = True statmake-0.4.1/poetry.lock000066400000000000000000001700001414022507300155150ustar00rootroot00000000000000[[package]] name = "appdirs" version = "1.4.4" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "main" optional = false python-versions = "*" [[package]] name = "astroid" version = "2.8.4" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] lazy-object-proxy = ">=1.4.0" typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<1.14" [[package]] name = "atomicwrites" version = "1.4.0" description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" version = "21.2.0" description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "black" version = "19.10b0" description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] appdirs = "*" attrs = ">=18.1.0" click = ">=6.5" pathspec = ">=0.6,<1" regex = "*" toml = ">=0.9.4" typed-ast = ">=1.4.0" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "booleanoperations" version = "0.9.0" description = "Boolean operations on paths." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] fonttools = ">=4.0.2" pyclipper = ">=1.1.0.post1" [[package]] name = "cattrs" version = "1.8.0" description = "Composable complex class support for attrs and dataclasses." category = "main" optional = false python-versions = ">=3.7,<4.0" [package.dependencies] attrs = ">=20" [[package]] name = "cffsubr" version = "0.2.9" description = "Standalone CFF subroutinizer based on the AFDKO tx tool" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] fontTools = ">=4.10.2" [package.extras] testing = ["pytest"] [[package]] name = "click" version = "8.0.3" description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" version = "6.1.1" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.6" [package.extras] toml = ["tomli"] [[package]] name = "cu2qu" version = "1.6.7.post1" description = "Cubic-to-quadratic bezier curve conversion" category = "dev" optional = false python-versions = "*" [package.dependencies] fonttools = {version = ">=3.32.0", extras = ["ufo"]} [package.extras] cli = ["defcon (>=0.6.0)"] [[package]] name = "fonttools" version = "4.27.1" description = "Tools to manipulate font files" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] fs = {version = ">=2.2.0,<3", optional = true, markers = "extra == \"ufo\""} [package.extras] all = ["fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "zopfli (>=0.1.4)", "lz4 (>=1.7.4.2)", "matplotlib", "sympy", "skia-pathops (>=0.5.0)", "brotlicffi (>=0.8.0)", "scipy", "brotli (>=1.0.1)", "munkres", "unicodedata2 (>=13.0.0)", "xattr"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["scipy", "munkres"] lxml = ["lxml (>=4.0,<5)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] symfont = ["sympy"] type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=13.0.0)"] woff = ["zopfli (>=0.1.4)", "brotlicffi (>=0.8.0)", "brotli (>=1.0.1)"] [[package]] name = "fs" version = "2.4.13" description = "Python's filesystem abstraction layer" category = "main" optional = false python-versions = "*" [package.dependencies] appdirs = ">=1.4.3,<1.5.0" pytz = "*" six = ">=1.10,<2.0" [package.extras] scandir = ["scandir (>=1.5,<2.0)"] [[package]] name = "importlib-metadata" version = "4.8.1" description = "Read metadata from Python packages" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] perf = ["ipython"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = "*" [[package]] name = "isort" version = "4.3.21" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] toml = {version = "*", optional = true, markers = "extra == \"pyproject\""} [package.extras] pipfile = ["pipreqs", "requirementslib"] pyproject = ["toml"] requirements = ["pipreqs", "pip-api"] xdg_home = ["appdirs (>=1.4.0)"] [[package]] name = "lazy-object-proxy" version = "1.6.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = "*" [[package]] name = "mypy" version = "0.910" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.5" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" toml = "*" typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} typing-extensions = ">=3.7.4" [package.extras] dmypy = ["psutil (>=4.0)"] python2 = ["typed-ast (>=1.4.0,<1.5.0)"] [[package]] name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." category = "dev" optional = false python-versions = "*" [[package]] name = "packaging" version = "21.2" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3" [[package]] name = "pathspec" version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "platformdirs" version = "2.4.0" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.6" [package.extras] docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "py" version = "1.10.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pyclipper" version = "1.3.0" description = "Cython wrapper for the C++ translation of the Angus Johnson's Clipper library (ver. 6.4.2)" category = "dev" optional = false python-versions = "*" [[package]] name = "pylint" version = "2.11.1" description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] astroid = ">=2.8.0,<2.9" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" platformdirs = ">=2.2.0" toml = ">=0.7.1" typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] name = "pyparsing" version = "2.4.7" description = "Python parsing module" category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] name = "pytz" version = "2021.3" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" [[package]] name = "regex" version = "2021.11.1" description = "Alternative regular expression module, to replace re." category = "dev" optional = false python-versions = "*" [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "typed-ast" version = "1.4.3" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false python-versions = "*" [[package]] name = "typing-extensions" version = "3.10.0.2" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false python-versions = "*" [[package]] name = "ufo2ft" version = "2.25.1" description = "A bridge between UFOs and FontTools." category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] booleanOperations = ">=0.9.0" cffsubr = ">=0.2.8" cu2qu = ">=1.6.7" fonttools = {version = ">=4.26.1", extras = ["ufo"]} [package.extras] compreffor = ["compreffor (>=0.4.6)"] pathops = ["skia-pathops (>=0.5.1)"] [[package]] name = "ufolib2" version = "0.11.4" description = "ufoLib2 is a UFO font processing library." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] attrs = ">=19.2.0" fonttools = {version = ">=4.0.0", extras = ["ufo"]} typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] lxml = ["lxml"] [[package]] name = "wrapt" version = "1.13.3" description = "Module for decorators, wrappers and monkey patching." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "zipp" version = "3.6.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" python-versions = "^3.7" content-hash = "eb84e2e2c30991401200f2b8b6ef5101df561d298f0d72711ab923d00f78f89e" [metadata.files] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] astroid = [ {file = "astroid-2.8.4-py3-none-any.whl", hash = "sha256:0755c998e7117078dcb7d0bda621391dd2a85da48052d948c7411ab187325346"}, {file = "astroid-2.8.4.tar.gz", hash = "sha256:1e83a69fd51b013ebf5912d26b9338d6643a55fec2f20c787792680610eed4a2"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] booleanoperations = [ {file = "booleanOperations-0.9.0-py3-none-any.whl", hash = "sha256:86c291c2fba9faedff6f007c932d7f242dd9b4304e9c6ca8149f864d07877a59"}, {file = "booleanOperations-0.9.0.zip", hash = "sha256:8cfa821c32ad374fa120d6b2e0b444ebeac57c91e6631528645fa19ac2a281b8"}, ] cattrs = [ {file = "cattrs-1.8.0-py3-none-any.whl", hash = "sha256:901fb2040529ae8fc9d93f48a2cdf7de3e983312ffb2a164ffa4e9847f253af1"}, {file = "cattrs-1.8.0.tar.gz", hash = "sha256:5c121ab06a7cac494813c228721a7feb5a6423b17316eeaebf13f5a03e5b0d53"}, ] cffsubr = [ {file = "cffsubr-0.2.9-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:ee50be093fba0903616e8a6fb399ece3e75bb3e318ea34830bf51bbc850c1445"}, {file = "cffsubr-0.2.9-py3-none-manylinux1_x86_64.whl", hash = "sha256:b607df896958c67da8ffcc56085622ec4a765785828c1a5fa84ccdc7330e307f"}, {file = "cffsubr-0.2.9-py3-none-win32.whl", hash = "sha256:65be6c387dbdd7dda7d5437a2f6eac3a9557e1f49cbc9dd03bdc5bc7347feec0"}, {file = "cffsubr-0.2.9-py3-none-win_amd64.whl", hash = "sha256:c116082ae338e2013fe333e67036e1f84d05ce639479d322cb58295ed687c454"}, {file = "cffsubr-0.2.9.tar.gz", hash = "sha256:f8dc522e1d2f703448897a019da8a7a4fdb8d56062e6c7c214906e1ebb051d0f"}, ] click = [ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ {file = "coverage-6.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:42a1fb5dee3355df90b635906bb99126faa7936d87dfc97eacc5293397618cb7"}, {file = "coverage-6.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a00284dbfb53b42e35c7dd99fc0e26ef89b4a34efff68078ed29d03ccb28402a"}, {file = "coverage-6.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:51a441011a30d693e71dea198b2a6f53ba029afc39f8e2aeb5b77245c1b282ef"}, {file = "coverage-6.1.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e76f017b6d4140a038c5ff12be1581183d7874e41f1c0af58ecf07748d36a336"}, {file = "coverage-6.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7833c872718dc913f18e51ee97ea0dece61d9930893a58b20b3daf09bb1af6b6"}, {file = "coverage-6.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8186b5a4730c896cbe1e4b645bdc524e62d874351ae50e1db7c3e9f5dc81dc26"}, {file = "coverage-6.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbca34dca5a2d60f81326d908d77313816fad23d11b6069031a3d6b8c97a54f9"}, {file = "coverage-6.1.1-cp310-cp310-win32.whl", hash = "sha256:72bf437d54186d104388cbae73c9f2b0f8a3e11b6e8d7deb593bd14625c96026"}, {file = "coverage-6.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:994ce5a7b3d20981b81d83618aa4882f955bfa573efdbef033d5632b58597ba9"}, {file = "coverage-6.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ab6a0fe4c96f8058d41948ddf134420d3ef8c42d5508b5a341a440cce7a37a1d"}, {file = "coverage-6.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10ab138b153e4cc408b43792cb7f518f9ee02f4ff55cd1ab67ad6fd7e9905c7e"}, {file = "coverage-6.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7e083d32965d2eb6638a77e65b622be32a094fdc0250f28ce6039b0732fbcaa8"}, {file = "coverage-6.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:359a32515e94e398a5c0fa057e5887a42e647a9502d8e41165cf5cb8d3d1ca67"}, {file = "coverage-6.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:bf656cd74ff7b4ed7006cdb2a6728150aaad69c7242b42a2a532f77b63ea233f"}, {file = "coverage-6.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:dc5023be1c2a8b0a0ab5e31389e62c28b2453eb31dd069f4b8d1a0f9814d951a"}, {file = "coverage-6.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:557594a50bfe3fb0b1b57460f6789affe8850ad19c1acf2d14a3e12b2757d489"}, {file = "coverage-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:9eb0a1923354e0fdd1c8a6f53f5db2e6180d670e2b587914bf2e79fa8acfd003"}, {file = "coverage-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:04a92a6cf9afd99f9979c61348ec79725a9f9342fb45e63c889e33c04610d97b"}, {file = "coverage-6.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:479228e1b798d3c246ac89b09897ee706c51b3e5f8f8d778067f38db73ccc717"}, {file = "coverage-6.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78287731e3601ea5ce9d6468c82d88a12ef8fe625d6b7bdec9b45d96c1ad6533"}, {file = "coverage-6.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c95257aa2ccf75d3d91d772060538d5fea7f625e48157f8ca44594f94d41cb33"}, {file = "coverage-6.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9ad5895938a894c368d49d8470fe9f519909e5ebc6b8f8ea5190bd0df6aa4271"}, {file = "coverage-6.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:326d944aad0189603733d646e8d4a7d952f7145684da973c463ec2eefe1387c2"}, {file = "coverage-6.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e7d5606b9240ed4def9cbdf35be4308047d11e858b9c88a6c26974758d6225ce"}, {file = "coverage-6.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:572f917267f363101eec375c109c9c1118037c7cc98041440b5eabda3185ac7b"}, {file = "coverage-6.1.1-cp37-cp37m-win32.whl", hash = "sha256:35cd2230e1ed76df7d0081a997f0fe705be1f7d8696264eb508076e0d0b5a685"}, {file = "coverage-6.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:65ad3ff837c89a229d626b8004f0ee32110f9bfdb6a88b76a80df36ccc60d926"}, {file = "coverage-6.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:977ce557d79577a3dd510844904d5d968bfef9489f512be65e2882e1c6eed7d8"}, {file = "coverage-6.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62512c0ec5d307f56d86504c58eace11c1bc2afcdf44e3ff20de8ca427ca1d0e"}, {file = "coverage-6.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2e5b9c17a56b8bf0c0a9477fcd30d357deb486e4e1b389ed154f608f18556c8a"}, {file = "coverage-6.1.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:666c6b32b69e56221ad1551d377f718ed00e6167c7a1b9257f780b105a101271"}, {file = "coverage-6.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fb2fa2f6506c03c48ca42e3fe5a692d7470d290c047ee6de7c0f3e5fa7639ac9"}, {file = "coverage-6.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f0f80e323a17af63eac6a9db0c9188c10f1fd815c3ab299727150cc0eb92c7a4"}, {file = "coverage-6.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:738e823a746841248b56f0f3bd6abf3b73af191d1fd65e4c723b9c456216f0ad"}, {file = "coverage-6.1.1-cp38-cp38-win32.whl", hash = "sha256:8605add58e6a960729aa40c0fd9a20a55909dd9b586d3e8104cc7f45869e4c6b"}, {file = "coverage-6.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:6e994003e719458420e14ffb43c08f4c14990e20d9e077cb5cad7a3e419bbb54"}, {file = "coverage-6.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e3c4f5211394cd0bf6874ac5d29684a495f9c374919833dcfff0bd6d37f96201"}, {file = "coverage-6.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e14bceb1f3ae8a14374be2b2d7bc12a59226872285f91d66d301e5f41705d4d6"}, {file = "coverage-6.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0147f7833c41927d84f5af9219d9b32f875c0689e5e74ac8ca3cb61e73a698f9"}, {file = "coverage-6.1.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b1d0a1bce919de0dd8da5cff4e616b2d9e6ebf3bd1410ff645318c3dd615010a"}, {file = "coverage-6.1.1-cp39-cp39-win32.whl", hash = "sha256:a11a2c019324fc111485e79d55907e7289e53d0031275a6c8daed30690bc50c0"}, {file = "coverage-6.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:4d8b453764b9b26b0dd2afb83086a7c3f9379134e340288d2a52f8a91592394b"}, {file = "coverage-6.1.1-pp36-none-any.whl", hash = "sha256:3b270c6b48d3ff5a35deb3648028ba2643ad8434b07836782b1139cf9c66313f"}, {file = "coverage-6.1.1-pp37-none-any.whl", hash = "sha256:ffa8fee2b1b9e60b531c4c27cf528d6b5d5da46b1730db1f4d6eee56ff282e07"}, {file = "coverage-6.1.1-pp38-none-any.whl", hash = "sha256:4cd919057636f63ab299ccb86ea0e78b87812400c76abab245ca385f17d19fb5"}, {file = "coverage-6.1.1.tar.gz", hash = "sha256:b8e4f15b672c9156c1154249a9c5746e86ac9ae9edc3799ee3afebc323d9d9e0"}, ] cu2qu = [ {file = "cu2qu-1.6.7.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:885988713beee1a17c2b5586d19029422112fcdb3d38b6190b76fefb8ca8bb6d"}, {file = "cu2qu-1.6.7.post1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5fc1620156983c41f15be52574ddb13f80e2c8185b35aa5e9fcc03473d1f65f6"}, {file = "cu2qu-1.6.7.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:faed09723cad8e714d3326a225e8d00376b4620de9b07b5980fb4c41c8fff688"}, {file = "cu2qu-1.6.7.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d0edf3e02ad092efee6a6bbb2649482a2c26b33626decf0dd595f9197c9a3bad"}, {file = "cu2qu-1.6.7.post1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d95ccf87599a3b4ed22ab94603e449a91eb03ec211179f19ee91f54d7a2c67b2"}, {file = "cu2qu-1.6.7.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:2d4ab5d8e84eb5f84acd6920d985b49de1d8eee5b57371afa472836807239365"}, {file = "cu2qu-1.6.7.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a108487328b6c10b5b25cecc2f4f4337570c2ce3d694696604c8286d34238e3b"}, {file = "cu2qu-1.6.7.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:659057d91b6f8afe3d75525b2555a64f5efc51eb84c0c3a3a73a2808ed62504f"}, {file = "cu2qu-1.6.7.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9612c71947e6b35002157d44ebb10009b2c6b96e756915bb73ef618ac5c8cb0a"}, {file = "cu2qu-1.6.7.post1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22065d8491f971c6a4c6afe8cca24bdb75ea9a863f1709f3fe54258f5c22f4fe"}, {file = "cu2qu-1.6.7.post1-cp38-cp38-win_amd64.whl", hash = "sha256:e6650526b4b9ac786c937cf4babf4b2f88dc9a2b2dfa931f1f2d386ad1c16c47"}, {file = "cu2qu-1.6.7.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4806dc0a0c98153eb48ff4e6320581b97c0015d08fff5415c102e27a1af8a2d1"}, {file = "cu2qu-1.6.7.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ec2fce44f7a265023fdaa094ab80da4180fe25935c3c11a18ffe677fa42f8f3"}, {file = "cu2qu-1.6.7.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb4b8d3154a1812bdabab69950175f7c3e017a10b12fc7178640997c5edec71c"}, {file = "cu2qu-1.6.7.post1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c45fa047a3142cdfdfb00cba4d3e827a1b096a02c54600a3d199d392dbd90e7d"}, {file = "cu2qu-1.6.7.post1-cp39-cp39-win_amd64.whl", hash = "sha256:dfb1c1abb012e6b0bfdefdfdbc2a7ae5d268a5923e56cf0416c439025316955f"}, {file = "cu2qu-1.6.7.post1-py2.py3-none-any.whl", hash = "sha256:c93619ba635427f42373077f52711bf3a253ec9080da2a9b80652037d6c0f987"}, {file = "cu2qu-1.6.7.post1.zip", hash = "sha256:8c982f11de0151f41da83df4a4f75207380301c0356fb7a3322f0db74e16e6f4"}, ] fonttools = [ {file = "fonttools-4.27.1-py3-none-any.whl", hash = "sha256:51ad7ee7ff878bcf75578d07f221bb90e6eaed1def53774249815377e89490e7"}, {file = "fonttools-4.27.1.zip", hash = "sha256:6e483f77dc5b862452c2888ec944fca5b79cffb741c7469786a442360681b4e8"}, ] fs = [ {file = "fs-2.4.13-py2.py3-none-any.whl", hash = "sha256:1d10cc8f9c55fbcf7b23775289a13f6796dca7acd5a135c379f49e87a56a7230"}, {file = "fs-2.4.13.tar.gz", hash = "sha256:caab4dc1561d63c92f36ee78976f6a4a01381830d8420ce34a78d4f1bb1dc95f"}, ] importlib-metadata = [ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, ] lazy-object-proxy = [ {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"}, {file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"}, {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"}, {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"}, {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"}, {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"}, {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"}, {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"}, {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"}, {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"}, {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"}, {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"}, {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"}, {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"}, {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"}, {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"}, {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] mypy = [ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] packaging = [ {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, ] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] platformdirs = [ {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pyclipper = [ {file = "pyclipper-1.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d6376b8337f3f90f9fdd80e81f418766a12491d83b9cc592a082cc097ecad3a6"}, {file = "pyclipper-1.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3869bdf6195bc5cc269df517940e2d98b9fc033702360533b3a0fb68c60f8aa7"}, {file = "pyclipper-1.3.0-cp36-cp36m-win32.whl", hash = "sha256:eccee9cddcfc0774c54ea97cd6238e158fde307e6f5f6b1a2c3f12171a271f1a"}, {file = "pyclipper-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a34c0b99f2e8c6aa16673e8f31deaaddcde1de2b6ceab0ed2a814f3581a7d5de"}, {file = "pyclipper-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f13cf3a05c342f65b3c1b28d7c223c17af18136ac160a0afb89a5456447bd8d2"}, {file = "pyclipper-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d053fb9ae553a75295b7d6da8c761a2d1622cc3c873aaa516ffe3107552fd241"}, {file = "pyclipper-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:d2cf436faeefba5c29ad98ca51f55ace1caf1bf2b0714e2dea77e85487fe73cc"}, {file = "pyclipper-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5846ab36ea17b970ca421587187c1b5c2804b231ca23a69fe4dd33e20255a175"}, {file = "pyclipper-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b371037c60d7b4dd06d50b03c4d306cf9f99de18c410d29e80ab15d69c304613"}, {file = "pyclipper-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a6c2da18f6763bee7d5f7d260a4482a611215b4c32ec9fa887f4a1d12045c309"}, {file = "pyclipper-1.3.0-cp38-cp38-win32.whl", hash = "sha256:d3f1d11c21efb75adb214bc23539a7c3f5b84e195c4880f641e2ac4908cad624"}, {file = "pyclipper-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f5f628d5c57a2d5c6b2e674bdf08ab68303933b067f15db6891690bef57fe58e"}, {file = "pyclipper-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5f224b9a5cec9ea7a3b55ee3f0825ef5902e27231d27f52b02718a51bb2a8f40"}, {file = "pyclipper-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e82f116ce53d14fb508c3b271ed87274b916cd6ac990df2f21d2739407e8e2a"}, {file = "pyclipper-1.3.0-cp39-cp39-win32.whl", hash = "sha256:24b87494f45e82d9128dbb01d8e8799fc37f833c0da92afe810401e721ec5c50"}, {file = "pyclipper-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:cb137770e2a886a7de98d286e519061ddebc4608b98da4a53bd6c1031b31fc89"}, {file = "pyclipper-1.3.0.zip", hash = "sha256:48a1b5c585aea10e5b9c0b82d6abe2642fafd9ef158b9921852bc4af815ca20c"}, ] pylint = [ {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"}, {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] pytz = [ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] regex = [ {file = "regex-2021.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce0625900e4d6d9a43f50e897f6aaa1a52e5e4931f994a1b8e9f6a4e49185e4e"}, {file = "regex-2021.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:197331fffc684af34534328a9e4a7d0a118d9a838b393b80abb7af4f709acad7"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8228e75d340e48b360d5e963acf1332b5c9080f73ec6ce8cf483ec7e0542f2dd"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0bd3cccb9e6e61ed64a01075353ded1e012b8c4af222496eb5478dc48a5c0b4"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:767cb9ba1e6151692fd27449f06550fbdbd82b42236b5a31bac862a1da628860"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61568c1633abfddd21552a261d3e1a83eda7e3fb1d46e148d61fd41d5541a8d"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86baabf4f346b612665ab9f5f38377def21f824c89574e71c67e5c38e4971e5c"}, {file = "regex-2021.11.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2f189743257300e9b3a3b4fdea10f46bf6d33ef580856b2a6bfc2073653c2287"}, {file = "regex-2021.11.1-cp310-cp310-win32.whl", hash = "sha256:50ceaaaa88abec74393301336a2494734386cf3cafa51dde26367b139fe86336"}, {file = "regex-2021.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:78c80cd9939b42eeac4f0556f689a6eda987b81678149071853391b922d98f64"}, {file = "regex-2021.11.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8ae9d75bbfebd402e1254b09a721c037ec9f018750a5091bea8c705729bbf5c1"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cef78eab573f07378f26662f24d28c706e6765a95980cce98a91d025d481ab95"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ad34d49fdd9faef0f4ccf9286c63ee9610d4664d92b13cdb4c4407e834921c"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5867bc04011ee03ed3160df2f378cdee732aa3ed070b4760b029ebefbea6116c"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1f3b0f23437eea6c1781bcc7d1d14f7c8a3032142ac660dc7ca43ba1a139e30"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a394085589dc549ad976290c93f688620af898ac49d46269ad6cdf3ef29bc58"}, {file = "regex-2021.11.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:495d98445daaa4657093fc54a5d53cffe41acec5c1edac901aac8061fc7c2f85"}, {file = "regex-2021.11.1-cp36-cp36m-win32.whl", hash = "sha256:a5bb5637a2fe6d8710d5f0b5600556c64fb3d49449502e9dece2038a9753e8b8"}, {file = "regex-2021.11.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4f6741b5506cbad28bfc46397c2e267ca59b357c075ea6b68f7781c5a8b150a"}, {file = "regex-2021.11.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a3abab9f5d487061b0d99beb5ff2d1619a3652c8b785bc66aca7682d8b7d4116"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dfb06ef5c47b41dcb3bf4fdf2983c048711e16a3bf74814be14089a1933b3c"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea372838910264443ad233a92a20279574b7f0e9743b5e5de526e274895b7274"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b77c919379409ae92a5b13ef2452c509632efaa40b926fab9eac7839ae9a266a"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e829fc2a1bcbb18c6579fd5fb563b93f25973b0451cf4e2a22933c991792e2cb"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c26d8d18ae84584d58e34c9ac5c8528110483d080dca77626fd62cdb316c0a2"}, {file = "regex-2021.11.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:724a1601ae73521e1e9fda0a3015915ae0d1931772802fcf7f0dd83f111d11d2"}, {file = "regex-2021.11.1-cp37-cp37m-win32.whl", hash = "sha256:69e047c969f7b952bc55274e2b5189117ff2322b049a4c9143f94af8976b55f6"}, {file = "regex-2021.11.1-cp37-cp37m-win_amd64.whl", hash = "sha256:5b4036abc6b3307146a81358cd4d4d091bd9a2fe3edaca9b95f66e7ba6d06e20"}, {file = "regex-2021.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cc93c277d6793a26cdb9bcadc6d6d9db9c6a6cf2aae207bbaef2f16d53570d43"}, {file = "regex-2021.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0aa74d18236f8a31b911caafc28aed2a8444bcca8e61eb377949771f84710ada"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b4d2b514c30a9c8f80f5d78ec978719f1c3823662a5ba0809c03f0cad4c5de6"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd31271de74c8f3e296644f9a12d9ad60bdc3fc8d3b8e8a26ccbf777169e5a0d"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ef40aa8e39dff52480e21c38b36486a0c256b3b93d0094e7a06ab517a246994"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05d8ddf6bb4f50342ecddee4deb621588a013afe13d9c77cf6eb58c5ad1bc21f"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95fa9665d8dac10c109a3dcc7d476b7f27b32fe22190b433c2a2b7eb903aa646"}, {file = "regex-2021.11.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7f44ee70fa7f346551550f8ec4650a4354b9494c0d1dfa08100fe056d6910388"}, {file = "regex-2021.11.1-cp38-cp38-win32.whl", hash = "sha256:1b4cf110002a8b6d039d2d4bed15095e5ddf3d9e4aa5eb67476eba0256c93893"}, {file = "regex-2021.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:68939d7fdc417174ee4975fd78aec41ae484de606add311d1387011484ce1da3"}, {file = "regex-2021.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b06599e60c421fb0512a2cef8553e6ea072a72081e51158f487e2d207b947aa9"}, {file = "regex-2021.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6a1ed9aef9748d76cf39e08529be9209bdfcf34e70c9133abf966d954a59bc6d"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b3f8852cf597388851c1d3d1073fb3694e5647303c002813aa230d41a9ec5fc"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ed2819b7c9d83ae3dfbbfea770f0d0780c732b5cbbd8269aa910dbe0205361"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b662e04e0fc8f3b99d9beacecc5e000b9a68bdb25ba5b64211ebe263e907f3a2"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf725b99f897b8e6d24d8b102320a31551530d7aae1e2fe42eb1ee85173f57b6"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0da1b6e39affa1b6da2106745c9d73f576ffe4484cbdfbd5e1c9b9872532eec8"}, {file = "regex-2021.11.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:09c9ff0c67f4949f14b2ddf935bc36cafd0fd4db6d3334a3e5a24a532773b2d1"}, {file = "regex-2021.11.1-cp39-cp39-win32.whl", hash = "sha256:3383f0d47e5e343fa5facd87a6f95de101c488d0aec1f41da00fcc019179aefc"}, {file = "regex-2021.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9108787c320940acc6676000716c3dc1734db9e14facbd98c13920972aee21b"}, {file = "regex-2021.11.1.tar.gz", hash = "sha256:20675d8bd3c2cc8dbfafd60a220ec04d0018564f101f80a64e56f4e4ed0afe55"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] typed-ast = [ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] ufo2ft = [ {file = "ufo2ft-2.25.1-py2.py3-none-any.whl", hash = "sha256:3ed6c539e47c122be706c08a709c855f71277d415d592329302966bf2686a0ac"}, {file = "ufo2ft-2.25.1.zip", hash = "sha256:41ade052a6bc4c6b192f627922b52053cab307295512724e8fad6f2b12c49e61"}, ] ufolib2 = [ {file = "ufoLib2-0.11.4-py3-none-any.whl", hash = "sha256:312752019bedfe3c55bd5a3ba88012a0e26d67df77ed3b12a56ddb66129f7deb"}, {file = "ufoLib2-0.11.4.zip", hash = "sha256:25359d24a46c7a67c3cd060f0ff2daee2e8aa96c0a811c43ef5111407beee49b"}, ] wrapt = [ {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, ] zipp = [ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] statmake-0.4.1/pyproject.toml000066400000000000000000000021171414022507300162400ustar00rootroot00000000000000[tool.poetry] name = "statmake" version = "0.4.1" description = "Applies STAT information from a Stylespace to a variable font." authors = ["Nikolaus Waxweiler "] license = "MIT" readme = "README.md" repository = "https://github.com/daltonmaag/statmake" include = ["py.typed"] [tool.poetry.dependencies] attrs = ">= 18.2" cattrs = "^1.1" fonttools = {version = "^4.11", extras = ["ufo"]} python = "^3.7" # https://medium.com/@cjolowicz/hypermodern-python-6-ci-cd-b233accfa2f6#bf20 importlib_metadata = {version = ">=1.6.0", python = "<3.8"} [tool.poetry.dev-dependencies] black = "^19.10b0" coverage = "*" isort = {extras = ["pyproject"], version = "^4.3.21"} mypy = "*" pylint = "*" pytest = "*" ufo2ft = "^2.7" ufoLib2 = ">= 0.4" [tool.poetry.scripts] statmake = "statmake.cli:main" [tool.black] target-version = ["py37"] [tool.isort] multi_line_output = 3 include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true line_length = 88 known_first_party = "statmake" [build-system] requires = ["poetry>=1.0.0"] build-backend = "poetry.masonry.api" statmake-0.4.1/pytest.ini000066400000000000000000000001521414022507300153520ustar00rootroot00000000000000[pytest] filterwarnings = ignore:fromstring:DeprecationWarning ignore:tostring:DeprecationWarning statmake-0.4.1/src/000077500000000000000000000000001414022507300141125ustar00rootroot00000000000000statmake-0.4.1/src/statmake/000077500000000000000000000000001414022507300157235ustar00rootroot00000000000000statmake-0.4.1/src/statmake/__init__.py000066400000000000000000000004661414022507300200420ustar00rootroot00000000000000try: from importlib.metadata import version, PackageNotFoundError # type: ignore except ImportError: # pragma: no cover from importlib_metadata import version, PackageNotFoundError try: __version__ = version(__name__) except PackageNotFoundError: # pragma: no cover __version__ = "unknown" statmake-0.4.1/src/statmake/__main__.py000066400000000000000000000001101414022507300200050ustar00rootroot00000000000000import statmake.cli if __name__ == "__main__": statmake.cli.main() statmake-0.4.1/src/statmake/classes.py000066400000000000000000000274171414022507300177450ustar00rootroot00000000000000import enum import functools import os from pathlib import Path from typing import Any, Dict, List, Mapping, Optional, Set, Tuple, Union import attr import cattr import fontTools.designspaceLib import fontTools.misc.plistlib from .errors import StylespaceError DESIGNSPACE_STYLESPACE_INLINE_KEY = "org.statmake.stylespace" DESIGNSPACE_STYLESPACE_PATH_KEY = "org.statmake.stylespacePath" class AxisValueFlag(enum.Flag): OlderSiblingFontAttribute = 0x0001 ElidableAxisValueName = 0x0002 @attr.s(auto_attribs=True, frozen=True, slots=True) class FlagList: """Represent a list of AxisValueFlags so I can implement a value property.""" flags: List[AxisValueFlag] = attr.ib(factory=list) @property def value(self) -> int: """Return the value of all flags ORed together.""" if not self.flags: return 0 return functools.reduce(lambda x, y: x | y, self.flags).value @attr.s(auto_attribs=True, frozen=True, slots=True) class NameRecord: """Represent a IETF BCP 47 language code to name string mapping for the `name` table.""" mapping: Mapping[str, str] def __attrs_post_init__(self) -> None: if "en" not in self.mapping: raise StylespaceError( "All NameRecords must have a default English (IETF BCP 47 language " "code 'en') entry." ) def __getitem__(self, key: str) -> str: return self.mapping.__getitem__(key) @property def default(self) -> str: return self.mapping["en"] @classmethod def from_string(cls, name: str) -> "NameRecord": return cls(mapping={"en": name}) @classmethod def from_dict(cls, dictionary: Mapping) -> "NameRecord": return cls(mapping=dictionary) @classmethod def structure(cls, data: Union[str, dict]) -> "NameRecord": if isinstance(data, str): return cls.from_string(data) if isinstance(data, dict): return cls.from_dict(data) raise StylespaceError(f"Don't know how to construct NameRecord from '{data}'.") @attr.s(auto_attribs=True, frozen=True, slots=True) class LocationFormat1: name: NameRecord value: float flags: FlagList = attr.ib(factory=FlagList) def to_builder_dict(self) -> Dict[str, Any]: return { "name": self.name.mapping, "value": self.value, "flags": self.flags.value, } @attr.s(auto_attribs=True, frozen=True, slots=True) class LocationFormat2: name: NameRecord value: float range: Tuple[float, float] flags: FlagList = attr.ib(factory=FlagList) def __attrs_post_init__(self) -> None: if len(self.range) != 2: raise StylespaceError("Range must be a value pair of (min, max).") def to_builder_dict(self) -> Dict[str, Any]: return { "name": self.name.mapping, "nominalValue": self.value, "rangeMinValue": self.range[0], "rangeMaxValue": self.range[1], "flags": self.flags.value, } @attr.s(auto_attribs=True, frozen=True, slots=True) class LocationFormat3: name: NameRecord value: float linked_value: float flags: FlagList = attr.ib(factory=FlagList) def to_builder_dict(self) -> Dict[str, Any]: return { "name": self.name.mapping, "value": self.value, "linkedValue": self.linked_value, "flags": self.flags.value, } @attr.s(auto_attribs=True, frozen=True, slots=True) class LocationFormat4: name: NameRecord axis_values: Mapping[str, float] flags: FlagList = attr.ib(factory=FlagList) def to_builder_dict(self, name_to_tag: Mapping[str, str]) -> Dict[str, Any]: return { "name": self.name.mapping, "location": {name_to_tag[k]: v for k, v in self.axis_values.items()}, "flags": self.flags.value, } @attr.s(auto_attribs=True, frozen=True, slots=True) class Axis: name: NameRecord tag: str locations: List[Union[LocationFormat1, LocationFormat2, LocationFormat3]] = attr.ib( factory=list ) ordering: Optional[int] = None @attr.s(auto_attribs=True, frozen=True, slots=True) class Stylespace: axes: List[Axis] locations: List[LocationFormat4] = attr.ib(factory=list) elided_fallback_name_id: int = 2 def __attrs_post_init__(self) -> None: """Fill in a default ordering unless the user specified at least one custom one, also do sanity checking. This works around the frozen state with `object.__setattr__`. """ if all(axis.ordering is None for axis in self.axes): for index, axis in enumerate(self.axes): object.__setattr__(axis, "ordering", index) elif not all( isinstance(axis.ordering, int) and axis.ordering >= 0 for axis in self.axes ): raise StylespaceError( "If you specify the ordering for one axis, you must specify all of " "them and they must be >= 0." ) # Ensure named locations only contain axis names that are present in the # Stylespace and specify a location for all axes. available_axes = {a.name.default for a in self.axes} for named_location in self.locations: named_location_axes = set(named_location.axis_values.keys()) if named_location_axes != available_axes: raise StylespaceError( f"Location named '{named_location.name.default}' must specify " "values for all axes in the Stylespace and contain no other axis " "names." ) # Ensure that all name records have the same languages specified. reference_languages = None for axis in self.axes: if reference_languages is None: reference_languages = sorted(axis.name.mapping.keys()) for location in axis.locations: location_languages = sorted(location.name.mapping.keys()) if location_languages != reference_languages: raise StylespaceError( "All names must be supplied in the same languages. On axis " f"'{axis.name.default}', location '{location.name.default}' is " f"named in languages {location_languages} but " f"expected was {reference_languages}." ) for named_location in self.locations: assert reference_languages is not None location_languages = sorted(named_location.name.mapping.keys()) if location_languages != reference_languages: raise StylespaceError( "All names must be supplied in the same languages. The named " f"location '{named_location.name.default}' is " f"named in languages {location_languages} but " f"expected was {reference_languages}." ) # Ensure linked_values are present on the same axis in the Stylespace for axis in self.axes: values = {l.value for l in axis.locations} for location in axis.locations: linked_value: Optional[float] = getattr(location, "linked_value", None) if linked_value is not None and linked_value not in values: raise StylespaceError( f"On axis '{axis.name.default}', location " f"'{location.name.default}' specifies a linked_value of " f"'{linked_value}', which does not exist on that axis " "(ranges are ignored)." ) # Ensure location values are unique. for axis in self.axes: values = set() for location in axis.locations: if location.value in values: raise StylespaceError( f"On axis '{axis.name.default}', location " f"'{location.name.default}' specifies a duplicate location " f"value of '{location.value}', which is already assigned on " "the same axis." ) values.add(location.value) named_values: Set[Tuple[Tuple[str, float], ...]] = set() for named_location in self.locations: named_location_tuple = tuple(named_location.axis_values.items()) if named_location_tuple in named_values: raise StylespaceError( f"The named location '{named_location.name.default}' specifies a " "duplicate location already taken by another." ) named_values.add(named_location_tuple) @classmethod def from_dict(cls, dict_data: dict) -> "Stylespace": """Construct Stylespace from unstructured dict data.""" converter = cattr.Converter() converter.register_structure_hook( FlagList, lambda list_of_str_flags, cls: cls( # type: ignore [getattr(AxisValueFlag, f) for f in list_of_str_flags] ), ) converter.register_structure_hook( NameRecord, lambda data, cls: cls.structure(data) # type: ignore ) return converter.structure(dict_data, cls) def to_dict(self) -> Dict[str, Any]: """Construct dict from structured Stylespace data.""" converter = cattr.Converter() converter.register_unstructure_hook( # type: ignore FlagList, lambda cls: [flag.name for flag in cls.flags], # type: ignore ) converter.register_unstructure_hook(NameRecord, lambda cls: cls.mapping) # type: ignore return converter.unstructure(self) @classmethod def from_bytes(cls, stylespace_content: bytes) -> "Stylespace": """Construct Stylespace from bytes containing (XML) plist data.""" stylespace_content_parsed = fontTools.misc.plistlib.loads(stylespace_content) return cls.from_dict(stylespace_content_parsed) @classmethod def from_file(cls, stylespace_path: Union[str, bytes, os.PathLike]) -> "Stylespace": """Construct Stylespace from path to (XML) plist file.""" with open(stylespace_path, "rb") as fp: return cls.from_bytes(fp.read()) @classmethod def from_designspace( cls, designspace: fontTools.designspaceLib.DesignSpaceDocument ) -> "Stylespace": f"""Construct Stylespace from unstructured dict data or a path stored in a Designspace object's lib. The keys: - `{DESIGNSPACE_STYLESPACE_INLINE_KEY}`: The content of a regular Stylespace file as a dict. - `{DESIGNSPACE_STYLESPACE_PATH_KEY}`: A path to an external Stylespace file, relative to the Designspace file (the Designspace object must have the `path` attribute set). """ stylespace_inline = designspace.lib.get(DESIGNSPACE_STYLESPACE_INLINE_KEY) stylespace_path = designspace.lib.get(DESIGNSPACE_STYLESPACE_PATH_KEY) if (stylespace_inline and stylespace_path) or ( not stylespace_inline and not stylespace_path ): raise StylespaceError( "Designspace lib must contain EITHER inline Stylespace data OR a path " "to an external Stylespace file." ) if stylespace_inline: return cls.from_dict(stylespace_inline) if not designspace.path: raise StylespaceError( "Designspace object must have `path` attribute set, because the " "Stylespace path is relative to the Designspace file." ) stylespace_path_lookup = Path(designspace.path).parent / stylespace_path return cls.from_file(stylespace_path_lookup) statmake-0.4.1/src/statmake/cli.py000066400000000000000000000041331414022507300170450ustar00rootroot00000000000000import argparse import logging import sys from pathlib import Path from typing import List, Optional import fontTools.designspaceLib import fontTools.ttLib import statmake import statmake.classes import statmake.lib from statmake.errors import Error, StylespaceError def main(args: Optional[List[str]] = None) -> None: logging.basicConfig(format="%(levelname)s: %(message)s") parser = argparse.ArgumentParser() parser.add_argument( "--version", action="version", version=statmake.__version__, ) parser.add_argument( "--stylespace", type=statmake.classes.Stylespace.from_file, help=( "The path to the Stylespace file, if it is not contained in the " "Designspace." ), ) parser.add_argument( "--designspace", "-m", required=True, type=fontTools.designspaceLib.DesignSpaceDocument.fromfile, help="The path to the Designspace file used to generate the variable font.", ) parser.add_argument( "--output-path", type=Path, help="Write the modified font to this path instead of in-place.", ) parser.add_argument( "variable_font", type=Path, help="The path to the variable font file." ) parsed_args = parser.parse_args(args) designspace = parsed_args.designspace if parsed_args.stylespace: stylespace = parsed_args.stylespace else: try: stylespace = statmake.classes.Stylespace.from_designspace(designspace) except StylespaceError as e: logging.error("Could not load Stylespace data from Designspace: %s", str(e)) sys.exit(1) additional_locations = designspace.lib.get("org.statmake.additionalLocations", {}) font = fontTools.ttLib.TTFont(parsed_args.variable_font) try: statmake.lib.apply_stylespace_to_variable_font( stylespace, font, additional_locations ) except Error as e: logging.error("Cannot apply Stylespace to font: %s", str(e)) sys.exit(1) font.save(parsed_args.output_path or parsed_args.variable_font) statmake-0.4.1/src/statmake/errors.py000066400000000000000000000002131414022507300176050ustar00rootroot00000000000000class Error(Exception): """Base exception.""" class StylespaceError(Error): """Represents a consistency error in Stylespaces.""" statmake-0.4.1/src/statmake/lib.py000066400000000000000000000170531414022507300170510ustar00rootroot00000000000000import collections from typing import Any, Dict, List, Mapping, Set, Tuple import fontTools.otlLib.builder import fontTools.ttLib import statmake.classes from statmake.errors import Error def apply_stylespace_to_variable_font( stylespace: statmake.classes.Stylespace, varfont: fontTools.ttLib.TTFont, additional_locations: Mapping[str, float], ) -> None: """Generate and apply a STAT table to a variable font. additional_locations: used in subset Designspaces to express where on which other axes not defined by an element the varfont stands. The primary use-case is defining a complete STAT table for variable fonts that do not include all axes of a family (either because they intentionally contain just a subset of axes or because the designs are incompatible). """ axes, locations, elided_fallback_name = _generate_builder_data( stylespace, varfont, additional_locations ) fontTools.otlLib.builder.buildStatTable( varfont, axes, locations, elided_fallback_name ) def _generate_builder_data( stylespace: statmake.classes.Stylespace, varfont: fontTools.ttLib.TTFont, additional_locations: Mapping[str, float], ) -> Tuple[List[Mapping[str, Any]], List[Mapping[str, Any]], int]: """Generate axes and locations dictionaries for use in fontTools.otlLib.builder.buildStatTable, tailored to the font. Rules: 1. There must be a fvar table so we know which named instances are defined. Every named instance needs a STAT entry for every point of its axis definition, i.e. an instance at {"Weight": 300, "Slant": 5} must have a Stylespace entry for Weight=300 and for Slant=5. 2. The Stylespace must contain all axis names the varfont does and tags must match. 3. Additional locations must only specify axes not in the font already and can only draw from axes available in the Stylespace. 4. All name IDs must have a default English (United States) entry for the Windows platform, Unicode BMP encoding, to match axis names to tags. 5. The font must get a location for every axis the Stylespace contains. """ name_to_tag = {a.name.default: a.tag for a in stylespace.axes} _sanity_check(stylespace, varfont, additional_locations, name_to_tag) # First, determine which stops are used on which axes. The STAT table must contain # a name for each stop that is used on each axis, so each stop must have an entry # in the Stylespace. Also include locations in additional_locations that can refer # to axes not present in the current varfont. stylespace_stops: Dict[str, Set[float]] = {} for axis in stylespace.axes: stylespace_stops[axis.tag] = {l.value for l in axis.locations} for named_location in stylespace.locations: for name, value in named_location.axis_values.items(): stylespace_stops[name_to_tag[name]].add(value) axis_stops: Mapping[str, Set[float]] = collections.defaultdict(set) # tag to stops for instance in varfont["fvar"].instances: for k, v in instance.coordinates.items(): if v not in stylespace_stops[k]: raise Error( f"There is no Stylespace entry for stop {v} on the '{k}' axis." ) axis_stops[k].add(v) for k, v in additional_locations.items(): axis_tag = name_to_tag[k] if v not in stylespace_stops[axis_tag]: raise Error( f"There is no Stylespace entry for stop {v} on the '{k}' axis (from " "additional locations)." ) axis_stops[axis_tag].add(v) # Generate formats 1, 2 and 3. builder_axes: List[Mapping[str, Any]] = [ { "tag": axis.tag, "name": axis.name.mapping, "ordering": axis.ordering, "values": [ location.to_builder_dict() for location in axis.locations if location.value in axis_stops[axis.tag] ], } for axis in stylespace.axes ] # Generate format 4. builder_locations: List[Mapping[str, Any]] = [ named_location.to_builder_dict(name_to_tag) for named_location in stylespace.locations if all( name_to_tag[k] in axis_stops and v in axis_stops[name_to_tag[k]] for k, v in named_location.axis_values.items() ) ] return builder_axes, builder_locations, stylespace.elided_fallback_name_id def _sanity_check( stylespace: statmake.classes.Stylespace, varfont: fontTools.ttLib.TTFont, additional_locations: Mapping[str, float], stylespace_name_to_tag: Mapping[str, str], ) -> None: """Ensures the input data contains no obvious faults.""" if "fvar" not in varfont: raise Error( "Need a variable font with the fvar table to determine which instances " "are present." ) # Sanity check: only allow axis names in additional_locations that are present in # the Stylespace. stylespace_names_set = set(stylespace_name_to_tag.keys()) additional_names_set = set(additional_locations.keys()) if not additional_names_set.issubset(stylespace_names_set): surplus_keys = ", ".join(additional_names_set - stylespace_names_set) raise Error( "Additional locations must only contain axis names that are present in " f"the Stylespace, the following aren't: {surplus_keys}." ) # Sanity check: Ensure all font axes are present in the Stylespace and tags match. font_name_to_tag = { _default_name_string(varfont, axis.axisNameID): axis.axisTag for axis in varfont["fvar"].axes } for name, tag in font_name_to_tag.items(): if name not in stylespace_name_to_tag: raise Error( f"Font contains axis named '{name}' which is not in Stylespace. The " "Stylespace must contain all axes any font from the same family " "contains." ) if stylespace_name_to_tag[name] != tag: raise Error( f"Font axis named '{name}' has tag '{tag}' but Stylespace defines it " f"to be '{stylespace_name_to_tag[name]}'. Axis names and tags must " "match between the font and the Stylespace." ) # Sanity check: Only allow axis names in additional_locations that aren't in the # font already. for axis_name in additional_locations: if axis_name in font_name_to_tag: raise Error( f"Rejecting the additional location for the axis named '{axis_name}' " "because it is already present in the font." ) # Sanity check: Ensure the location of the font is fully specified. This means # the font axis names plus additional_locations axis names must equal Stylespace # axis names. font_names_set = set(font_name_to_tag.keys()).union(additional_names_set) if font_names_set != stylespace_names_set: missing_axis_names = ", ".join(stylespace_names_set - font_names_set) raise Error( "The location of the font is not fully specified, missing locations " f"for the following axes: {missing_axis_names}." ) def _default_name_string(otfont: fontTools.ttLib.TTFont, name_id: int) -> str: """Return English name for name_id.""" name = otfont["name"].getName(name_id, 3, 1, 0x409) if name is None: raise Error(f"No English record for id {name_id} for Windows platform.") return name.toStr() statmake-0.4.1/src/statmake/py.typed000066400000000000000000000000001414022507300174100ustar00rootroot00000000000000statmake-0.4.1/tests/000077500000000000000000000000001414022507300144655ustar00rootroot00000000000000statmake-0.4.1/tests/__init__.py000066400000000000000000000000001414022507300165640ustar00rootroot00000000000000statmake-0.4.1/tests/conftest.py000066400000000000000000000001731414022507300166650ustar00rootroot00000000000000from pathlib import Path import pytest @pytest.fixture def datadir() -> Path: return Path(__file__).parent / "data" statmake-0.4.1/tests/data/000077500000000000000000000000001414022507300153765ustar00rootroot00000000000000statmake-0.4.1/tests/data/Test.stylespace000066400000000000000000000134531414022507300204210ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 locations name ASDF axis_values Weight 333 Italic 1 name fgfg axis_values Weight 650 Italic 0.5 flags ElidableAxisValueName statmake-0.4.1/tests/data/TestBogusFormat4.stylespace000066400000000000000000000053611414022507300226550ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 locations name ASDF axis_values Weight 333 statmake-0.4.1/tests/data/TestBogusFormat4_2.stylespace000066400000000000000000000054561414022507300231030ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 locations name ASDF axis_values Weight 333 Fooooo 333 statmake-0.4.1/tests/data/TestBroken.stylespace000066400000000000000000000030211414022507300215500ustar00rootroot00000000000000 axes locations name Extra Light range 200 value 200 name Light range 250 350 value 300 name Weight tag wght ordering 0 locations name Italic value 1 name Italic tag ital ordering 1 elided_fallback_name_id 2 statmake-0.4.1/tests/data/TestBrokenAxes.stylespace000066400000000000000000000027751414022507300224100ustar00rootroot00000000000000 axes locations name Extra Light range 200 230 value 200 name Light range 250 350 value 300 name Weight tag wght ordering 0 locations name Italic value 1 name Italic tag ital elided_fallback_name_id 2 statmake-0.4.1/tests/data/TestDuplicateValue.stylespace000066400000000000000000000023651414022507300232510ustar00rootroot00000000000000 axes name Weight tag wght locations name Regular value 400 range 350 500 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Bold value 700 statmake-0.4.1/tests/data/TestDuplicateValueFormat4.stylespace000066400000000000000000000022141414022507300244770ustar00rootroot00000000000000 axes name Weight tag wght locations locations name ASDF axis_values Weight 333 name fgfg axis_values Weight 333 flags ElidableAxisValueName statmake-0.4.1/tests/data/TestExternalStylespace.designspace000066400000000000000000000036131414022507300242670ustar00rootroot00000000000000 org.statmake.stylespacePath Test.stylespace org.statmake.additionalLocations Italic 0 statmake-0.4.1/tests/data/TestIncomplete.stylespace000066400000000000000000000042111414022507300224310ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 statmake-0.4.1/tests/data/TestInlineStylespace.designspace000066400000000000000000000110041414022507300237140ustar00rootroot00000000000000 org.statmake.stylespace axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 org.statmake.additionalLocations Italic 0 statmake-0.4.1/tests/data/TestItalIsSlnt.stylespace000066400000000000000000000134531414022507300223700ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 name Italic tag slnt locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 locations name ASDF axis_values Weight 333 Italic 1 name fgfg axis_values Weight 650 Italic 0.5 flags ElidableAxisValueName statmake-0.4.1/tests/data/TestJustWght.stylespace000066400000000000000000000034451414022507300221210ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 name Light value 300 name Regular value 400 linked_value 700 flags ElidableAxisValueName name Semi Bold value 600 name en Bold value 700 name Black value 900 range 701 900 statmake-0.4.1/tests/data/TestMissingLinkedValue.stylespace000066400000000000000000000033221414022507300240710ustar00rootroot00000000000000 axes name Weight tag wght locations name Regular value 400 linked_value 100 flags ElidableAxisValueName name en Bold value 700 name Italic tag ital locations name Upright value 0 linked_value 1 flags ElidableAxisValueName name Italic value 1 statmake-0.4.1/tests/data/TestMultilingual.stylespace000066400000000000000000000051011414022507300227770ustar00rootroot00000000000000 axes name en Weight de Gäwicht tag wght locations name Light value 300 name en Regular de Regulär value 400 linked_value 700 flags ElidableAxisValueName name en Bold fr Bôld value 700 name en Italic de Italienisch tag ital locations name en Upright de Aufrecht value 0 linked_value 1 flags ElidableAxisValueName name en Italic de Italienisch value 1 statmake-0.4.1/tests/data/TestMultilingualBrokenAxisNameLang.stylespace000066400000000000000000000051011414022507300263700ustar00rootroot00000000000000 axes name en Weight de Gäwicht tag wght locations name Light value 300 name en Regular de Regulär value 400 linked_value 700 flags ElidableAxisValueName name en Bold fr Bôld value 700 name en Italic de Italienisch tag ital locations name en Upright de Aufrecht value 0 linked_value 1 flags ElidableAxisValueName name en Italic de Italienisch value 1 statmake-0.4.1/tests/data/TestMultilingualBrokenLocNameLang.stylespace000066400000000000000000000030771414022507300262130ustar00rootroot00000000000000 axes name en Weight de Gäwicht tag wght locations name en Italic de Italienisch tag ital locations name en Upright de Aufrecht value 0 linked_value 1 flags ElidableAxisValueName name en Italic value 1 statmake-0.4.1/tests/data/TestMultilingualNoEn.stylespace000066400000000000000000000050071414022507300235640ustar00rootroot00000000000000 axes name de Gäwicht tag wght locations name Light value 300 name en Regular de Regulär value 400 linked_value 700 flags ElidableAxisValueName name en Bold fr Bôld value 700 name en Italic de Italienisch tag ital locations name en Upright de Aufrecht value 0 linked_value 1 flags ElidableAxisValueName name en Italic de Italienisch value 1 statmake-0.4.1/tests/data/TestNoFormat4.stylespace000066400000000000000000000011251414022507300221440ustar00rootroot00000000000000 axes name Weight tag wght locations name XLight value 200 statmake-0.4.1/tests/data/Test_WghtItal.designspace000066400000000000000000000106501414022507300223310ustar00rootroot00000000000000 statmake-0.4.1/tests/data/Test_WghtItal_Multilingual.designspace000066400000000000000000000050261414022507300250600ustar00rootroot00000000000000 statmake-0.4.1/tests/data/Test_Wght_Italic.designspace000066400000000000000000000043641414022507300230110ustar00rootroot00000000000000 org.statmake.additionalLocations Italic 1 statmake-0.4.1/tests/data/Test_Wght_Upright.designspace000066400000000000000000000040251414022507300232200ustar00rootroot00000000000000 org.statmake.additionalLocations Italic 0 statmake-0.4.1/tests/test_cli.py000066400000000000000000000066311414022507300166530ustar00rootroot00000000000000import fontTools.designspaceLib import fontTools.ttLib import pytest import ufo2ft import statmake.cli from . import testutil def test_cli_stylespace_in_designspace(datadir, tmp_path): varfont = empty_varfont(datadir / "Test_Wght_Upright.designspace") varfont.save(tmp_path / "varfont.ttf") statmake.cli.main( [ "-m", str(datadir / "TestInlineStylespace.designspace"), str(tmp_path / "varfont.ttf"), ] ) font = fontTools.ttLib.TTFont(tmp_path / "varfont.ttf") v = testutil.dump_axis_values(font, font["STAT"].table.AxisValueArray.AxisValue) assert v == TEST_WGHT_UPRIGHT_STAT_DUMP def test_cli_designspace_stylespace_external(datadir, tmp_path): varfont = empty_varfont(datadir / "Test_Wght_Upright.designspace") varfont.save(tmp_path / "varfont.ttf") statmake.cli.main( [ "-m", str(datadir / "TestExternalStylespace.designspace"), str(tmp_path / "varfont.ttf"), ] ) font = fontTools.ttLib.TTFont(tmp_path / "varfont.ttf") v = testutil.dump_axis_values(font, font["STAT"].table.AxisValueArray.AxisValue) assert v == TEST_WGHT_UPRIGHT_STAT_DUMP def test_cli_stylespace_external(datadir, tmp_path): varfont = empty_varfont(datadir / "Test_Wght_Upright.designspace") varfont.save(tmp_path / "varfont.ttf") statmake.cli.main( [ "-m", str(datadir / "Test_Wght_Upright.designspace"), "--stylespace", str(datadir / "Test.stylespace"), str(tmp_path / "varfont.ttf"), ] ) font = fontTools.ttLib.TTFont(tmp_path / "varfont.ttf") v = testutil.dump_axis_values(font, font["STAT"].table.AxisValueArray.AxisValue) assert v == TEST_WGHT_UPRIGHT_STAT_DUMP def test_cli_stylespace_in_broken_designspace(datadir, tmp_path): with pytest.raises(SystemExit): statmake.cli.main( [ "-m", str(datadir / "Test_Wght_Upright.designspace"), str(tmp_path / "varfont.ttf"), ] ) def empty_varfont(designspace_path): designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile( designspace_path ) for source in designspace.sources: source.font = testutil.empty_UFO(source.styleName) ufo2ft.compileInterpolatableTTFsFromDS(designspace, inplace=True) varfont, _, _ = fontTools.varLib.build(designspace) return varfont TEST_WGHT_UPRIGHT_STAT_DUMP = [ {"Format": 1, "Name": {"en": "XLight"}, "Flags": 0, "AxisIndex": 0, "Value": 200.0}, {"Format": 1, "Name": {"en": "Light"}, "Flags": 0, "AxisIndex": 0, "Value": 300.0}, { "Format": 3, "Name": {"en": "Regular"}, "Flags": 2, "AxisIndex": 0, "Value": 400.0, "LinkedValue": 700.0, }, { "Format": 1, "Name": {"en": "Semi Bold"}, "Flags": 0, "AxisIndex": 0, "Value": 600.0, }, {"Format": 1, "Name": {"en": "Bold"}, "Flags": 0, "AxisIndex": 0, "Value": 700.0}, { "Format": 2, "Name": {"en": "Black"}, "Flags": 0, "AxisIndex": 0, "NominalValue": 900.0, "RangeMinValue": 701.0, "RangeMaxValue": 900.0, }, { "Format": 3, "Name": {"en": "Upright"}, "Flags": 2, "AxisIndex": 1, "Value": 0.0, "LinkedValue": 1.0, }, ] statmake-0.4.1/tests/test_make_stat.py000066400000000000000000000311721414022507300200520ustar00rootroot00000000000000import fontTools.designspaceLib import fontTools.otlLib.builder import pytest import statmake.classes import statmake.lib from statmake.errors import Error, StylespaceError from . import testutil def test_load_stylespace_broken_range(datadir): with pytest.raises(StylespaceError, match=r"Range .*"): statmake.classes.Stylespace.from_file(datadir / "TestBroken.stylespace") def test_load_stylespace_broken_ordering(datadir): with pytest.raises(StylespaceError, match=r".* ordering .*"): statmake.classes.Stylespace.from_file(datadir / "TestBrokenAxes.stylespace") def test_load_stylespace_broken_format4_1(datadir): with pytest.raises( StylespaceError, match=r".* must specify values for all axes .*" ): statmake.classes.Stylespace.from_file(datadir / "TestBogusFormat4.stylespace") def test_load_stylespace_broken_format4_2(datadir): with pytest.raises( StylespaceError, match=r".* must specify values for all axes .*" ): statmake.classes.Stylespace.from_file(datadir / "TestBogusFormat4_2.stylespace") def test_load_stylespace_missing_linked_value(datadir): with pytest.raises( StylespaceError, match=r".* location 'Regular' specifies a linked_value of '100.0'.*", ): statmake.classes.Stylespace.from_file( datadir / "TestMissingLinkedValue.stylespace" ) def test_load_stylespace_duplicate_value(datadir): with pytest.raises( StylespaceError, match=r".* 'Regular' specifies a duplicate location value of '400.0'.*", ): statmake.classes.Stylespace.from_file(datadir / "TestDuplicateValue.stylespace") def test_load_stylespace_duplicate_value_format4(datadir): with pytest.raises( StylespaceError, match=r".* location 'fgfg' specifies a duplicate location .*", ): statmake.classes.Stylespace.from_file( datadir / "TestDuplicateValueFormat4.stylespace" ) def test_load_stylespace_broken_multilingual_no_en(datadir): with pytest.raises(StylespaceError, match=r".* must have a default English .*"): statmake.classes.Stylespace.from_file( datadir / "TestMultilingualNoEn.stylespace" ) def test_load_stylespace_broken_multilingual_incomplete_lang(datadir): with pytest.raises( StylespaceError, match=r".* languages \['en'\] but expected was \['de', 'en'\]." ): statmake.classes.Stylespace.from_file( datadir / "TestMultilingualBrokenAxisNameLang.stylespace" ) def test_load_stylespace_broken_multilingual_incomplete_lang2(datadir): with pytest.raises( StylespaceError, match=r".* languages \['en'\] but expected was \['de', 'en'\]." ): statmake.classes.Stylespace.from_file( datadir / "TestMultilingualBrokenLocNameLang.stylespace" ) def test_load_stylespace_no_format4(datadir): statmake.classes.Stylespace.from_file(datadir / "TestNoFormat4.stylespace") def test_load_from_designspace(datadir): designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile( datadir / "TestInlineStylespace.designspace" ) statmake.classes.Stylespace.from_designspace(designspace) def test_load_from_broken_designspace(datadir): designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile( datadir / "TestNoFormat4.stylespace" ) with pytest.raises(StylespaceError, match=r".* lib .*"): statmake.classes.Stylespace.from_designspace(designspace) def test_generation_incomplete_stylespace(datadir): with pytest.raises(Error, match=r".* no Stylespace entry .*"): _ = testutil.generate_variable_font( datadir / "Test_Wght_Italic.designspace", datadir / "TestIncomplete.stylespace", ) def test_generation_incomplete_additional_location(datadir): with pytest.raises( Error, match=r".* no Stylespace entry .* additional locations.*" ): _ = testutil.generate_variable_font( datadir / "Test_Wght_Italic.designspace", datadir / "Test.stylespace", {"Italic": 2}, ) def test_generation_disjunct_additional_location(datadir): with pytest.raises(Error, match=r".* the following aren't: Foo."): _ = testutil.generate_variable_font( datadir / "Test_Wght_Italic.designspace", datadir / "Test.stylespace", {"Foo": 2}, ) def test_generation_superfluous_additional_location(datadir): with pytest.raises( Error, match=r"Rejecting the additional location for the axis named 'Italic'.*" ): _ = testutil.generate_variable_font( datadir / "Test_WghtItal.designspace", datadir / "Test.stylespace", {"Italic": 1}, ) def test_generation_unknown_font_axis(datadir): with pytest.raises( Error, match=r"Font contains axis named 'Italic' which is not in Stylespace.*" ): _ = testutil.generate_variable_font( datadir / "Test_WghtItal.designspace", datadir / "TestJustWght.stylespace", {}, ) def test_generation_wrong_tag(datadir): with pytest.raises( Error, match=r"Font axis named 'Italic' has tag 'ital' but Stylespace .* 'slnt'.", ): _ = testutil.generate_variable_font( datadir / "Test_WghtItal.designspace", datadir / "TestItalIsSlnt.stylespace", {}, ) def test_generation_incomplete_location(datadir): with pytest.raises( Error, match=r"missing locations for the following axes: Italic.", ): _ = testutil.generate_variable_font( datadir / "Test_Wght_Italic.designspace", datadir / "Test.stylespace", {}, ) def test_generation_full(datadir): varfont = testutil.generate_variable_font( datadir / "Test_WghtItal.designspace", datadir / "Test.stylespace" ) stat_table = varfont["STAT"] assert stat_table.table.Version == 0x00010002 stat_axes = testutil.dump_axes(varfont, stat_table.table.DesignAxisRecord.Axis) stat_axes_expected = [ {"Name": "Weight", "AxisTag": "wght", "AxisOrdering": 0}, {"Name": "Italic", "AxisTag": "ital", "AxisOrdering": 1}, ] assert stat_axes == stat_axes_expected stat_axis_values = testutil.dump_axis_values( varfont, stat_table.table.AxisValueArray.AxisValue ) stat_axis_values_expected = [ { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "XLight"}, "Value": 200.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Light"}, "Value": 300.0, }, { "AxisIndex": 0, "Flags": 2, "Format": 3, "LinkedValue": 700.0, "Name": {"en": "Regular"}, "Value": 400.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Semi Bold"}, "Value": 600.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Bold"}, "Value": 700.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 2, "Name": {"en": "Black"}, "NominalValue": 900.0, "RangeMaxValue": 900.0, "RangeMinValue": 701.0, }, { "AxisIndex": 1, "Flags": 2, "Format": 3, "LinkedValue": 1.0, "Name": {"en": "Upright"}, "Value": 0.0, }, { "AxisIndex": 1, "Flags": 0, "Format": 1, "Name": {"en": "Italic"}, "Value": 1.0, }, { "AxisValueRecord": [(0, 333.0), (1, 1.0)], "Flags": 0, "Format": 4, "Name": {"en": "ASDF"}, }, { "AxisValueRecord": [(0, 650.0), (1, 0.5)], "Flags": 2, "Format": 4, "Name": {"en": "fgfg"}, }, ] assert sorted(stat_axis_values, key=lambda x: x["Name"]["en"]) == sorted( stat_axis_values_expected, key=lambda x: x["Name"]["en"] ) assert stat_table.table.ElidedFallbackNameID == 2 def test_generation_upright(datadir): varfont = testutil.generate_variable_font( datadir / "Test_Wght_Upright.designspace", datadir / "Test.stylespace" ) stat_table = varfont["STAT"] assert stat_table.table.Version == 0x00010001 stat_axes = testutil.dump_axes(varfont, stat_table.table.DesignAxisRecord.Axis) stat_axes_expected = [ {"Name": "Weight", "AxisTag": "wght", "AxisOrdering": 0}, {"Name": "Italic", "AxisTag": "ital", "AxisOrdering": 1}, ] assert stat_axes == stat_axes_expected stat_axis_values = testutil.dump_axis_values( varfont, stat_table.table.AxisValueArray.AxisValue ) stat_axis_values_expected = [ { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "XLight"}, "Value": 200.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Light"}, "Value": 300.0, }, { "AxisIndex": 0, "Flags": 2, "Format": 3, "LinkedValue": 700.0, "Name": {"en": "Regular"}, "Value": 400.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Semi Bold"}, "Value": 600.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Bold"}, "Value": 700.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 2, "Name": {"en": "Black"}, "NominalValue": 900.0, "RangeMaxValue": 900.0, "RangeMinValue": 701.0, }, { "AxisIndex": 1, "Flags": 2, "Format": 3, "LinkedValue": 1.0, "Name": {"en": "Upright"}, "Value": 0.0, }, ] assert stat_axis_values == stat_axis_values_expected assert stat_table.table.ElidedFallbackNameID == 2 def test_generation_italic(datadir): varfont = testutil.generate_variable_font( datadir / "Test_Wght_Italic.designspace", datadir / "Test.stylespace" ) stat_table = varfont["STAT"] assert stat_table.table.Version == 0x00010002 stat_axes = testutil.dump_axes(varfont, stat_table.table.DesignAxisRecord.Axis) stat_axes_expected = [ {"Name": "Weight", "AxisTag": "wght", "AxisOrdering": 0}, {"Name": "Italic", "AxisTag": "ital", "AxisOrdering": 1}, ] assert stat_axes == stat_axes_expected stat_axis_values = testutil.dump_axis_values( varfont, stat_table.table.AxisValueArray.AxisValue ) stat_axis_values_expected = [ { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "XLight"}, "Value": 200.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Light"}, "Value": 300.0, }, { "AxisIndex": 0, "Flags": 2, "Format": 3, "LinkedValue": 700.0, "Name": {"en": "Regular"}, "Value": 400.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Semi Bold"}, "Value": 600.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 1, "Name": {"en": "Bold"}, "Value": 700.0, }, { "AxisIndex": 0, "Flags": 0, "Format": 2, "Name": {"en": "Black"}, "NominalValue": 900.0, "RangeMaxValue": 900.0, "RangeMinValue": 701.0, }, { "AxisIndex": 1, "Flags": 0, "Format": 1, "Name": {"en": "Italic"}, "Value": 1.0, }, { "AxisValueRecord": [(0, 333.0), (1, 1.0)], "Flags": 0, "Format": 4, "Name": {"en": "ASDF"}, }, ] assert sorted(stat_axis_values, key=lambda x: x["Name"]["en"]) == sorted( stat_axis_values_expected, key=lambda x: x["Name"]["en"] ) assert stat_table.table.ElidedFallbackNameID == 2 statmake-0.4.1/tests/test_serialize.py000066400000000000000000000004301414022507300200620ustar00rootroot00000000000000from pathlib import Path from statmake.classes import Stylespace def test_serialize(datadir: Path) -> None: stylespace = Stylespace.from_file(datadir / "Test.stylespace") stylespace_rt = Stylespace.from_dict(stylespace.to_dict()) assert stylespace == stylespace_rt statmake-0.4.1/tests/testutil.py000066400000000000000000000066521414022507300167250ustar00rootroot00000000000000import io from pathlib import Path from typing import Mapping, Optional import fontTools.designspaceLib import fontTools.ttLib import fontTools.ttLib.tables._n_a_m_e import fontTools.varLib import ufo2ft import ufoLib2 import statmake.classes import statmake.lib def dump_axes(font, axes_array): dump_list = [] for axis in axes_array: entry = { "Name": statmake.lib._default_name_string(font, axis.AxisNameID), "AxisTag": axis.AxisTag, "AxisOrdering": axis.AxisOrdering, } dump_list.append(entry) return dump_list def dump_axis_values(font, axis_value_array): dump_list = [] for axis in axis_value_array: entry = { "Format": axis.Format, "Name": dump_name_ids(font, axis.ValueNameID), "Flags": axis.Flags, } if axis.Format == 1: entry["AxisIndex"] = axis.AxisIndex entry["Value"] = axis.Value elif axis.Format == 2: entry["AxisIndex"] = axis.AxisIndex entry["NominalValue"] = axis.NominalValue entry["RangeMinValue"] = axis.RangeMinValue entry["RangeMaxValue"] = axis.RangeMaxValue elif axis.Format == 3: entry["AxisIndex"] = axis.AxisIndex entry["Value"] = axis.Value entry["LinkedValue"] = axis.LinkedValue elif axis.Format == 4: entry["AxisValueRecord"] = [ (r.AxisIndex, r.Value) for r in axis.AxisValueRecord ] else: raise ValueError("Unknown format") dump_list.append(entry) return dump_list def dump_name_ids(otfont: fontTools.ttLib.TTFont, name_id: int) -> Mapping[str, str]: """Return a mapping of language codes to name strings.""" name_mapping = fontTools.ttLib.tables._n_a_m_e._WINDOWS_LANGUAGES name_table = otfont["name"].names matches = { name_mapping[n.langID]: n.toUnicode() for n in name_table if n.platformID == 3 and n.nameID == name_id } return matches def empty_UFO(style_name: str) -> ufoLib2.Font: ufo = ufoLib2.Font() ufo.info.familyName = "Test" ufo.info.styleName = style_name ufo.info.unitsPerEm = 1000 ufo.info.ascender = 800 ufo.info.descender = -200 ufo.info.xHeight = 500 ufo.info.capHeight = 700 ufo.info.postscriptUnderlineThickness = 50 ufo.info.postscriptUnderlinePosition = -75 g = ufo.newGlyph("a") g.width = 500 return ufo def reload_font(font): buf = io.BytesIO() font.save(buf) buf.seek(0) return fontTools.ttLib.TTFont(buf) def generate_variable_font( designspace_path: Path, stylespace_path: Path, additional_locations: Optional[Mapping[str, float]] = None, ) -> fontTools.ttLib.TTFont: designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile( designspace_path ) for source in designspace.sources: source.font = empty_UFO(source.styleName) ufo2ft.compileInterpolatableTTFsFromDS(designspace, inplace=True) varfont, _, _ = fontTools.varLib.build(designspace) stylespace = statmake.classes.Stylespace.from_file(stylespace_path) if additional_locations is None: additional_locations = designspace.lib.get( "org.statmake.additionalLocations", {} ) statmake.lib.apply_stylespace_to_variable_font( stylespace, varfont, additional_locations ) return reload_font(varfont) statmake-0.4.1/tox.ini000066400000000000000000000012161414022507300146360ustar00rootroot00000000000000[tox] isolated_build = true envlist = lint, py3{7,8,9,10}-cov, htmlcov [testenv] whitelist_externals = poetry commands = poetry install cov: poetry run coverage run --parallel-mode -m pytest {posargs} !cov: poetry run pytest {posargs} [testenv:htmlcov] basepython = python3 deps = coverage skip_install = true commands = coverage combine coverage report coverage html [testenv:lint] whitelist_externals = poetry commands = poetry install --no-root poetry run black --check --diff src tests poetry run isort --check-only --diff --recursive src tests poetry run mypy src tests poetry run pylint src tests