Skip to content

Botocore sync: feedback needed for 1.43.67 #1705

Description

@claude

Botocore sync needs your input

Botocore 1.43.67 introduces changes that require a port before the upper bound can be raised.
ENABLE_BUMP is false for the sync bot, so this run made no code changes — it only analyzed
the diff and validated the hashes.

Botocore diff: boto/botocore@1.43.62...1.43.67
Sync PR: none open (last one, #1693, is merged and covers up to < 1.43.63)
WIP PR: none

Classifier verdict: port-required

register_retry_handler (tracked override) gains MaxAttemptsSeeder registration; aiobotocore
override must mirror it

File Function Change Verdict Reason
botocore/init.py module data (__version__, _xform_cache) changed pure-sync Version bump + new static dict entries (WhatsApp ops); no function logic changed
botocore/retries/standard.py register_retry_handler changed port-required Exact match in overrides (standard.register_retry_handler); adds new client.meta.events.register(...) call
botocore/retries/standard.py MaxAttemptsSeeder (new class) added port-required New class used inside the now-changed register_retry_handler; aiobotocore's reimplementation lacks it

Hash check confirms ittests/test_patches.py fails exactly where the classifier predicted:

FAILED tests/test_patches.py::test_patches[register_retry_handler-digests211]
  assert 'dc2a6d66286b5413704564fe21e421343a323136' not in the recorded digests
1 failed, 212 passed

What changed upstream

MaxAttemptsSeeder was added in botocore 1.43.66 (absent in 1.43.65). It seeds
request.context['retries']['max'] at request-created time so the max token of the
amz-sdk-request header is present on the initial attempt, not just from the first retry
onward. register_retry_handler now registers it inside the NEW_RETRIES_ENABLED branch:

client.meta.events.register(
    f'request-created.{service_event_name}',
    MaxAttemptsSeeder(max_attempts).seed_max_attempts,
    unique_id=f'seed-max-attempts-{service_event_name}',
)

aiobotocore/retries/standard.py reimplements register_retry_handler wholesale (it must pick
AioRetryHandler vs AnyioRetryHandler and the Aio* policy classes), so it does not inherit
this registration. Without the port, aiobotocore emits amz-sdk-request: attempt=1 with no max
token on the first attempt — a behavioral divergence from botocore, not just a stale hash.

MaxAttemptsSeeder.seed_max_attempts is a plain sync handler that only mutates
request.context — no I/O — so it needs no async adaptation. The adaptive path is unaffected:
aiobotocore/retries/adaptive.py only registers the rate limiter and gets the seeder via the
standard handler, same as botocore.

Questions

1. Confirm the port shape: import MaxAttemptsSeeder from botocore rather than reimplementing it?

  • Context: the class is sync-only and has no awaitables in it. aiobotocore's file already
    imports 18 names from botocore.retries.standard (ExponentialBackoff, MaxAttemptsChecker,
    RetryQuotaChecker, …) and only subclasses what genuinely needs async.
  • Option A (preferred): add MaxAttemptsSeeder to the existing import list and mirror the
    registration block verbatim into aiobotocore's register_retry_handler, same position, same
    unique_id. Minimal diff from botocore, per CLAUDE.md §"Minimize divergence".
  • Option B: reimplement the class locally. No upside — it would be pure drift.
  • Trade-off: A couples us to a botocore symbol that only exists from 1.43.66 — see question 2.

2. Does this port raise the lower bound to 1.43.66/1.43.67, or should the import be guarded?

  • Context: current bound is botocore >= 1.43.3, < 1.43.63. A bare
    from botocore.retries.standard import MaxAttemptsSeeder is an ImportError on every supported
    botocore below 1.43.66 — i.e. the entire currently-supported range.
  • Option A: let /aiobotocore-bot:update-botocore-bounds --mode=port move both bounds to
    >= 1.43.67, < 1.43.68. This is what the skill does in port mode and matches how aiobotocore has
    historically handled ports. Clean, no conditional code.
  • Option B: keep the wide lower bound and guard with try: ... except ImportError: /
    hasattr so the seeder is registered only when available.
  • Trade-off: A is a hard floor bump for downstream users on a narrow botocore pin; B preserves
    the range but adds version-conditional branching that has to be carried until the floor moves
    anyway — and it is exactly the kind of unmatched divergence CLAUDE.md asks us to avoid.
    My reading is A, but it is a user-visible packaging decision, so I would rather you confirm.

3. How far should the ported tests go — the mirror lacks BaseRetryTest?

  • Context: botocore added TestMaxAttemptsInRequestHeader (4 tests) to
    tests/unit/retries/test_standard_retry_v2_1.py, which does have an aiobotocore mirror at
    tests/botocore_tests/unit/retries/test_standard_retry_v2_1.py. But the new class subclasses
    tests.functional.test_retry.BaseRetryTest, and there is no tests/botocore_tests/functional/test_retry.py
    mirror — so the base class does not exist on our side. ClientHTTPStubber does exist
    (tests/botocore_tests/__init__.py:183).
  • Option A: port only test_seeder_adds_max_to_empty_context (the pure-unit one, no base class
    needed) plus a small async test that builds a client and asserts the max token on the first
    amz-sdk-request header, using the existing ClientHTTPStubber. Targeted, no new harness.
  • Option B: port BaseRetryTest into tests/botocore_tests/functional/test_retry.py first, so
    all four upstream tests port near-verbatim and future retry-test syncs have the base available.
  • Trade-off: B is more upfront work and pulls in a functional-test harness we have so far done
    without; A is cheaper but leaves the next retry sync to face the same gap. Leaning A unless you
    want the harness backfilled now.

Also in the diff (no action needed)

botocore/__init__.py moved __version__ and gained _xform_cache entries for new WhatsApp
operations — static data, no function logic. tests/unit/test_utils.py and the endpoint-rules
JSON fixtures changed but have no aiobotocore mirror.

Scope estimate

Small. One import line, one ~10-line registration block, one hash update in
tests/test_patches.py (add dc2a6d66286b5413704564fe21e421343a323136), plus tests and the bound
update. The only genuinely open items are questions 2 and 3.

How to respond

Reply with your answers. You can:

  • Answer questions directly
  • Ask @claude for more context (e.g. "@claude show me the botocore diff for question 2")
  • Provide partial answers — the bot will proceed with what it can and ask follow-ups
  • Suggest a different approach entirely
  • Flip ENABLE_BUMP to true if you want the bot to attempt the port itself next run

Once resolved, the bot will apply decisions on its next run. Close this issue when done.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions