Skip to content

Commit

Permalink
Merge pull request #410 from justvanrossum/maintainance-2024-05
Browse files Browse the repository at this point in the history
Maintainance 2024 05
  • Loading branch information
justvanrossum authored May 25, 2024
2 parents 9d6f1c6 + 3b81805 commit 52bd5e6
Show file tree
Hide file tree
Showing 17 changed files with 28,889 additions and 28,595 deletions.
48 changes: 31 additions & 17 deletions .github/workflows/buildapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,35 @@ jobs:
rm -fr *.p12
# security find-identity -v -p codesigning
- name: Set up Python 3.9
run: |
curl https://www.python.org/ftp/python/3.9.13/python-3.9.13-macosx10.9.pkg --output pythonInstaller.pkg
sudo installer -pkg pythonInstaller.pkg -target /
- name: Check Python
run: |
python3 --version
python3 -c "import platform; print('macOS version:', platform.mac_ver()[0])"
- name: Git checkout
uses: actions/checkout@v4

- name: Checkout
uses: actions/checkout@v1

- name: Setup Virtual Environment
- name: Set up Python from python.org
run: |
python3 -m venv venv
curl https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg --output python-installer.pkg
sudo installer -pkg python-installer.pkg -target /
# Somehow using plain "python3" gives us the runner's homebrew Python,
# so let's be explicit about the path:
ourpython=/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12
ls -l $ourpython
$ourpython --version
$ourpython -c "import platform; print('platform:', platform.platform())"
$ourpython -c "import platform; print('macOS version:', platform.mac_ver()[0])"
$ourpython -m venv venv
source venv/bin/activate
python -c "print('venv')"
python -c "import sys; print('\n'.join(sys.path))"
python -c "import platform; print('platform:', platform.platform())"
python -c "import platform; print('macOS version:', platform.mac_ver()[0])"
- name: Install Dependencies
- name: Install dependencies
run: |
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
python -m pip install --upgrade pip
python -m pip --version
pip install -r requirements.txt | tee pip_log.txt
python App/Distribute/ensure_universal_wheels.py pip_log.txt
pip install --force build/universal_wheels/*.whl
pip install -r requirements-dev.txt
pip install .
Expand Down Expand Up @@ -89,8 +94,15 @@ jobs:
xcrun stapler staple "$DMG_PATH"
- name: Storing macOS Artifacts
uses: actions/upload-artifact@v4
with:
name: FontGoggles
path: App/dist/FontGoggles.dmg

- name: Read CHANGELOG.md
id: changelog
if: github.ref == 'refs/heads/master'
env:
GITHUB_REF: ${{ github.ref }}
run: |
Expand All @@ -99,6 +111,7 @@ jobs:
- name: Create Release
id: create_release
if: github.ref == 'refs/heads/master'
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -111,6 +124,7 @@ jobs:

- name: Upload Release Asset
id: upload-release-asset
if: github.ref == 'refs/heads/master'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/runtests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: Run FontGoggles test suite

on:
push:
branches: [master]
paths-ignore:
- docs/*
- docsSource/*
Expand All @@ -13,17 +14,18 @@ on:
- README.md

pull_request:
branches: [master]

jobs:
build:
runs-on: macos-latest

steps:
- uses: actions/checkout@v1
- name: Set up Python 3.9
uses: actions/setup-python@v1
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
2 changes: 1 addition & 1 deletion App/Distribute/build_dmg.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
try:
createCommand = [
"hdiutil", "create", "-fs", "HFS+",
"-size", "200m",
"-size", "300m",
"-srcfolder", imgPath,
"-volname", appName,
"-format", "UDZO",
Expand Down
139 changes: 139 additions & 0 deletions App/Distribute/ensure_universal_wheels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import argparse
import json
import pathlib
import re
import sys
from tempfile import TemporaryDirectory
from urllib.request import urlopen

from delocate.fuse import fuse_wheels
from packaging.utils import parse_wheel_filename

#
# The problem we are trying to solve is:
# - To build a universal2 app with py2app or PyInstaller, we need _all_ compiled packages
# to be universal2
# - This is hard for two reasons:
# - Not all packages offer universal2 wheels
# - When running on x86, pip will serve x86, even if universal2 is available
#
# We take the following approach:
# - Run `pip install -r requirements.txt` and capture the output
# - Find and parse all wheel filenames
# - Any wheel that is x86 or arm64 needs attention (eg. we ignore "any" and "universal2"):
# - Check the pypi json for the package + version
# - If there is a universal2 wheel, download it, write to `build/universal_wheels/*.whl`
# - Else if there are x86 and arm64 wheels, download both and merge, write to
# `build/universal_wheels/*.whl`
# - Else: error
# - `pip install --force build/universal_wheels/*.whl`
#

python_versions = {f"cp{sys.version_info.major}{sys.version_info.minor}", "py3"}


class IncompatibleWheelError(Exception):
pass


def url_filename(url):
return url.rsplit("/", 1)[-1]


def download_file(url, dest_dir):
filename = url_filename(url)
print("downloading wheel", filename)
response = urlopen(url)
with open(dest_dir / filename, "wb") as f:
f.write(response.read())


def merge_wheels(url1, url2, dest_dir):
wheel_name1 = url_filename(url1)
wheel_name2 = url_filename(url2)
print("merging wheels", wheel_name1, "and", wheel_name2)

with TemporaryDirectory() as tmpdir:
tmpdir = pathlib.Path(tmpdir)

download_file(url1, tmpdir)
download_file(url2, tmpdir)

wheel_names = [wheel_name1, wheel_name2]
wheel_names.sort()

assert any("x86" in name for name in wheel_names)
assert any("arm64" in name for name in wheel_names)

package, version, build, tags = parse_wheel_filename(wheel_names[0])

wheel_base, platform = wheel_names[0].rsplit("-", 1)
platform_base_parts = platform.split("_")
platform_base = "_".join(platform_base_parts[:3])

universal_wheel_path = (
dest_dir / f"{wheel_base.lower()}-{platform_base}_universal2.whl"
)
print("writing universal wheel", universal_wheel_path.name)
fuse_wheels(tmpdir / wheel_name1, tmpdir / wheel_name2, universal_wheel_path)


wheel_filename_pattern = re.compile(r"[^\s=]+.whl")


def main():
parser = argparse.ArgumentParser()
parser.add_argument("pip_log")
parser.add_argument("--wheels-dir", default="build/universal_wheels")

args = parser.parse_args()

wheels_dir = pathlib.Path(args.wheels_dir).resolve()
wheels_dir.mkdir(exist_ok=True, parents=True)

pip_log_path = args.pip_log
with open(pip_log_path) as f:
pip_log = f.read()

non_portable_wheels = {}

for wheel_filename in wheel_filename_pattern.findall(pip_log):
package, version, build, tags = parse_wheel_filename(wheel_filename)
package = package.lower()
if any("x86" in tag.platform or "arm64" in tag.platform for tag in tags):
assert non_portable_wheels.get(package, wheel_filename) == wheel_filename
non_portable_wheels[package] = wheel_filename

for wheel_filename in non_portable_wheels.values():
package, version, build, tags = parse_wheel_filename(wheel_filename)
response = urlopen(f"https://pypi.org/pypi/{package}/{version}/json")
data = json.load(response)

universal_wheels = []
platform_wheels = []

for file_descriptor in data["urls"]:
if file_descriptor["python_version"] not in python_versions:
continue
wheel_filename = file_descriptor["filename"]
package, version, build, tags = parse_wheel_filename(wheel_filename)
if any("macosx" in tag.platform for tag in tags):
if any("universal2" in tag.platform for tag in tags):
universal_wheels.append(file_descriptor["url"])
else:
platform_wheels.append(file_descriptor["url"])

if universal_wheels:
assert len(universal_wheels) == 1
download_file(universal_wheels[0], wheels_dir)
elif platform_wheels:
assert len(platform_wheels) == 2
merge_wheels(platform_wheels[0], platform_wheels[1], wheels_dir)
else:
raise IncompatibleWheelError(
f"No universal2 solution found for non-portable wheel {wheel_filename}"
)


if __name__ == "__main__":
main()
7 changes: 6 additions & 1 deletion Lib/fontgoggles/compile/ufoCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,19 @@ def __init__(self, ufoPath, reader, revCmap, anchors):
def keys(self):
return self._glyphNames

def __iter__(self):
for glyphName in self._glyphNames:
glyph = self[glyphName]
yield glyph

def __getitem__(self, glyphName):
if glyphName not in self._glyphNames:
raise KeyError(glyphName)
# TODO: should we even bother caching?
glyph = self._glyphs.get(glyphName)
if glyph is None:
glyph = MinimalGlyphObject(glyphName, self._revCmap.get(glyphName), self._anchors.get(glyphName, ()))
self._glyphs[glyphName] = glyphName
self._glyphs[glyphName] = glyph
return glyph


Expand Down
2 changes: 2 additions & 0 deletions Lib/fontgoggles/mac/aligningScrollView.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import objc
import AppKit
import vanilla
from objc import super

from ..misc.properties import hookedProperty


Expand Down
1 change: 1 addition & 0 deletions Lib/fontgoggles/mac/document.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pathlib
import AppKit
from objc import super

from ..project import Project
from .mainWindow import FGMainWindowController
Expand Down
2 changes: 2 additions & 0 deletions Lib/fontgoggles/mac/fontList.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from types import SimpleNamespace
import objc
import AppKit
from objc import super

from vanilla import Group, ProgressSpinner, TextBox, VanillaBaseObject
from jundo import UndoManager
from fontTools.misc.arrayTools import offsetRect, scaleRect, unionRect
Expand Down
8 changes: 8 additions & 0 deletions Lib/fontgoggles/mac/mainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def __init__(self, project):
self.updateFileObservers()
self.loadFonts(shouldRestoreSettings=True)

@objc.python_method
@suppressAndLogException
def _windowCloseCallback(self, sender):
obs = getFileObserver()
Expand Down Expand Up @@ -423,6 +424,7 @@ def addExternalFileObservers(self, externalFiles, fontItemInfo):
self.observedPaths[path].append(fontItemInfo)
obs.addObserver(path, self._fileChanged)

@objc.python_method
@suppressAndLogException
def _fileChanged(self, oldPath, newPath, wasModified):
# This gets called by the file observer, when a file changed on disk.
Expand Down Expand Up @@ -459,6 +461,7 @@ def _fileChanged(self, oldPath, newPath, wasModified):
if wasModified:
self.loadFonts()

@objc.python_method
@suppressAndLogException
def _projectFontsChanged(self, changeSet):
# This gets called by the undo manager, upon performing undo or redo.
Expand Down Expand Up @@ -606,6 +609,7 @@ def iterFontItems(self):
def iterFontItemInfoAndItems(self):
return self.fontList.iterFontItemInfoAndItems()

@objc.python_method
@asyncTaskAutoCancel
async def textEntryChangedCallback(self, sender, updateCharacterList=True):
if not hasattr(self, "directionPopUp"):
Expand Down Expand Up @@ -705,6 +709,7 @@ def blockCallbackRecursion(self):
yield
self._callbackRecursionLock -= 1

@objc.python_method
@asyncTaskAutoCancel
async def updateGlyphList(self, glyphs, delay=0):
if not hasattr(self, "fontList"):
Expand All @@ -726,6 +731,7 @@ async def updateGlyphList(self, glyphs, delay=0):
if fontItem is not None:
self.glyphList.setSelection(fontItem.selection)

@objc.python_method
@asyncTaskAutoCancel
async def updateCharacterList(self, selection=None, delay=0):
if not hasattr(self, "project"):
Expand Down Expand Up @@ -906,6 +912,7 @@ def directionPopUpCallback(self, sender):
self.textEntryChangedCallback(self.textEntry)
self.relativeBaselineSlider.set(self.getRelativeBaselineValueForSlider())

@objc.python_method
@suppressAndLogException
def alignmentChangedCallback(self, sender):
values = [alignmentValuesHorizontal,
Expand Down Expand Up @@ -1038,6 +1045,7 @@ def showFormattingOptions_(self, sender):
def showFontFileName_(self, sender):
self.fontList.showFontFileName = not self.fontList.showFontFileName

@objc.python_method
@suppressAndLogException
def validateMenuItem_(self, sender):
action = sender.action()
Expand Down
Loading

0 comments on commit 52bd5e6

Please sign in to comment.