From 1aa4509c76c7a1988b54a23d4200a1c791482ee1 Mon Sep 17 00:00:00 2001 From: TiM Date: Mon, 19 Oct 2026 10:46:53 +1300 Subject: [PATCH 1/5] chore: pin CI black to the pre-commit rev and stop Dependabot bumping it The [dev] black bound is meant to match the black rev the pre-commit hook pins (25.9.0), so CI's `black --check` and the local hook format the same way. Three Dependabot bumps walked it from <25.10 to <26.11, and CI (py3.12) has been running black 26.x since. The two disagree: test_import_footprint.py was committed in 25.9 style through the hook, failed CI under 26.x, and was reformatted to 26 style, which the hook now rewrites back. Restore the bound to <25.10, take the 25.9 formatting of that one call, and add a Dependabot ignore for black so the cap moves only with the hook rev, by hand. The comments now say why the cap is where it is: 25.11.0 is the last black that runs on Python 3.9, so 25.12 is the ceiling while 3.9 is supported. --- .github/dependabot.yml | 5 +++++ .pre-commit-config.yaml | 5 +++-- pyproject.toml | 9 ++++++--- tests/test_import_footprint.py | 4 +++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index af04d92..5848440 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,11 @@ updates: open-pull-requests-limit: 5 labels: - "dependencies" + ignore: + # The [dev] black cap tracks the black rev pinned in + # .pre-commit-config.yaml, and the two move together by hand. A + # Dependabot bump of the cap alone splits CI from the local hook. + - dependency-name: "black" # GitHub Actions used by the CI and publish workflows. - package-ecosystem: "github-actions" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 200f675..9ba4f88 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,8 +9,9 @@ repos: - id: ruff-check - repo: https://github.com/psf/black - # Last black that still runs on Python 3.9 (the project's runtime floor); - # 25.12.0+ require 3.10+. Kept in sync with the [dev] cap in pyproject.toml. + # Kept in sync with the [dev] cap in pyproject.toml (<25.10); move both + # together by hand. Stay on a black that runs on Python 3.9 (the project's + # runtime floor): 25.11.0 is the last one, 25.12.0+ require 3.10. rev: 25.9.0 hooks: - id: black diff --git a/pyproject.toml b/pyproject.toml index cee7f78..ab699d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,9 +41,12 @@ dev = [ "pytest-asyncio>=0.21", "respx>=0.20", "ruff>=0.4", - # Capped below 25.10: 25.12.0+ drop Python 3.9, which the package still - # supports. Keeps CI and the pinned pre-commit hook on the same black. - "black>=24.0,<26.11", + # Capped below 25.10 so CI formats with the same black the pre-commit hook + # pins (rev 25.9.0 in .pre-commit-config.yaml). Move the two together by + # hand; Dependabot ignores black (.github/dependabot.yml). Keep the cap at + # or below 25.12 while Python 3.9 is supported: 25.12.0+ require 3.10, so + # a higher cap resolves a different black per interpreter. + "black>=24.0,<25.10", "mypy>=1.8", "build>=1.0", "pre-commit>=3.5", diff --git a/tests/test_import_footprint.py b/tests/test_import_footprint.py index 147fb3a..e915985 100644 --- a/tests/test_import_footprint.py +++ b/tests/test_import_footprint.py @@ -64,7 +64,9 @@ def exec_module(self, module): sys.meta_path.insert(0, _Recorder()) import snowflake_sql_api # noqa: F401 print(",".join(sorted(set(attempted)))) -""".format(forbidden=FORBIDDEN_ON_BARE_IMPORT) +""".format( + forbidden=FORBIDDEN_ON_BARE_IMPORT +) _TIMING_SCRIPT = """ import time From 0ea6c303909d75d5fc9836b5a15ce3f6269313b1 Mon Sep 17 00:00:00 2001 From: TiM Date: Mon, 19 Oct 2026 10:51:48 +1300 Subject: [PATCH 2/5] chore: keep black security updates open past the Dependabot ignore Scope the black ignore to semver version updates, so routine bumps stay off while a security update can still open a PR. --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5848440..fa40e62 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,7 +12,12 @@ updates: # The [dev] black cap tracks the black rev pinned in # .pre-commit-config.yaml, and the two move together by hand. A # Dependabot bump of the cap alone splits CI from the local hook. + # Scoped to version updates so a black security update still opens. - dependency-name: "black" + update-types: + - "version-update:semver-major" + - "version-update:semver-minor" + - "version-update:semver-patch" # GitHub Actions used by the CI and publish workflows. - package-ecosystem: "github-actions" From d7008119f7770d057bd8a49fa0fad8176ee26163 Mon Sep 17 00:00:00 2001 From: TiM Date: Mon, 19 Oct 2026 13:28:26 +1300 Subject: [PATCH 3/5] fix(review): make the black Dependabot ignore match the range cap An update-types ignore needs the dependency's current version, and the [dev] cap is a range with none, so the scoped ignore matched nothing and cap bumps would have kept coming. A name-only ignore blocks every version update and leaves security updates alone, so return to it and say why. Also correct the pre-commit header, which said every hook rev tracks a [dev] floor: black tracks the cap, and ruff tracks nothing. --- .github/dependabot.yml | 10 +++++----- .pre-commit-config.yaml | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fa40e62..30f3654 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,12 +12,12 @@ updates: # The [dev] black cap tracks the black rev pinned in # .pre-commit-config.yaml, and the two move together by hand. A # Dependabot bump of the cap alone splits CI from the local hook. - # Scoped to version updates so a black security update still opens. + # Name-only on purpose: the cap is a range with no pinned version, so an + # update-types ignore would match nothing. A name-only ignore does not + # block security updates. Black's security fixes since 25.9.0 need + # Python 3.10+, so a security PR for it is a decision about the 3.9 + # floor and the hook rev, not a plain merge. - dependency-name: "black" - update-types: - - "version-update:semver-major" - - "version-update:semver-minor" - - "version-update:semver-patch" # GitHub Actions used by the CI and publish workflows. - package-ecosystem: "github-actions" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ba4f88..d9b751c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,8 @@ # Pre-commit hooks for snowflake-sql-api. Install with `pre-commit install` # (the hook then runs on every commit); run the full set with -# `pre-commit run --all-files`. Hook versions track the `[dev]` floors in -# pyproject.toml. +# `pre-commit run --all-files`. The black rev is pinned to match the `[dev]` +# black cap in pyproject.toml (see its comment); the other revs are set here +# independently of the `[dev]` floors. repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.17 From fabc92ca939bb4913f93fcbe5014ce6fccfdbcfd Mon Sep 17 00:00:00 2001 From: TiM Date: Mon, 19 Oct 2026 13:34:31 +1300 Subject: [PATCH 4/5] fix(review): name the real reason the black ignore is name-only An update-types ignore is computed from the first X.Y.Z version in the requirement string. The cap has none, which is why the typed form matched nothing; a range written with a three-part floor would not have that problem, so the comment should not blame ranges in general. --- .github/dependabot.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 30f3654..6a8cd9b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,8 +12,9 @@ updates: # The [dev] black cap tracks the black rev pinned in # .pre-commit-config.yaml, and the two move together by hand. A # Dependabot bump of the cap alone splits CI from the local hook. - # Name-only on purpose: the cap is a range with no pinned version, so an - # update-types ignore would match nothing. A name-only ignore does not + # Name-only on purpose: an update-types ignore is computed from the first + # X.Y.Z version in the requirement, and `>=24.0,<25.10` has none, so it + # would match nothing. A name-only ignore does not # block security updates. Black's security fixes since 25.9.0 need # Python 3.10+, so a security PR for it is a decision about the 3.9 # floor and the hook rev, not a plain merge. From 908107e635bd075f5923a3287a4d20b2e75e83ba Mon Sep 17 00:00:00 2001 From: TiM Date: Mon, 19 Oct 2026 14:32:59 +1300 Subject: [PATCH 5/5] fix(review): cite dependabot-core for the black ignore's security-update behaviour Point the comment at the code that decides it, IgnoreCondition#ignored_versions, which returns only an ignore's explicit versions for a security-update job, so a reader can check the claim where it is made. --- .github/dependabot.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6a8cd9b..cb65dac 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,8 +14,10 @@ updates: # Dependabot bump of the cap alone splits CI from the local hook. # Name-only on purpose: an update-types ignore is computed from the first # X.Y.Z version in the requirement, and `>=24.0,<25.10` has none, so it - # would match nothing. A name-only ignore does not - # block security updates. Black's security fixes since 25.9.0 need + # would match nothing. Security updates are not blocked by it: in + # dependabot-core, IgnoreCondition#ignored_versions returns early for + # security_updates_only with just the explicit `versions`, and a + # name-only ignore has none. Black's security fixes since 25.9.0 need # Python 3.10+, so a security PR for it is a decision about the 3.9 # floor and the hook rev, not a plain merge. - dependency-name: "black"