Skip to content

Commit 83dbc82

Browse files
committed
Updates for ots 8.2.0
- use ots 8.2.0 source - [ci] drop Python 3.6, add Python 3.10 for release - [ci] switch to CIBW to build wheels (still macOS & Ubuntu only) - [docs] update README.md with current info
1 parent ff15ab1 commit 83dbc82

File tree

4 files changed

+94
-128
lines changed

4 files changed

+94
-128
lines changed

.github/workflows/release.yml

Lines changed: 76 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,101 @@
1-
name: Create Release and build wheels
1+
name: Build Python Wheels
22

33
on:
44
push:
55
tags:
6-
- '[0-9].*'
6+
- '\d+\.\d+\.[0-9a-z]+'
7+
8+
workflow_dispatch:
9+
inputs:
10+
reason:
11+
description: 'Reason for running workflow'
12+
required: true
713

814
jobs:
9-
do_release:
10-
name: Create release and build manylinux wheels
11-
runs-on: ubuntu-latest
12-
15+
build_wheels:
16+
name: Build Py3 Wheel on ${{ matrix.os }}
17+
runs-on: ${{ matrix.os }}
18+
strategy:
19+
matrix:
20+
os: [ubuntu-latest, macos-latest]
21+
1322
steps:
14-
- name: Check out project
23+
24+
- name: Log reason (manual run only)
25+
if: github.event_name == 'workflow_dispatch'
26+
run: |
27+
echo "Reason for triggering: ${{ github.event.inputs.reason }}"
28+
29+
- name: Check out
1530
uses: actions/checkout@v2
16-
17-
- name: Get tag
18-
id: get_tag
19-
# strip 'refs/tags/' from ref, store in variable called VERSION
20-
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
21-
22-
- name: Create Release
23-
id: create_release
24-
uses: actions/create-release@v1
25-
env:
26-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2731
with:
28-
tag_name: ${{ steps.get_tag.outputs.VERSION }} # VERSION from get_tag step above
29-
release_name: v${{ steps.get_tag.outputs.VERSION }}
30-
draft: false
31-
prerelease: true
32+
fetch-depth: 0 # unshallow fetch for setuptools-scm
3233

33-
- name: Set up Python
34-
uses: actions/setup-python@v1
34+
- name: Install Python 3.9
35+
uses: actions/setup-python@v2
3536
with:
36-
python-version: 3.7
37+
python-version: '3.9'
3738

38-
- name: Install dependencies
39-
run: |
40-
python -m pip install --upgrade pip
41-
pip install -r requirements.txt
42-
pip install wheel setuptools_scm[toml]'>=3.4'
43-
python setup.py download
44-
45-
- name: Build manylinux Python wheels
46-
id: build_wheels
47-
uses: RalfG/python-wheels-manylinux-build@v0.2.2-manylinux2010_x86_64
39+
- name: Build wheel
40+
uses: pypa/cibuildwheel@v2.3.0
4841
with:
49-
python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39'
50-
build-requirements: 'meson ninja'
51-
system-packages: 'zlib-devel'
52-
53-
- name: Attach wheel and sdist assets to release
42+
output-dir: dist
5443
env:
55-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
CIBW_BUILD: "cp37-* cp38-* cp39-* cp310-*"
45+
CIBW_ARCHS_MACOS: x86_64 universal2
46+
CIBW_ARCHS_LINUX: x86_64
47+
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
48+
CIBW_SKIP: "*musllinux*"
49+
CIBW_BEFORE_ALL_LINUX: "yum install -y libuuid-devel"
50+
51+
- name: Build sdist (Ubuntu only)
52+
if: matrix.os == 'ubuntu-latest'
5653
run: |
57-
mkdir -p dist
58-
for audited in ./wheelhouse/*-manylinux*.whl; do
59-
cp $audited dist
60-
done
61-
set -x
62-
assets=()
63-
for asset in ./dist/*; do
64-
assets+=("-a" "$asset")
65-
done
66-
hub release edit "${assets[@]}" -m "${{ steps.get_tag.outputs.VERSION }}" "${{ steps.get_tag.outputs.VERSION }}"
67-
68-
do_mac_wheels:
69-
needs: [do_release]
70-
name: Build Mac wheels
71-
runs-on: macos-latest
72-
strategy:
73-
matrix:
74-
python-version: [3.6, 3.7, 3.8, 3.9]
54+
python setup.py sdist
7555
76-
steps:
77-
- name: Check out project
78-
uses: actions/checkout@v2
79-
80-
- name: Get tag
81-
id: get_tag
82-
# strip 'refs/tags/' from ref, store in variable called VERSION
83-
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
84-
85-
- name: Set up Python ${{ matrix.python-version }}
86-
uses: actions/setup-python@v1
56+
- name: Upload build artifacts
57+
uses: actions/upload-artifact@v2
8758
with:
88-
python-version: ${{ matrix.python-version }}
59+
name: wheelstorage
60+
path: ./dist/*
61+
if-no-files-found: error
62+
retention-days: 30
63+
64+
publish_release:
65+
name: Publish Release
66+
needs: build_wheels
67+
runs-on: ubuntu-latest
8968

90-
- name: Install dependencies, build wheels, and attach to release
91-
env:
92-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
93-
run: |
94-
python -m pip install --upgrade pip
95-
pip install -r requirements.txt
96-
pip install wheel
97-
python setup.py download
98-
pip wheel . -w dist
99-
for asset in ./dist/*.whl; do
100-
assets+=("-a" "$asset")
101-
done
102-
hub release edit "${assets[@]}" -m "${{ steps.get_tag.outputs.VERSION }}" "${{ steps.get_tag.outputs.VERSION }}"
103-
104-
do_pypi_publish:
105-
needs: [do_release, do_mac_wheels] # wait for the other jobs to complete
106-
runs-on: ubuntu-latest # pypa/gh-action-pypi-publish only runs on Linux...
107-
10869
steps:
109-
- name: Check out project
110-
uses: actions/checkout@v2
111-
112-
- name: Get tag
113-
id: get_tag
114-
# strip 'refs/tags/' from ref, store in variable called VERSION
115-
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
116-
117-
- name: Set up Python 3.8
118-
uses: actions/setup-python@v1
119-
with:
120-
python-version: 3.8
12170

122-
- name: Build Python source tarball
123-
# do this here, because we can't use the GitHub .tar.gz (no PKG-INFO)
124-
# should go into 'dist' folder to be picked up when publishing to PyPI
71+
- name: Get date & flat tag
72+
id: date_tag
12573
run: |
126-
pip install wheel setuptools_scm[toml]'>=3.4'
127-
python setup.py sdist
74+
export DATE=$(TZ=US/Pacific date +'%Y-%m-%d')
75+
echo $DATE
76+
export FLAT_TAG=$(echo ${GITHUB_REF##*/} | sed 's/\.//g')
77+
echo $FLAT_TAG
78+
echo ::set-output name=TODAY::$DATE
79+
echo ::set-output name=VERSION::$FLAT_TAG
80+
shell: bash
12881

129-
- name: Download .whl assets from release
130-
env:
131-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132-
run: |
133-
hub release download "${{ steps.get_tag.outputs.VERSION }}"
134-
mkdir -p dist
135-
cp *.whl dist # move .whl files to dist for publishing
136-
137-
- name: Publish wheels to PyPI
138-
uses: pypa/gh-action-pypi-publish@master
82+
- name: Download release assets
83+
uses: actions/download-artifact@v2
84+
with:
85+
name: wheelstorage
86+
path: dist
87+
88+
- name: Publish dist(s) to PyPI
89+
uses: pypa/gh-action-pypi-publish@release/v1
13990
with:
14091
user: __token__
14192
password: ${{ secrets.pypi_password }}
93+
94+
- name: Create GitHub Release
95+
uses: softprops/action-gh-release@v1
96+
env:
97+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
98+
with:
99+
body: '{{ steps.date_tag.outputs.VERSION }}-released-${{ steps.date_tag.outputs.TODAY }}'
100+
prerelease: true
101+
files: ./dist/*

README.md

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
# pyots (PYthon OT Sanitizer)
2+
3+
[![Run Tests](https://github.com/adobe-type-tools/pyots/actions/workflows/run_tests.yml/badge.svg)](https://github.com/adobe-type-tools/pyots/actions/workflows/run_tests.yml) [![Build Python Wheels](https://github.com/adobe-type-tools/pyots/actions/workflows/release.yml/badge.svg)](https://github.com/adobe-type-tools/pyots/actions/workflows/release.yml)
4+
5+
![PyPI](https://img.shields.io/pypi/v/pyots) [![PyPI](https://img.shields.io/pypi/pyversions/pyots)](https://pypi.org/project/pyots/)
6+
7+
![macOS](https://img.shields.io/badge/-macOS-lightgrey) ![ubuntu](https://img.shields.io/badge/-ubuntu-lightgrey)
8+
29
Python wrapper for [OpenType Sanitizer](https://github.com/khaledhosny/ots), also known as just "OTS". It is similar to and partially based on [ots-python](https://github.com/googlefonts/ots-python), but builds OTS as a Python C Extension (instead of as an executable and calling through `subprocess` as ots-python does).
310

411
**NOTE:** Although this package is similar to **ots-python**, it is _not_ a drop-in replacement for it, as the Python API is different.
512

613
## Requirements
7-
The project builds `pip`-installable wheels for Python 3.6, 3.7, 3.8, or 3.9 under Mac or Linux. It is possible this project will build and run with other Pythons and other operating systems, but it has only been tested with the listed configurations.
14+
The project builds `pip`-installable wheels for Python 3.7, 3.8, 3.9 or 3.10 under Mac or Linux. It is possible this project will build and run with other Pythons and other operating systems, but it has only been tested with the listed configurations.
815

916
## Installation with `pip`
1017
If you just want to _use_ `pyots`, you can simply run `pip install pyots` (in one of the supported platforms/Python versions) which will install pre-built, compiled, ready-to-use Python wheels. Then you can skip down to the [Use](#Use) section.
@@ -14,6 +21,7 @@ If you'd like to tinker with the `pyots` code, you will want to get your local s
1421
- clone this repo
1522
- run `python setup.py download` to download the OTS source (which is _not included_ in this project). You can modify the `version` value in [`setup.cfg`](./setup.cfg) under `[download]` to specify a different version of OTS. You'll also need to change the `sha256` hash value that corresponds to the OTS tar.xz package. Note that this scheme has some limitations: OTS sources older than 8.1.3 might not build correctly since they used different build systems. Also, versions newer than the one specified in this repo might require adjustments in order to build correctly. What can we say, we're dependent on `ots`...
1623
- to build and install `pyots` after downloading OTS, you can run `python setup.py install` or `pip install .`
24+
- while iterating changes, you will want to delete the temporary `build` and `src/ots/build` folders.
1725

1826
## Testing
1927
There is a test suite defined for exercising the Python extension. It makes use (and assumes the presence of) the downloaded OTS library source's test font data in `src/ots` so ensure you have run `python setup.py download` and have the `ots` folder under `src`. Invoke the tests with `python -m pytest tests` *OR* `pytest tests` (make sure you specify `tests` folder, otherwise `pytest` will discover and attempt to execute other Python tests within the `ots` tree, which will probably fail)
@@ -25,25 +33,23 @@ import pyots
2533
result = pyots.sanitize('/path/to/font/file.ttf')
2634
```
2735

28-
`result` is an `OTSResult` ([`namedtuple`](https://docs.python.org/3/library/collections.html#collections.namedtuple)/lightweight object) with 3 attributes:
36+
`result` is an `OTSResult` object with 3 attributes:
2937
- `sanitized` Boolean indicating whether the file was successfully sanitized
3038
- `modified` Boolean indicating whether the file was modified* during sanitization
3139
- `messages` Tuple of message strings generated during sanitization (may be empty)
3240

33-
* **Note:** currently the back-end OTS code _always_ modifies fonts that are successfully sanitized. Thus `modified` will be True for all cases where `sanitized` is True. Usually the modification is only to the modification date and related checksums. Thus, it might be possible to devise a better detection of modification, i.e. ignoring `head.modified` and other inconsequential modifications, but that was out-of-scope for this work.
41+
* **Note:** currently the back-end OTS code can modify fonts that are successfully sanitized, even when no changes are performed. Thus `modified` can sometimes be True when `sanitized` is True. Usually the modification is only to the modification date and related checksums. Thus, it might be possible to devise a better detection of modification, i.e. ignoring `head.modified` and other inconsequential modifications, but that was out-of-scope for this work.
3442

3543
### Example: sanitizing a folder of font files
3644
```python
3745
# sanitize a folder of fonts. Print messages for any that were not successfully sanitized.
3846
import pyots
39-
import os
47+
from pathlib import Path
4048

41-
foldername = 'path/to/folder'
42-
for filename in os.listdir(foldername):
43-
filepath = os.path.join(foldername, filename)
44-
result = pyots.sanitize(filepath)
49+
for filename in Path("src/tests/fonts/good").rglob("*"):
50+
result = pyots.sanitize(filename.absolute())
4551
if not result.sanitized:
46-
print('{}:\n{}'.format(filepath, "\n".join([m for m in result.messages])))
52+
print('{}:\n{}'.format(filename, "\n".join([m for m in result.messages])))
4753
```
4854

4955
### Options for `sanitize()`

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[download]
22
; OpenType Sanitizer version that is downloaded from setup.py
3-
version = 8.1.4
3+
version = 8.2.0
44
; expected SHA-256 of the downloaded tarball. E.g. you can calculate it with:
55
; $ shasum -a 256 ots-X.X.X.tar.xz
6-
sha256 = f21b927b03248e392e416c765eea4940c21a4e82f09045bc893b141eb57f1e29
6+
sha256 = e56f173008996a6292756a6a43186a00e638ed48dd9334df4d9aa20981b442c0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def run(self):
245245
)
246246
# remove unused ('executable('ots-sanitize' and all after)
247247
meson = re.sub(
248-
r"executable\('ots-sanitize'(.+)",
248+
r"ots_sanitize = executable\('ots-sanitize',(.+)",
249249
'',
250250
meson,
251251
flags=re.MULTILINE | re.DOTALL,

0 commit comments

Comments
 (0)