From 52f44443b0e72a898c229e5c4e191f97b05977f8 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 17 Jul 2026 12:57:49 -0400 Subject: [PATCH 01/17] docs(deps): establish and apply a managing-dependencies convention (#1551) Co-authored-by: Claude --- .claude/skills/managing-dependencies/SKILL.md | 127 ++++++++++++++++++ AGENTS.md | 2 + requirements/adapter_dev.txt | 95 +++++++++---- requirements/async_dev.txt | 9 +- requirements/dev_tools.txt | 7 + requirements/test.txt | 7 +- requirements/test_adapter.txt | 12 +- requirements/test_async.txt | 11 +- 8 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 .claude/skills/managing-dependencies/SKILL.md diff --git a/.claude/skills/managing-dependencies/SKILL.md b/.claude/skills/managing-dependencies/SKILL.md new file mode 100644 index 000000000..8f67fccd8 --- /dev/null +++ b/.claude/skills/managing-dependencies/SKILL.md @@ -0,0 +1,127 @@ +--- +name: managing-dependencies +description: >- + Use when adding, updating, pinning, or reviewing any dependency in this repo's requirements/*.txt files, including taking a Dependabot bump or writing a requirement line by hand. Also use when a pip install fails on the older Python jobs while newer ones pass, or when CI errors with "Could not find a version that satisfies", "No matching distribution found", "Requires-Python >=3.x", or "ResolutionImpossible". Triggers include: "add to requirements", "bump/update ", "pin ", "fix this dependabot PR", "CI can't install on Python 3.7/3.8/3.9", "requires-python error in the install step". This skill defines the layout and version-constraint conventions to follow; reach for it before hand-editing any requirements/*.txt file. +--- + +# Managing dependencies + +## Overview + +Every `requirements/*.txt` file in this repo follows one layout convention and one version-constraint pattern. This keeps a single set of pins working across the whole Python matrix and every diff readable. The mechanism for making one line behave differently per interpreter is [PEP 508](https://peps.python.org/pep-0508/) environment markers. That spec is the authority for the marker syntax used throughout this skill. + +Two facts about this repo drive everything below: + +- It supports **Python `>=3.7`** (`requires-python` and the classifiers in `pyproject.toml`). Its CI matrix in `.github/workflows/ci-build.yml` tests **CPython 3.7 through 3.14 only** — there is **no PyPy** (the classifiers list only `Programming Language :: Python :: Implementation :: CPython`). +- Many popular packages keep raising their minimum Python. A bump that raises a dependency's **lower bound** to a release requiring a newer Python makes pip's resolver find nothing installable on the old interpreters. The install step then fails there before tests even run. + +The convention resolves this without dropping old-Python support: pin each interpreter to the newest release it can actually install, using PEP 508 `python_version` markers. + +## File-layout convention + +Each file starts with a `# pip install -r requirements/.txt` header, then lists **one dependency per section**: the name of the dependency (as a `# name` header), an optional rationale note (starting with `# Note:`, explaining why a version is pinned or split), then the requirement line(s), separated from the next section by a blank line. This makes every pin self-documenting. + +``` +# pip install -r requirements/test.txt + +# pytest +pytest<9.2 + +# pytest-cov +# Note: only needed to evaluate coverage on the latest supported python version +pytest-cov>=7.1.0,<8; python_version >= "3.14" +``` + +Keep this layout when adding or editing dependencies. Never leave an empty trailing `;` (a fossil of a collapsed split; delete it — the old `pytest-asyncio<2;` line was exactly this). + +## Which files need Python-version markers + +A marker split is only needed for requirements files installed across the **full** Python matrix. Which file you are editing decides this. To see where a file is installed, read `.github/workflows/ci-build.yml`. It is the source of truth for which Python versions install which requirements files. Everything except `dev_tools.txt` is installed by the `unittest` matrix job across 3.7–3.14. + +| File | Installed on | Needs markers? | +| ----------------------------- | ----------------------------------------------------- | ----------------------------------- | +| `requirements/adapter_dev.txt` | full matrix (`unittest`; also `typecheck`/`codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/async_dev.txt` | full matrix (`unittest`; also `typecheck`/`codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/test.txt` | full matrix (`unittest`) | **Yes, if a bump raises the floor** | +| `requirements/test_adapter.txt` | full matrix (`unittest`; `codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/test_async.txt` | full matrix (`unittest`) | **Yes, if a bump raises the floor** | +| `requirements/dev_tools.txt` | `lint` + `typecheck`, **latest Python only** (3.14) | No, just take the bump (`==` pins) | + +If the bump lands in a latest-Python-only file, take it as-is: no markers, no ceiling, just the layout convention above. + +Unlike some sibling repos, bolt-python has **no requirements file that feeds packaged wheel metadata**: `pyproject.toml` has no `[project.optional-dependencies]`, and `[tool.setuptools.dynamic]` resolves only `version` and `readme`. So there is no "special" file whose comments leak into a wheel — every `requirements/*.txt` file is dev/test-only. + +## The version-constraint pattern + +When a dependency's floor rises to a release that requires a newer Python, **do not** just take the bump, and **do not** drop old-Python support to make CI pass. Instead, split the requirement into `python_version`-marked lines that **partition the whole matrix**. Every interpreter matches exactly one line. Old interpreters keep the last compatible release (with an explicit ceiling), and the newest line is open-ended so future Pythons stay covered. + +``` +# aiohttp +aiohttp>=3,<4; python_version < "3.9" +aiohttp>=3.13.5,<4; python_version >= "3.9" +``` + +`aiohttp` 3.13.5 requires Python `>=3.9`, so Python 3.7/3.8 stay on the older line. The same 3.9 split appears for `falcon`, `fastapi`, `Flask`, `Werkzeug`, `starlette`, `tornado`, `websocket_client`, and (in `test_async.txt`) `asgiref`. The `Django` split lands at 3.8 instead — Django 4.x requires `>=3.8`, so 3.7 keeps the 3.2 line: + +``` +# Django +Django>=3.2,<4; python_version < "3.8" +Django>=4.2.30,<6; python_version >= "3.8" +``` + +## Canonical marker style + +Consistency matters because these lines are read and edited often, and a stray style makes diffs noisy. Standardize on this: + +- Spaces around every operator in the marker: `python_version >= "3.9"`, never `python_version>="3.9"`. +- Double-quoted `major.minor` string: `"3.9"`. (`packaging` compares these version-aware, so `python_version >= "3.9"` correctly includes 3.10–3.14, no lexicographic surprise.) +- Use `>=` / `<` for the Python boundary; avoid `>` / `<=` so the boundary version lands on exactly one side. **This rule is about the `python_version` marker, not the version specifier** — `boto3<=2` and `cheroot<12` are correct as written. +- One space after the `;`, none before: `pkg>=1,<2; python_version >= "3.9"`. +- The old-side line always carries an explicit upper bound (the floor-jump version). +- The marker set must be **exhaustive and mutually exclusive** across the matrix. The newest line ends open-ended (`>= "X.Y"`), never a bare `==` that leaves future Pythons unmatched. + +## Deriving the versions to pin + +You need two numbers: the **floor** (which Python the new release requires) and the old-side **ceiling** (the first release that raised that floor). + +1. **Floor.** Read the metadata for the _exact target version_ at `https://pypi.org/pypi///json`. The `info.requires_python` field gives the new minimum (e.g. `">=3.9"`). A `null` there means the release declares no floor. + +2. **Ceiling.** Walk the release history at `https://pypi.org/pypi//json` and find the **first version that raised the floor above the oldest matrix Python**. The old-side ceiling is `< `. For example, if a package jumped to `>=3.9` at version **4.0.0**, the old-side cap is `<4` even if the target is `4.2.0` (pinning `<4.2.0` would wrongly admit 4.0.0–4.1.x, which are also 3.9-only). + +Why an explicit ceiling instead of trusting pip to filter by `Requires-Python`? Because that filtering only holds if every future release keeps its metadata correct; a single mis-tagged release would silently float onto an untested interpreter. An explicit ceiling makes the intent self-documenting and robust. + +`tracerite` is the cautionary case: its releases after 1.1.2 break on Python `<= 3.8`, yet those releases publish **no `requires_python` metadata at all** (verify: `https://pypi.org/pypi/tracerite/1.1.3/json` shows `requires_python: null`). So pip filtering offers zero protection on the old interpreters, and the explicit `tracerite<1.1.2` ceiling is load-bearing, not decorative. + +**If you arrived here from a red CI job:** the failing _install_ log is ground truth. It names the interpreter that failed and the versions pip was actually offered, e.g.: + +``` +ERROR: Ignored the following versions that require a different python version: 4.2.0 Requires-Python >=3.9 +ERROR: Could not find a version that satisfies the requirement falcon>=4.2.0 (from versions: ..., 3.1.3) +``` + +Cross-check the PyPI value against that log so you are never guessing. (If instead the failure is a real test failure, or hits _every_ Python version, this pattern does not apply, so investigate the bump normally.) + +## One harder shape: a coupled companion dependency + +Sometimes a package drags in another distribution with loose or wildcard versions that you must co-pin on the same boundary. `Sanic` imports `tracerite` with wildcard versions, so `tracerite` is pinned right beside it: + +``` +# sanic +# Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. +# Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +tracerite<1.1.2; python_version < "3.9" +sanic>=21,<24; python_version < "3.9" +sanic>=25.3.0,<26; python_version >= "3.9" +``` + +The split boundary here (3.9) is driven by `tracerite`, **not** by Sanic itself — Sanic 25.3 supports Python 3.8 (`requires_python: >=3.8`). Python 3.8 is kept on old Sanic only because tracerite breaks there. When a companion transitive dependency is the thing that breaks, pin it explicitly rather than hoping the parent's resolver picks a compatible version, and keep it in the same section so the coupling stays visible. + +## Collapse when a Python is dropped + +Marker splits are maintenance cost, so remove them when they stop earning their keep. When a Python version is dropped from the CI matrix (and from `requires-python` / the classifiers), collapse any split whose only reason was that version back into a single unmarked line, and delete the trailing `;`. For example, if 3.7 is dropped, the `Django>=3.2,<4; python_version < "3.8"` line has no interpreter left to serve, and the section collapses to a single `Django` line. A leaner file is easier for both humans and Dependabot to reason about. + +## What to leave alone + +- **Do not touch `requires-python` or the CI matrix.** Keeping 3.7 working on old dependency versions is the entire point; changing the floor is a separate, deliberate decision. +- **Do not add runtime dependencies to `pyproject.toml`.** The core package depends only on `slack_sdk` (see the "Single Runtime Dependency Rule" in `AGENTS.md`); everything else belongs in `requirements/*.txt`. +- **Prefer markers over a Dependabot `ignore`.** An `ignore` rule freezes newer Pythons on the old version too, and hides the version knowledge in config. Reserve `ignore` for the rare dep that must stay pinned everywhere for reproducible output. diff --git a/AGENTS.md b/AGENTS.md index 005f6eeda..eae86f330 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -214,6 +214,8 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare). +Before adding, bumping, pinning, or reviewing any dependency in `requirements/*.txt` -- whether a Dependabot PR or a manual edit -- follow the `managing-dependencies` skill in `.claude/skills/`. It defines the layout and `python_version` marker conventions that keep one set of pins working across the full CPython 3.7--3.14 matrix. + ## Test Organization and CI ### Directory Structure diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index d118106b9..ac70e1fdd 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -1,34 +1,73 @@ # pip install -r requirements/adapter_dev.txt -# NOTE: any of async ones requires pip install -r requirements/async_dev.txt too -# used only under slack_bolt/adapter +# Note: any of the async ones requires pip install -r requirements/async_dev.txt too +# Note: used only under slack_bolt/adapter + +# boto3 boto3<=2 + +# bottle bottle>=0.12,<1 -chalice>=1.28,<1.31; python_version<"3.9" -chalice>=1.32.0,<2; python_version>="3.9" + +# chalice +chalice>=1.28,<1.31; python_version < "3.9" +chalice>=1.32.0,<2; python_version >= "3.9" + +# cheroot cheroot<12 + +# CherryPy CherryPy>=18.10.0,<19 -Django>=3.2,<4; python_version<"3.8" -Django>=4.2.30,<6; python_version>="3.8" -falcon>=2,<4; python_version<"3.9" -falcon>=4.2.0,<5; python_version>="3.9" -fastapi>=0.70.0,<1; python_version<"3.9" -fastapi>=0.128.8,<1; python_version>="3.9" -Flask>=1,<4; python_version<"3.9" -Flask>=3.1.3,<4; python_version>="3.9" -Werkzeug>=2,<3; python_version<"3.9" -Werkzeug>=3.1.8,<4; python_version>="3.9" + +# Django +# Note: Django 4.2.30 requires Python >=3.8; 3.7 stays on the 3.2 line. +Django>=3.2,<4; python_version < "3.8" +Django>=4.2.30,<6; python_version >= "3.8" + +# falcon +# Note: falcon 4.2.0 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +falcon>=2,<4; python_version < "3.9" +falcon>=4.2.0,<5; python_version >= "3.9" + +# fastapi +# Note: fastapi 0.128.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +fastapi>=0.70.0,<1; python_version < "3.9" +fastapi>=0.128.8,<1; python_version >= "3.9" + +# Flask +# Note: Flask 3.1.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +Flask>=1,<4; python_version < "3.9" +Flask>=3.1.3,<4; python_version >= "3.9" + +# Werkzeug +# Note: Werkzeug 3.1.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +Werkzeug>=2,<3; python_version < "3.9" +Werkzeug>=3.1.8,<4; python_version >= "3.9" + +# pyramid pyramid>=1,<3 -setuptools<82 # Pinned: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 - -# Sanic and its dependencies -# Note: Sanic imports tracerite with wild card versions -tracerite<1.1.2; python_version<="3.8" # older versions of python are not compatible with tracerite>1.1.2 -sanic>=21,<24; python_version<="3.8" -sanic>=25.3.0,<26; python_version>"3.8" - -starlette>=0.19.1,<0.45; python_version<"3.9" -starlette>=0.49.3,<1; python_version>="3.9" -tornado>=6.2,<7; python_version<"3.9" -tornado>=6.5.6,<7; python_version>="3.9" -websocket_client>=1.2.3,<1.9; python_version<"3.9" # Socket Mode 3rd party implementation -websocket_client>=1.9.0,<2; python_version>="3.9" # Socket Mode 3rd party implementation + +# setuptools +# Note: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 +setuptools<82 + +# sanic +# Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. +# Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +tracerite<1.1.2; python_version < "3.9" +sanic>=21,<24; python_version < "3.9" +sanic>=25.3.0,<26; python_version >= "3.9" + +# starlette +# Note: starlette 0.49.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +starlette>=0.19.1,<0.45; python_version < "3.9" +starlette>=0.49.3,<1; python_version >= "3.9" + +# tornado +# Note: tornado 6.5.6 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +tornado>=6.2,<7; python_version < "3.9" +tornado>=6.5.6,<7; python_version >= "3.9" + +# websocket_client +# Note: Socket Mode 3rd party implementation +websocket_client>=1.2.3,<1.9; python_version < "3.9" +websocket_client>=1.9.0,<2; python_version >= "3.9" diff --git a/requirements/async_dev.txt b/requirements/async_dev.txt index a40433441..32dff305b 100644 --- a/requirements/async_dev.txt +++ b/requirements/async_dev.txt @@ -1,4 +1,9 @@ # pip install -r requirements/async_dev.txt -aiohttp>=3,<4; python_version<"3.9" -aiohttp>=3.13.5,<4; python_version>="3.9" + +# aiohttp +# Note: aiohttp 3.13.5 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +aiohttp>=3,<4; python_version < "3.9" +aiohttp>=3.13.5,<4; python_version >= "3.9" + +# websockets websockets<17 diff --git a/requirements/dev_tools.txt b/requirements/dev_tools.txt index dd13bd614..0b826cf41 100644 --- a/requirements/dev_tools.txt +++ b/requirements/dev_tools.txt @@ -1,3 +1,10 @@ +# pip install -r requirements/dev_tools.txt + +# mypy mypy==1.19.1 + +# flake8 flake8==7.3.0 + +# black black==26.3.1 diff --git a/requirements/test.txt b/requirements/test.txt index 020e2f41a..af895692b 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,8 @@ # pip install -r requirements/test.txt + +# pytest pytest<9.2 -pytest-cov>=7.1.0,<8; python_version>="3.14" # only needed to evaluate coverage on the latest supported python version + +# pytest-cov +# Note: only needed to evaluate coverage on the latest supported python version +pytest-cov>=7.1.0,<8; python_version >= "3.14" diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index ecb3741f2..0757e7bce 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -1,4 +1,12 @@ # pip install -r requirements/test_adapter.txt -moto>=3,<6 # For AWS tests -boddle>=0.2.9,<0.3 # For Bottle app tests + +# moto +# Note: for AWS tests +moto>=3,<6 + +# boddle +# Note: for Bottle app tests +boddle>=0.2.9,<0.3 + +# sanic-testing sanic-testing>=0.7 diff --git a/requirements/test_async.txt b/requirements/test_async.txt index 8fa6c6806..5dc51a201 100644 --- a/requirements/test_async.txt +++ b/requirements/test_async.txt @@ -1,6 +1,11 @@ # pip install -r requirements/test_async.txt -r test.txt -r async_dev.txt -asgiref>=3.7.2,<3.8; python_version<"3.9" -asgiref>=3.11.1,<4; python_version>="3.9" -pytest-asyncio<2; + +# asgiref +# Note: asgiref 3.11.1 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +asgiref>=3.7.2,<3.8; python_version < "3.9" +asgiref>=3.11.1,<4; python_version >= "3.9" + +# pytest-asyncio +pytest-asyncio<2 From 58b3de50436ec73cefbc715e521749e0d3718638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:29:28 -0700 Subject: [PATCH 02/17] chore(deps): update moto requirement from <6,>=3 to >=5.2.2,<6 (#1546) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/test_adapter.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index 0757e7bce..44af631f4 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -2,7 +2,11 @@ # moto # Note: for AWS tests -moto>=3,<6 +# Note: moto drops old Pythons across the 5.x line — 5.0.0 requires >=3.8, 5.1.0 >=3.9, 5.2.0 >=3.10; older interpreters stay on the last compatible release. +moto>=3,<5; python_version < "3.8" +moto>=3,<5.1; python_version >= "3.8" and python_version < "3.9" +moto>=3,<5.2; python_version >= "3.9" and python_version < "3.10" +moto>=5.2.2,<6; python_version >= "3.10" # boddle # Note: for Bottle app tests From abac1dd816fe263b18e9caa8f9db071709f26e24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:58:58 -0700 Subject: [PATCH 03/17] chore(deps-dev): update django requirement from <6,>=4.2.30 to >=5.2.15,<6 (#1539) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index ac70e1fdd..f313dc945 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -19,9 +19,10 @@ cheroot<12 CherryPy>=18.10.0,<19 # Django -# Note: Django 4.2.30 requires Python >=3.8; 3.7 stays on the 3.2 line. +# Note: Django 5.x requires Python >=3.10 and 4.x requires >=3.8, so 3.8/3.9 stay on the 4.2 LTS line and 3.7 stays on the 3.2 line. Django>=3.2,<4; python_version < "3.8" -Django>=4.2.30,<6; python_version >= "3.8" +Django>=4.2.30,<5; python_version >= "3.8" and python_version < "3.10" +Django>=5.2.15,<6; python_version >= "3.10" # falcon # Note: falcon 4.2.0 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From be299e7671c4df699697b06dfad3eeb60fb5e0ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:37:17 -0700 Subject: [PATCH 04/17] chore(deps-dev): update pyramid requirement from <3,>=1 to >=2.1,<3 (#1538) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index f313dc945..959ee8823 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -45,7 +45,9 @@ Werkzeug>=2,<3; python_version < "3.9" Werkzeug>=3.1.8,<4; python_version >= "3.9" # pyramid -pyramid>=1,<3 +# Note: pyramid 2.1 requires Python >=3.10; 3.7/3.8/3.9 stay on the last 2.0.x release. +pyramid>=1,<2.1; python_version < "3.10" +pyramid>=2.1,<3; python_version >= "3.10" # setuptools # Note: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 From 5a0cfb80a16b8789ac03c8babe8fa79fe28ca8c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:27:46 -0700 Subject: [PATCH 05/17] chore(deps): update sanic-testing requirement from >=0.7 to >=24.6.0 (#1547) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/test_adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index 44af631f4..702856aac 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -13,4 +13,4 @@ moto>=5.2.2,<6; python_version >= "3.10" boddle>=0.2.9,<0.3 # sanic-testing -sanic-testing>=0.7 +sanic-testing>=24.6.0 From a2f21ae4b1d1d63a6a5dc533aa53a8c4badd103d Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 20 Jul 2026 12:48:40 -0400 Subject: [PATCH 06/17] ci: do not fail CI when Codecov test-results upload errors (#1552) Co-authored-by: Claude --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 89671a50c..a250564f4 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -129,7 +129,7 @@ jobs: uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: directory: ./reports/ - fail_ci_if_error: true + fail_ci_if_error: false flags: ${{ matrix.python-version }} report_type: test_results token: ${{ secrets.CODECOV_TOKEN }} From 097e8c421023aac845d680226b5b9dd60e5944d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:15:54 +0000 Subject: [PATCH 07/17] chore(deps): bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.2 (#1554) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 624243bf6..2927afa2c 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -60,7 +60,7 @@ jobs: - name: Publish release distributions to test.pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: repository-url: https://test.pypi.org/legacy/ @@ -84,4 +84,4 @@ jobs: - name: Publish release distributions to pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 From 26a3072d3d94a56125f8411eed58172f9b072c67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:20:50 +0000 Subject: [PATCH 08/17] chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#1559) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index a250564f4..df435f86d 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -20,7 +20,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -77,7 +77,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} @@ -144,7 +144,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 2927afa2c..a3743b990 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false From 8d1c383a3320dd1609f18d28ce45f60d41b9fa37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:14:55 -0700 Subject: [PATCH 09/17] chore(deps): bump actions/setup-python from 6.3.0 to 7.0.0 (#1557) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index df435f86d..c62ce61e6 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -24,7 +24,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Run lint verification @@ -41,7 +41,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install synchronous dependencies @@ -81,7 +81,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies @@ -148,7 +148,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install dependencies diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index a3743b990..2d0f7630a 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.x" From c49416b33b002962886d6520559bfe2f29315e31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:59:01 -0700 Subject: [PATCH 10/17] chore(deps-dev): update sanic requirement from <26,>=25.3.0 to >=25.12.1,<26 (#1556) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 959ee8823..3fa8d003a 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -56,9 +56,11 @@ setuptools<82 # sanic # Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. # Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +# Note: sanic 25.12 requires Python >=3.10; 3.9 stays on the older 25.x line. tracerite<1.1.2; python_version < "3.9" sanic>=21,<24; python_version < "3.9" -sanic>=25.3.0,<26; python_version >= "3.9" +sanic>=25.3.0,<25.12.0; python_version >= "3.9" and python_version < "3.10" +sanic>=25.12.1,<26; python_version >= "3.10" # starlette # Note: starlette 0.49.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From 62e7ea4274fc60e8a1c7142b1e50992ae17bb009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:03:11 +0000 Subject: [PATCH 11/17] chore(deps-dev): update bottle requirement from <1,>=0.12 to >=0.13.4,<1 (#1555) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 3fa8d003a..4fb4cf774 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -6,7 +6,7 @@ boto3<=2 # bottle -bottle>=0.12,<1 +bottle>=0.13.4,<1 # chalice chalice>=1.28,<1.31; python_version < "3.9" From 8d279b7ebb0bcb73bb57514d6fa3b433331a669f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:12:14 +0000 Subject: [PATCH 12/17] chore(deps): bump slackapi/slack-github-action from 3.0.3 to 4.0.0 (#1561) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index c62ce61e6..dd2119b8c 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -179,7 +179,7 @@ jobs: if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests - uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 with: errors: true webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} From a9153faf1a11debf1e5c14ffa393534145ee6a63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:17:18 +0000 Subject: [PATCH 13/17] chore(deps): bump actions/stale from 10.3.0 to 11.0.0 (#1553) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index 9d99c40da..adad546b8 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From cfb983033c62fcd1f7eace284275d77d49926dc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:36:32 +0000 Subject: [PATCH 14/17] chore(deps-dev): bump mypy from 1.19.1 to 2.3.0 (#1560) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/dev_tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/dev_tools.txt b/requirements/dev_tools.txt index 0b826cf41..59e4cdd21 100644 --- a/requirements/dev_tools.txt +++ b/requirements/dev_tools.txt @@ -1,7 +1,7 @@ # pip install -r requirements/dev_tools.txt # mypy -mypy==1.19.1 +mypy==2.3.0 # flake8 flake8==7.3.0 From 59ec0443fa9bf90eda57cee6108049e302c81a8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:47:02 +0000 Subject: [PATCH 15/17] chore(deps-dev): update fastapi requirement from <1,>=0.128.8 to >=0.141.1,<1 (#1562) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 4fb4cf774..c5cd52ca7 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -30,9 +30,9 @@ falcon>=2,<4; python_version < "3.9" falcon>=4.2.0,<5; python_version >= "3.9" # fastapi -# Note: fastapi 0.128.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. fastapi>=0.70.0,<1; python_version < "3.9" -fastapi>=0.128.8,<1; python_version >= "3.9" +fastapi>=0.128.8,<0.129.0; python_version >= "3.9" and python_version < "3.10" +fastapi>=0.141.1,<1; python_version >= "3.10" # Flask # Note: Flask 3.1.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From 2572efb6550b20cb0bb4162e5ffd59223579b68d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:51:22 +0000 Subject: [PATCH 16/17] chore(deps-dev): update chalice requirement from <2,>=1.32.0 to >=1.33.0,<2 (#1558) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index c5cd52ca7..34a96a911 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -10,7 +10,8 @@ bottle>=0.13.4,<1 # chalice chalice>=1.28,<1.31; python_version < "3.9" -chalice>=1.32.0,<2; python_version >= "3.9" +chalice>=1.32.0,<1.33; python_version >= "3.9" and python_version < "3.10" +chalice>=1.33.0,<2; python_version >= "3.10" # cheroot cheroot<12 From a70d2475d184acfe74ef2f7619a37ef6f81951a0 Mon Sep 17 00:00:00 2001 From: Tracy Rericha Date: Mon, 10 Aug 2026 13:50:28 -0400 Subject: [PATCH 17/17] docs request --- docs/english/concepts/authorization.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/english/concepts/authorization.md b/docs/english/concepts/authorization.md index f6a258491..9d0086ca7 100644 --- a/docs/english/concepts/authorization.md +++ b/docs/english/concepts/authorization.md @@ -2,9 +2,10 @@ Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack request. -Apps installed on a single workspace can simply pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options. The easier option is to use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. Read the section on [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for details. +Apps installed on a single workspace can pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options: -For a more custom solution, you can set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. +* Use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. See [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for more details. +* Set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. `AuthorizeResult` should have a few specific properties, all of type `str`: - Either **`bot_token`** (xoxb) *or* **`user_token`** (xoxp) are **required**. Most apps will use `bot_token` by default. Passing a token allows built-in functions (like `say()`) to work. @@ -62,3 +63,11 @@ app = App( authorize=authorize ) ``` + +## Handling failed token lookups {#handling-failed-token-lookups} + +In the event that you receive events from disconnected teams, make sure to gracefully drop these unauthorized payloads by returning `None` to silently drop the payload as follows. + +In the custom `authorize` callback, if a token lookup fails due to an `unknown team_id`, you should return `None` rather than raising an exception (such as `BoltUnauthorizedError`). Bolt's authorization middleware will recognize the `None` response as an authorization failure and immediately halt execution, silently dropping the payload without generating server error logs. + +Additionally, the `user_facing_authorize_error_message` parameter strictly controls the ephemeral message sent back to the end user via the Slack UI. It does not suppress internal server logs or exceptions. To achieve both the desired UI behavior and clean server logs, pair this parameter with returning `None` in your authorize function.