Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ omit =
*/faust/assignor/*
*/faust/transport/drivers/memory.py

# optional driver: only importable/tested with the ckafka extra installed
# (faust[ckafka]); not part of the CI test environment.
# optional driver (faust[ckafka]). The CI matrix does now install the
# extra and run tests/unit/transport/drivers/test_confluent.py, but the
# driver stays out of the reported total: a contributor without
# confluent-kafka installed skips those tests, and counting the module
# would drop their local coverage by a couple of points for no reason.
*/faust/transport/drivers/confluent.py

# tested by integration
Expand Down
145 changes: 145 additions & 0 deletions .github/actions/setup-faust/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
name: Set up a faust environment
description: >
Provision an interpreter and install faust's dependencies with uv.

Every job in the CI/CD workflow needs the same three things -- an
interpreter, the dependency set, and (usually) faust itself -- and used to
spell all three out inline. That was eight near-identical copies of the
same twenty lines, which is how the pip cache key and the requirements list
drifted apart between jobs in the first place. Keeping it here means there
is exactly one place to fix when any of it changes.

The caller must run `actions/checkout` first: GitHub resolves a local
`uses: ./.github/actions/...` from the workspace, so this file does not
exist yet until the repository is on disk.

inputs:
python-version:
description: >
Interpreter to provision, spelled as `actions/setup-python` spells it
(`3.12`, `3.14t`, `pypy3.11`, ...).
required: true
requirements:
description: >
Requirement files to install, one per line. Passed to uv as `-r` in a
single resolution, so conflicting pins across files fail loudly here
rather than silently depending on install order.
required: false
default: requirements/test.txt
install:
description: >
How to install faust itself: `wheel` (a normal build+install),
`editable` (registers the distribution metadata against the source
tree, which the free-threading job needs), or `none`.
required: false
default: wheel
build-ext:
description: >
Build the Cython extensions in place after installing. See the step
itself for why the in-place copy is the one that matters.
required: false
default: 'false'

runs:
using: composite
steps:
- name: Set up Python ${{ inputs.python-version }}
id: python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
# Required by the 3.15 rows: a bare `3.15` matches stable releases
# only and fails with "Version 3.15 was not found in the local cache".
# Safe for every other row -- it widens `3.X` to `~3.X.0-0`, and a
# pre-release only wins when no stable release satisfies the spec, so
# 3.10-3.14 still resolve to their newest stable patch.
allow-prereleases: true

- name: Set up uv
# Pinned to an exact tag, not a `v10` major alias: setup-uv stopped
# publishing floating major tags after v7, so `@v8`/`@v9`/`@v10` do not
# resolve at all ("unable to find version `v10`"). Dependabot's
# github-actions updater bumps this pin like any other.
uses: astral-sh/setup-uv@v10.0.0
with:
enable-cache: true
# uv's default glob (`**/*requirements*.txt`) matches
# requirements/requirements.txt and nothing else here, because `*`
# does not cross a `/` -- test.txt, ci.txt and the extras/ files would
# all miss. Hash the same list every job installs from, so they share
# one cache entry per interpreter instead of each warming a partial
# one and none of them ever being invalidated by a moved pin.
cache-dependency-glob: |
requirements/*.txt
requirements/extras/*.txt

- name: Install dependencies
shell: bash
env:
# Install into the interpreter `setup-python` just provisioned, rather
# than into a uv-managed virtualenv the later steps would have to know
# to activate.
#
# UV_PYTHON names that interpreter by absolute path instead of letting
# uv search PATH. The search is not equivalent: uv's discovery skips
# free-threaded interpreters unless the request explicitly asks for a
# `t` ABI, so on the 3.13t/3.14t legs it walked past the interpreter we
# had just installed and settled on Debian's /usr/bin/python3 -- which
# then failed as "externally managed", and would have been the *wrong
# interpreter* even if it had succeeded. `python-path` is exactly what
# setup-python resolved, for every leg including pypy.
UV_SYSTEM_PYTHON: '1'
UV_PYTHON: ${{ steps.python.outputs.python-path }}
REQUIREMENTS: ${{ inputs.requirements }}
INSTALL: ${{ inputs.install }}
run: |
set -euo pipefail
# Carry the interpreter choice to any later step in the calling job
# that shells out to uv (the docs build does).
echo "UV_SYSTEM_PYTHON=1" >> "$GITHUB_ENV"
echo "UV_PYTHON=$UV_PYTHON" >> "$GITHUB_ENV"
args=()
while IFS= read -r req; do
[ -n "$req" ] || continue
args+=(-r "$req")
done <<< "$REQUIREMENTS"
uv pip install "${args[@]}"
case "$INSTALL" in
wheel)
uv pip install .
;;
editable)
# Editable, unlike `wheel`. pytest runs from the repository root,
# so `import faust` resolves to the source tree either way -- but
# the suite also needs the distribution *metadata* to exist,
# because `faust/__init__.py` calls `version("faust-streaming")`
# at import time. An editable install registers that metadata
# against the tree the tests actually import, instead of a second
# copy in site-packages that nothing loads.
USE_CYTHON=1 uv pip install -e . --no-build-isolation
;;
none)
;;
*)
echo "::error::unknown install mode '$INSTALL'"
exit 1
;;
esac

- name: Build the Cython extensions in place
# The install above compiles the extensions into site-packages, where
# the tests never see them: pytest runs from the repository root, so
# `import faust` resolves to the source tree, and every accelerated
# import sits behind `try: ... except ImportError`. The fallback
# engages silently, so without this the Cython legs would differ from
# the pure-Python ones only in whether a build step succeeded -- the
# compiled code itself would never be executed by a single test.
#
# Building in place puts the .so files next to the .pyx files, which is
# what the source-tree import actually picks up. It is nearly free:
# the objects were already compiled by the install above, so this only
# copies them out of build/lib.*/ into the tree.
if: inputs.build-ext == 'true'
shell: bash
run: USE_CYTHON=1 python setup.py build_ext --inplace
48 changes: 44 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
# Both ecosystems are grouped and weekly rather than ungrouped and daily.
#
# Every dependabot pull request costs a full CI run, and daily ungrouped
# updates meant a steady stream of one-line bumps each paying for the entire
# matrix -- repeatedly the largest single consumer of CI minutes in the
# repository, for changes that are almost always reviewed and merged as a
# batch anyway. Grouping collapses a week's bumps into one pull request per
# ecosystem, which is one CI run instead of a dozen, and still surfaces
# exactly the same version changes.
#
# Security advisories are not affected by either setting: dependabot opens
# those immediately and ungrouped regardless of the schedule here.
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
groups:
github-actions:
patterns:
- "*"

# Maintain dependencies for pip
- package-ecosystem: "pip"
directory: "/requirements"
schedule:
interval: "daily"
interval: "weekly"
groups:
# Split so a failing lint-tool bump (black and isort are pinned exactly,
# and a new release reformats the tree) cannot hold back the runtime and
# test dependency updates in the same pull request.
lint-tools:
patterns:
- "black"
- "isort"
- "flake8*"
- "mypy"
- "autoflake"
- "pydocstyle"
- "bandit"
- "pre-commit"
dependencies:
patterns:
- "*"
exclude-patterns:
- "black"
- "isort"
- "flake8*"
- "mypy"
- "autoflake"
- "pydocstyle"
- "bandit"
- "pre-commit"
62 changes: 22 additions & 40 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
---
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
# CodeQL security analysis.
#
# `languages: python` is the whole of the build configuration: faust's C
# extensions are generated Cython, which CodeQL does not analyse, so there is
# nothing for the autobuild step to do and it is not run.
name: CodeQL
on:
push:
branches: [master]
# Only re-analyse a pull request when it actually changes Python. A CodeQL
# run on a docs- or workflow-only branch re-derives the identical database
# for two minutes and reports the identical alerts; the scheduled and
# master-push runs below keep the security dashboard current regardless.
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
paths:
- '**/*.py'
- .github/workflows/codeql-analysis.yml
schedule:
- cron: 19 10 * * 6
# Supersede a pull request's analysis when it gets a new push; scheduled and
Expand All @@ -25,52 +25,34 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
analyze:
name: Analyze
# Spelled out rather than derived from a one-entry `language` matrix, which
# is all that matrix ever did. The rendered check name is unchanged, so
# any branch protection referring to it keeps matching.
name: Analyze (python)
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [python]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
languages: python
# Cache the dependencies the Python extractor installs to resolve
# imports, instead of leaving it to a server-side feature flag.
dependency-caching: true
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# Kept for Python: autobuild is what installs the project's dependencies
# so the extractor can resolve imports, and `dependency-caching` above
# caches exactly that work. Dropping it would leave unresolved imports
# and quietly weaken the analysis.
- name: Autobuild
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Loading
Loading