Skip to content

Commit

Permalink
chore: build binary wheels using cibuildwheel (#2267)
Browse files Browse the repository at this point in the history
* test(conftest.py): fix module paths in load_module()

* chore(tests): tweak some tests to afford running pytest from any directory

* chore: just set up Actions job to test artefacts

* chore(mintest): reorganize mintest to include ASGI, skip slow tests

* chore: actually prototype building a couple of wheels

* chore(CIBW): roll back to upload-artifact@v3 for now

* chore(cibw): just test the matrix of all platform combos in a dry-run

* chore: fix a YAML PEBCAK

* chore(cibw): actually try to build all wheels for 3.13

* chore(cibw): choose all Linux archs, we control via `CIBW_BUILD` anyway

* chore: test building all Python versions on Windows

* test(static): attempt to fix static route tests on Windows+py313

* test(static): attempt to fix tests on Windows+py313, take #2

* chore: fix 1 more PEBCAK

* test(static): adjust tests even more to pass on Windows...

* test(static): attempt normalizing even more on M$ Windows...

* chore: more debuggin'

* chore(cibw): try building the whole wheel matrix + add dispatch

* chore: fix a workflow pebcak

* chore(cibw): fix another pebcak

* chore(cibw): only build cp38 on x86 Linux

* chore: remove cibuildwheel from running on PR

* test(mintest): make all 3rd party JSON libraries optional

* chore(tests): make msgpack & yaml optional (but include in cibw tests)

* chore(cibw): address review comments
  • Loading branch information
vytas7 authored Aug 16, 2024
1 parent c124e3a commit 9a46499
Show file tree
Hide file tree
Showing 30 changed files with 375 additions and 303 deletions.
141 changes: 141 additions & 0 deletions .github/workflows/cibuildwheel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Build wheels using cibuildwheel (https://cibuildwheel.pypa.io/)
name: Build wheels

on:
# Run when a release has been created
release:
types: [created]

# NOTE(vytas): Also allow to release to Test PyPi manually.
workflow_dispatch:

jobs:
build-sdist:
name: sdist
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Build sdist
run: |
pip install build
python -m build --sdist
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz

build-wheels:
name: ${{ matrix.python }}-${{ matrix.platform.name }}
needs: build-sdist
runs-on: ${{ matrix.platform.os }}
strategy:
fail-fast: false
matrix:
platform:
- name: manylinux_x86_64
os: ubuntu-latest
- name: musllinux_x86_64
os: ubuntu-latest
- name: manylinux_aarch64
os: ubuntu-latest
emulation: true
- name: musllinux_aarch64
os: ubuntu-latest
emulation: true
- name: manylinux_s390x
os: ubuntu-latest
emulation: true
- name: macosx_x86_64
os: macos-13
- name: macosx_arm64
os: macos-14
- name: win_amd64
os: windows-latest
python:
- cp39
- cp310
- cp311
- cp312
- cp313
include:
- platform:
name: manylinux_x86_64
os: ubuntu-latest
python: cp38
- platform:
name: musllinux_x86_64
os: ubuntu-latest
python: cp38

defaults:
run:
shell: bash

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
if: ${{ matrix.platform.emulation }}
with:
platforms: all

- name: Build wheels
uses: pypa/cibuildwheel@v2.20.0
env:
CIBW_ARCHS_LINUX: all
CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform.name }}

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: cibw-wheel-${{ matrix.python }}-${{ matrix.platform.name }}
path: wheelhouse/*.whl

publish-wheels:
name: publish
needs:
- build-sdist
- build-wheels
runs-on: ubuntu-latest

steps:
- name: Download packages
uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true

- name: Check collected artifacts
# TODO(vytas): Run a script to perform version sanity checks instead.
run: ls -l dist/

- name: Publish artifacts to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: github.event_name == 'workflow_dispatch'
with:
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository-url: https://test.pypi.org/legacy/

# TODO(vytas): Enable this nuclear option once happy with other tests.
# - name: Publish artifacts to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# if: github.event_name == 'release'
# with:
# password: ${{ secrets.PYPI_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ parts
sdist
var
pip-wheel-metadata
wheelhouse

# Installer logs
pip-log.txt
Expand Down
2 changes: 0 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# Falcon documentation build configuration file, created by
# sphinx-quickstart on Wed Mar 12 14:14:02 2014.
#
Expand Down
10 changes: 9 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
requires = [
"setuptools>=47",
"wheel>=0.34",
"cython>=0.29.21; python_implementation == 'CPython'", # Skip cython when using pypy
"cython>=3.0.8; python_implementation == 'CPython'", # Skip cython when using pypy
]

[tool.mypy]
Expand Down Expand Up @@ -168,6 +168,14 @@ filterwarnings = [
"ignore:path is deprecated\\. Use files\\(\\) instead:DeprecationWarning",
"ignore:This process \\(.+\\) is multi-threaded",
]
markers = [
"slow: mark Falcon tests as slower (potentially taking more than ~500ms).",
]
testpaths = [
"tests"
]

[tool.cibuildwheel]
build-frontend = "build"
test-requires = ["-r requirements/cibwtest"]
test-command = "pytest {project}/tests"
5 changes: 5 additions & 0 deletions requirements/cibwtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
msgpack
pytest
pytest-asyncio<0.22.0
pyyaml
requests
5 changes: 1 addition & 4 deletions requirements/mintest
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
coverage>=4.1
msgpack
mujson
pytest
pyyaml
pytest-asyncio<0.22.0
requests
ujson
19 changes: 16 additions & 3 deletions tests/asgi/test_asgi_servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@
import sys
import time

import httpx
import pytest
import requests
import requests.exceptions
import websockets
import websockets.exceptions

try:
import httpx
except ImportError:
httpx = None # type: ignore

try:
import websockets
import websockets.exceptions
except ImportError:
websockets = None # type: ignore


from falcon import testing

Expand Down Expand Up @@ -166,6 +175,7 @@ def test_sse_client_disconnects_early(self, server_base_url):
)

@pytest.mark.asyncio
@pytest.mark.skipif(httpx is None, reason='httpx is required for this test')
async def test_stream_chunked_request(self, server_base_url):
"""Regression test for https://github.com/falconry/falcon/issues/2024"""

Expand All @@ -183,6 +193,9 @@ async def emitter():
assert resp.json().get('drops') >= 1


@pytest.mark.skipif(
websockets is None, reason='websockets is required for this test class'
)
class TestWebSocket:
@pytest.mark.asyncio
@pytest.mark.parametrize('explicit_close', [True, False])
Expand Down
1 change: 1 addition & 0 deletions tests/asgi/test_boundedstream_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
)
@pytest.mark.parametrize('extra_body', [True, False])
@pytest.mark.parametrize('set_content_length', [True, False])
@pytest.mark.slow
def test_read_all(body, extra_body, set_content_length):
if extra_body and not set_content_length:
pytest.skip(
Expand Down
3 changes: 3 additions & 0 deletions tests/asgi/test_buffered_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ async def test_read(reader1, sizes, expected):


@pytest.mark.parametrize('start_size', [1, 16777216])
@pytest.mark.slow
@falcon.runs_sync
async def test_varying_read_size(reader2, start_size):
size = start_size
Expand Down Expand Up @@ -318,6 +319,7 @@ async def test_invalid_delimiter_length(reader1):
(13372477, 51637898),
],
)
@pytest.mark.slow
@falcon.runs_sync
async def test_irregular_large_read_until(reader2, size1, size2):
delimiter = b'--boundary1234567890--'
Expand Down Expand Up @@ -376,6 +378,7 @@ async def test_small_reads(reader3):
assert last.endswith(b'4')


@pytest.mark.slow
@falcon.runs_sync
async def test_small_reads_with_delimiter(reader3):
ops = 0
Expand Down
Loading

0 comments on commit 9a46499

Please sign in to comment.