Skip to content

Commit 08406a9

Browse files
authored
Merge pull request #3 from btucker/feature/add-wheel
fix: include libfoundation_models.dylib in wheels and build for multiple python versions
2 parents bfe9d3d + 5b41a69 commit 08406a9

File tree

6 files changed

+184
-173
lines changed

6 files changed

+184
-173
lines changed

.github/workflows/publish-to-pypi.yml

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,98 @@ permissions:
1010
id-token: write # Required for trusted publishing to PyPI
1111

1212
jobs:
13-
build:
14-
name: Build package on macOS
13+
build-wheels:
14+
name: Build wheels on macOS for Python ${{ matrix.python-version }}
1515
runs-on: macos-26
16+
strategy:
17+
matrix:
18+
# Build wheels for all supported Python versions
19+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
20+
fail-fast: false
1621

1722
steps:
1823
- name: Checkout code
1924
uses: actions/checkout@v4
2025

21-
- name: Set up Python
26+
- name: Set up Python ${{ matrix.python-version }}
2227
uses: actions/setup-python@v5
2328
with:
24-
python-version: "3.11"
29+
python-version: ${{ matrix.python-version }}
2530

2631
- name: Check macOS version
2732
run: |
2833
echo "macOS version:"
2934
sw_vers
3035
echo "Architecture:"
3136
uname -m
37+
echo "Python version:"
38+
python --version
3239
33-
- name: Install build dependencies
34-
run: |
35-
python -m pip install --upgrade pip
36-
pip install build wheel setuptools Cython>=3.0.0
40+
- name: Install uv
41+
uses: astral-sh/setup-uv@v4
42+
with:
43+
enable-cache: true
3744

38-
- name: Build package
45+
- name: Build wheel
46+
env:
47+
MACOSX_DEPLOYMENT_TARGET: "26.0"
48+
ARCHFLAGS: "-arch arm64"
49+
_PYTHON_HOST_PLATFORM: "macosx-26.0-arm64"
3950
run: |
40-
python -m build
51+
uv build --wheel
4152
42-
- name: Check build artifacts
53+
- name: Verify wheel contents
4354
run: |
4455
echo "Build artifacts:"
4556
ls -lh dist/
4657
echo ""
47-
echo "Verifying wheel contents:"
48-
unzip -l dist/*.whl || true
58+
echo "Checking for dylib in wheel:"
59+
if unzip -l dist/*.whl | grep -q "libfoundation_models.dylib"; then
60+
echo "✓ libfoundation_models.dylib found in wheel"
61+
unzip -l dist/*.whl | grep -E "(dylib|\.so)"
62+
else
63+
echo "✗ ERROR: libfoundation_models.dylib NOT found in wheel!"
64+
echo "Full wheel contents:"
65+
unzip -l dist/*.whl
66+
exit 1
67+
fi
4968
50-
- name: Upload build artifacts
69+
- name: Upload wheel
5170
uses: actions/upload-artifact@v4
5271
with:
53-
name: python-package-distributions
54-
path: dist/
72+
name: wheel-${{ matrix.python-version }}
73+
path: dist/*.whl
5574

5675
publish:
5776
name: Publish to PyPI
58-
needs: build
77+
needs: [build-wheels]
5978
runs-on: ubuntu-latest
6079

6180
steps:
62-
- name: Download build artifacts
81+
- name: Download all artifacts
6382
uses: actions/download-artifact@v4
6483
with:
65-
name: python-package-distributions
6684
path: dist/
85+
merge-multiple: true
6786

6887
- name: Verify artifacts
6988
run: |
7089
echo "Downloaded artifacts:"
7190
ls -lh dist/
91+
echo ""
92+
echo "Verifying each wheel contains dylib:"
93+
for wheel in dist/*.whl; do
94+
echo "Checking $wheel:"
95+
ls -lh "$wheel"
96+
if unzip -l "$wheel" | grep -q "libfoundation_models.dylib"; then
97+
echo " ✓ dylib present"
98+
else
99+
echo " ✗ ERROR: dylib missing in $wheel!"
100+
echo "Wheel contents:"
101+
unzip -l "$wheel"
102+
exit 1
103+
fi
104+
done
72105
73106
- name: Publish to PyPI
74107
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/test.yml

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
python-version: ["3.10", "3.11", "3.12"]
17+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
1818
fail-fast: false
1919

2020
steps:
@@ -38,13 +38,20 @@ jobs:
3838
echo "Architecture:"
3939
uname -m
4040
41-
- name: Install dependencies and build extensions
41+
- name: Build wheel
4242
run: |
43-
uv sync --extra dev
43+
uv build --wheel
44+
45+
- name: Install wheel and test dependencies
46+
run: |
47+
uv venv .test-venv
48+
source .test-venv/bin/activate
49+
uv pip install dist/*.whl pytest pytest-asyncio pytest-cov
4450
4551
- name: Check Apple Intelligence availability
4652
run: |
47-
uv run python -c "
53+
source .test-venv/bin/activate
54+
python -c "
4855
import applefoundationmodels as afm
4956
status = afm.Client.check_availability()
5057
reason = afm.Client.get_availability_reason()
@@ -54,15 +61,19 @@ jobs:
5461
"
5562
continue-on-error: true
5663

57-
- name: Run tests
64+
- name: Run tests from installed wheel
5865
run: |
59-
# Tests will auto-skip if Apple Intelligence is unavailable
60-
uv run pytest tests/ -v --tb=short -ra
66+
source .test-venv/bin/activate
67+
cd /tmp
68+
pytest ${{ github.workspace }}/tests/ -v --tb=short -ra
6169
6270
- name: Run tests with coverage (Python 3.11 only)
6371
if: matrix.python-version == '3.11'
6472
run: |
65-
uv run pytest tests/ --cov=applefoundationmodels --cov-report=xml --cov-report=term
73+
source .test-venv/bin/activate
74+
cd /tmp
75+
pytest ${{ github.workspace }}/tests/ --cov=applefoundationmodels --cov-report=xml --cov-report=term
76+
cp coverage.xml ${{ github.workspace }}/
6677
6778
- name: Upload coverage reports
6879
if: matrix.python-version == '3.11'

MANIFEST.in

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
# Include all necessary files in source distribution
2-
3-
# Swift source files
4-
include applefoundationmodels/swift/*.swift
5-
include applefoundationmodels/swift/*.h
6-
7-
# Dynamic library
8-
include lib/*.dylib
9-
include lib/*.swiftmodule
10-
11-
# Cython source files
12-
include applefoundationmodels/*.pyx
13-
include applefoundationmodels/*.pxd
14-
15-
# Type stub marker
16-
include applefoundationmodels/py.typed
1+
# This file is for sdist builds only (not used for wheels)
2+
# Wheels use pyproject.toml [tool.setuptools.package-data] instead
173

184
# Documentation
195
include README.md
206
include LICENSE
217

22-
# Exclude unnecessary files
8+
# Exclude build artifacts and source files from wheels
239
global-exclude *.pyc
2410
global-exclude __pycache__
2511
global-exclude *.so
2612
global-exclude .DS_Store
2713
global-exclude *.a
14+
global-exclude *.swift
15+
global-exclude *.h
16+
global-exclude lib/*

README.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Python bindings for Apple's FoundationModels framework - Direct access to on-dev
1414
## Requirements
1515

1616
- macOS 26.0+ (macOS Sequoia or later)
17-
- Python 3.8 or higher
17+
- Python 3.9 or higher
1818
- Apple Intelligence enabled on your device
1919

2020
## Installation
@@ -47,7 +47,7 @@ pip install -e .
4747

4848
- macOS 26.0+ (Sequoia) with Apple Intelligence enabled
4949
- Xcode command line tools (`xcode-select --install`)
50-
- Python 3.8 or higher
50+
- Python 3.9 or higher
5151

5252
**Note:** The Swift dylib is built automatically during installation.
5353

@@ -320,17 +320,34 @@ See the `examples/` directory for complete working examples:
320320

321321
### Building from Source
322322

323+
This project uses [uv](https://docs.astral.sh/uv/) for fast, reliable builds and dependency management:
324+
323325
```bash
326+
# Install uv (if not already installed)
327+
curl -LsSf https://astral.sh/uv/install.sh | sh
328+
324329
# Install development dependencies
325-
pip install -e ".[dev]"
330+
uv sync --extra dev
326331

327332
# Run tests
328-
pytest
333+
uv run pytest
329334

330335
# Type checking
331-
mypy applefoundationmodels
336+
uv run mypy applefoundationmodels
332337

333338
# Format code
339+
uv run black applefoundationmodels examples
340+
341+
# Build wheels
342+
uv build --wheel
343+
```
344+
345+
You can also use pip if preferred:
346+
347+
```bash
348+
pip install -e ".[dev]"
349+
pytest
350+
mypy applefoundationmodels
334351
black applefoundationmodels examples
335352
```
336353

pyproject.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "apple-foundation-models"
7-
version = "0.1.3"
8-
description = "Python bindings for Apple's FoundationModels framework - on-device AI"
7+
version = "0.1.4"
8+
description = "Python bindings for Apple's FoundationModels framework - on-device AI (requires macOS 26.0+)"
99
readme = "README.md"
1010
license = { text = "MIT" }
1111
authors = [{ name = "Ben Tucker" }]
@@ -24,16 +24,17 @@ classifiers = [
2424
"License :: OSI Approved :: MIT License",
2525
"Operating System :: MacOS :: MacOS X",
2626
"Programming Language :: Python :: 3",
27-
"Programming Language :: Python :: 3.8",
2827
"Programming Language :: Python :: 3.9",
2928
"Programming Language :: Python :: 3.10",
3029
"Programming Language :: Python :: 3.11",
3130
"Programming Language :: Python :: 3.12",
31+
"Programming Language :: Python :: 3.13",
32+
"Programming Language :: Python :: 3.14",
3233
"Programming Language :: Cython",
3334
"Topic :: Scientific/Engineering :: Artificial Intelligence",
3435
"Topic :: Software Development :: Libraries :: Python Modules",
3536
]
36-
requires-python = ">=3.8"
37+
requires-python = ">=3.9"
3738
dependencies = ["typing-extensions>=4.0.0"]
3839

3940
[project.optional-dependencies]
@@ -48,10 +49,14 @@ Issues = "https://github.com/btucker/apple-foundation-models-py/issues"
4849

4950
[tool.setuptools]
5051
packages = ["applefoundationmodels"]
51-
include-package-data = true
5252

5353
[tool.setuptools.package-data]
54-
applefoundationmodels = ["py.typed", "*.pxd"]
54+
applefoundationmodels = [
55+
"py.typed",
56+
"*.pxd",
57+
"*.pyx",
58+
"*.dylib",
59+
]
5560

5661
[tool.pytest.ini_options]
5762
testpaths = ["tests"]

0 commit comments

Comments
 (0)