babelqueue 1.23.0__tar.gz → 1.25.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {babelqueue-1.23.0 → babelqueue-1.25.0}/.github/workflows/ci.yml +1 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/.github/workflows/release.yml +2 -2
- {babelqueue-1.23.0 → babelqueue-1.25.0}/CHANGELOG.md +152 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/PKG-INFO +16 -4
- {babelqueue-1.23.0 → babelqueue-1.25.0}/README.md +15 -3
- {babelqueue-1.23.0 → babelqueue-1.25.0}/pyproject.toml +1 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/__init__.py +13 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/app.py +230 -31
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/artemis_transport.py +26 -4
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/asb_transport.py +77 -3
- babelqueue-1.25.0/src/babelqueue/dead_letter.py +87 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/headers.py +71 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/kafka_transport.py +68 -2
- babelqueue-1.25.0/src/babelqueue/otel.py +677 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/pulsar_transport.py +31 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/redrive.py +11 -3
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/sqs_transport.py +29 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/transport.py +77 -0
- babelqueue-1.25.0/tests/conformance/CONFORMANCE_VERSION +1 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/manifest.json +3039 -2
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/schema/results.example.json +25 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_app.py +114 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_artemis_transport.py +68 -0
- babelqueue-1.25.0/tests/test_asb_conformance.py +177 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_asb_transport.py +283 -1
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_behaviour_conformance.py +108 -4
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_conformance.py +7 -0
- babelqueue-1.25.0/tests/test_dead_letter_observer.py +298 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_headers.py +18 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_kafka_retry.py +58 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_kafka_transport.py +140 -0
- babelqueue-1.25.0/tests/test_otel.py +553 -0
- babelqueue-1.25.0/tests/test_otel_conformance.py +480 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_poison_routing.py +85 -15
- babelqueue-1.25.0/tests/test_publish_envelope.py +181 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_pulsar_transport.py +101 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_redrive.py +8 -0
- babelqueue-1.25.0/tests/test_sqs_conformance.py +155 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_sqs_transport.py +99 -13
- babelqueue-1.23.0/src/babelqueue/dead_letter.py +0 -39
- babelqueue-1.23.0/src/babelqueue/otel.py +0 -265
- babelqueue-1.23.0/tests/conformance/CONFORMANCE_VERSION +0 -1
- babelqueue-1.23.0/tests/test_asb_conformance.py +0 -84
- babelqueue-1.23.0/tests/test_otel.py +0 -263
- babelqueue-1.23.0/tests/test_sqs_conformance.py +0 -80
- {babelqueue-1.23.0 → babelqueue-1.25.0}/.github/FUNDING.yml +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/.github/dependabot.yml +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/.gitignore +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/LICENSE +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/conformance-known-gaps.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/scripts/check_conformance_results.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/_envelope_shape.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/_poison_record.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/celery.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/codec.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/contracts.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/django/__init__.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/django/apps.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/django/management/__init__.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/django/management/commands/__init__.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/django/management/commands/babelqueue_worker.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/exceptions.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/gdpr.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/idempotency.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/kafka_retry.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/outbox.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/pika_transport.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/py.typed +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/redis_transport.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/replay.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/routing.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/src/babelqueue/schema.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/blank-ascii-job-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/data-array-rejected.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/dead-letter-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/dead-lettered.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/duplicate-keys-last-wins.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/empty-data-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-job-and-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-job-nonascii-space-and-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-meta-attempts.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-meta-max-retries.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-meta-source.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-meta-ts.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/forbidden-key-timestamp.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/invalid-missing-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/invalid-unknown-schema-version.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/invalid-wrong-case-job.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-array-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-bool-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-empty-string-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-missing-job-array-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-null-no-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-null-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-number-blank-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-number-nonstring-urn.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-number-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/job-type-object-urn-fallback.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/meta-ext-vendor-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/order-created.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-cr.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-ff.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-lf.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-space.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-tab.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-job-vt.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-cr.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-ff.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-lf.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-space.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-tab.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-blank-trace-id-vt.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-job-and-urn-both-blank.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-reemit-job-ascii-padded.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-reemit-job-unicode-padded.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-reemit-trace-id-padded.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-unicode-job-emsp.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-unicode-job-ideographic.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-unicode-job-nbsp.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-urn-only-ascii-blank.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/trim-urn-only-unicode-space.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/unicode-and-numbers.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/unknown-lang.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/unknown-meta-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/unknown-toplevel-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/urn-alias.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/urn-only-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/fixtures/wrong-case-keys-roundtrip.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/schema/message-envelope.schema.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/conformance/schema/results.schema.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/fakes/confluent_kafka/__init__.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/fakes/confluent_kafka/admin.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/fixtures/dead-lettered.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/fixtures/order-created.json +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/redis_backend.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_artemis_conformance.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_celery.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_codec.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_dead_letter.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_django.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_gdpr.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_idempotency.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_kafka_conformance.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_kafka_integration.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_kafka_integration_logic.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_outbox.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_overhead.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_pika_transport.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_pulsar_conformance.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_redis_transport.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_reencode_regression.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_replay.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_schema.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_schema_conformance.py +0 -0
- {babelqueue-1.23.0 → babelqueue-1.25.0}/tests/test_shutdown.py +0 -0
|
@@ -177,7 +177,7 @@ jobs:
|
|
|
177
177
|
--health-retries 10
|
|
178
178
|
env:
|
|
179
179
|
# The pinned conformance release (ADR-0032). Bump together with tests/conformance/.
|
|
180
|
-
BQ_CONFORMANCE_VERSION: 1.
|
|
180
|
+
BQ_CONFORMANCE_VERSION: 1.9.0
|
|
181
181
|
# sha256 of that release's fetch.sh: the script runs on the runner, so it is pinned rather
|
|
182
182
|
# than trusted from a mutable tag. Bump together with BQ_CONFORMANCE_VERSION.
|
|
183
183
|
BQ_FETCH_SH_SHA256: 8bb6ba567b288fb0ac71a70fd17041b2503df119e01998d76fdad372570bf849
|
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
# Recommended for PyPI Trusted Publishing — configure a "pypi" environment +
|
|
15
15
|
# a trusted publisher on PyPI (repo BabelQueue/babelqueue-python, this workflow).
|
|
16
16
|
environment: pypi
|
|
17
|
-
# redis_layout (conformance 1.7.0) drives the real Lua scripts: a real redis:7 is mandatory,
|
|
17
|
+
# redis_layout (conformance 1.7.0+) drives the real Lua scripts: a real redis:7 is mandatory,
|
|
18
18
|
# exactly as in ci.yml's `conformance` job — without it the cases are reported as failures.
|
|
19
19
|
services:
|
|
20
20
|
redis:
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
--health-retries 10
|
|
29
29
|
env:
|
|
30
30
|
# The pinned conformance release (ADR-0032). Keep identical to ci.yml.
|
|
31
|
-
BQ_CONFORMANCE_VERSION: 1.
|
|
31
|
+
BQ_CONFORMANCE_VERSION: 1.9.0
|
|
32
32
|
BQ_FETCH_SH_SHA256: 8bb6ba567b288fb0ac71a70fd17041b2503df119e01998d76fdad372570bf849
|
|
33
33
|
permissions:
|
|
34
34
|
id-token: write # PyPI Trusted Publishing (OIDC) — no API token needed
|
|
@@ -9,6 +9,158 @@ The envelope wire format is versioned separately by `meta.schema_version`
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.25.0] - 2026-11-01
|
|
13
|
+
|
|
14
|
+
### Changed (dashboard-breaking) — `babelqueue.otel` follows the OpenTelemetry messaging semantic conventions v1.44.0 (ADR-0038)
|
|
15
|
+
- **Span names change with no overlap:** `publish <urn>` becomes `send <queue>` and
|
|
16
|
+
`process <urn>` becomes `process <queue>` (the bare `send` / `process` when there is no queue).
|
|
17
|
+
The URN is never in the span name now (it is unbounded); it is the `babelqueue.urn` attribute.
|
|
18
|
+
The producer's `messaging.destination.name` is the queue the message is published to (it was the
|
|
19
|
+
URN), so a producer span and its consumer span share one destination. **Saved queries and
|
|
20
|
+
dashboards that filter on the old span names or on the producer destination must be updated.**
|
|
21
|
+
- **`messaging.system` is now the broker when you pass it.** `wrap_handler(..., system="kafka")` and
|
|
22
|
+
`publish(..., system="kafka")` (`redis`, `rabbitmq`, `aws_sqs`, `servicebus`, `pulsar`, `kafka`,
|
|
23
|
+
`activemq`; any other value is written as given). **Omitting `system` keeps `babelqueue`, which is
|
|
24
|
+
deprecated** (a one-time warning is logged); what an absent `system` means after the deprecation
|
|
25
|
+
window is not decided yet, so pass it now.
|
|
26
|
+
- New span attributes: `messaging.operation.type` / `messaging.operation.name` (`send` or
|
|
27
|
+
`process`), `babelqueue.urn`, `babelqueue.schema_version`, `babelqueue.attempts` and, on a failure,
|
|
28
|
+
`error.type` (`module.QualName` of the exception, the bare name for builtins) with an explicit
|
|
29
|
+
`ERROR` status whose description is the exception message. `consumer_group="..."` on
|
|
30
|
+
`wrap_handler` writes `messaging.consumer.group.name`.
|
|
31
|
+
- **Dual-emit for this MINOR only:** the deprecated `messaging.operation` (`publish` / `process`) and
|
|
32
|
+
`messaging.babelqueue.attempts` are still written next to their replacements. **The next MINOR
|
|
33
|
+
removes both.**
|
|
34
|
+
- `babelqueue.otel.SEMCONV_VERSION` (`1.44.0`) and `SEMCONV_SCHEMA_URL` (`https://opentelemetry.io/schemas/1.44.0`)
|
|
35
|
+
are public; create the tracer and meter with the schema URL
|
|
36
|
+
(`trace.get_tracer("orders", schema_url=otel.SEMCONV_SCHEMA_URL)`).
|
|
37
|
+
- A `trace_id` that is not a UUID is still preserved byte for byte and hashed for the OTel TraceID
|
|
38
|
+
(`trace_id_of` is unchanged); `wrap_handler` now logs a warning once per distinct value
|
|
39
|
+
(message-envelope.md 5.1, ADR-0038 section 7). Nothing is rejected or regenerated.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- **Standard metrics (ADR-0039), opt-in.** Pass `meter=` to `wrap_handler` and `publish` to record
|
|
43
|
+
`messaging.process.duration` (histogram, `s`, semconv bucket advice where the API supports it),
|
|
44
|
+
`messaging.client.consumed.messages`, `messaging.client.sent.messages` and `babelqueue.retries`
|
|
45
|
+
(counters, `{message}`; `retries` counts a delivery with `attempts > 0`). The attributes are
|
|
46
|
+
`messaging.system`, `messaging.operation.name`, `messaging.destination.name` (when known) and
|
|
47
|
+
`error.type` (on failure); never the URN, an id, the attempt count or a dead-letter reason. With no
|
|
48
|
+
`meter` nothing is recorded.
|
|
49
|
+
- **`babelqueue.dlq.messages` and the dead-letter observer seam (ADR-0039 section 5).**
|
|
50
|
+
`BabelQueue(..., dead_letter_observer=callback)` calls `callback(DeadLetterEvent(queue,
|
|
51
|
+
dead_letter_queue, reason))` once per dead-letter the runtime carried out, after the DLQ write is
|
|
52
|
+
confirmed (`BabelQueue._write_dead_letter`, the one chokepoint for exhausted retries, an unknown
|
|
53
|
+
URN, a poison body and a version-gated raw body) or after an SDK-initiated native dead-letter. A
|
|
54
|
+
failed write, a native no-op (SQS) and a broker-side dead-letter are not reported; an exception
|
|
55
|
+
raised by the observer is logged and never changes how the message is settled.
|
|
56
|
+
`otel.dead_letter_observer(meter, system=...)` is the observer that counts the metric.
|
|
57
|
+
New exports: `DeadLetterEvent`, `DeadLetterObserver`, and the `NativeDeadLetterSettler` protocol.
|
|
58
|
+
- **`NativeDeadLetterSettler`**: the optional native-settle variant
|
|
59
|
+
`settle_native_dead_letter(message, reason, description) -> bool` next to `NativeSettler`
|
|
60
|
+
(unchanged). When a transport implements it the runtime prefers it and reports a dead-letter only
|
|
61
|
+
on `True`; `AsbTransport` implements it, the SQS no-op does not.
|
|
62
|
+
- **`BabelQueue.publish_envelope(urn, data, *, queue=None, trace_id=None, headers=None)`** returns a
|
|
63
|
+
`PublishResult(envelope, error)` pair instead of raising. After a transport failure it still
|
|
64
|
+
carries the envelope that was handed to the transport (so `otel.publish` can put `meta.id` and
|
|
65
|
+
`trace_id` on a failed span); after a `make` or `encode` failure `envelope` is `None`. `publish`
|
|
66
|
+
and `publish_with_headers` delegate to it and behave exactly as before (an empty `queue` still
|
|
67
|
+
resolves to the default queue). `otel.publish` falls back to a duck-typed `app.publish(...)` for
|
|
68
|
+
objects that do not have it.
|
|
69
|
+
- Conformance **1.9.0** is vendored; the `span-shape`, `trace-id` and `metrics` sections (and the six
|
|
70
|
+
new `traceparent` cases) run and pass.
|
|
71
|
+
- **W3C `traceparent` / `tracestate` on the Kafka, Pulsar, Artemis and Azure Service Bus
|
|
72
|
+
transports (ADR-0028; broker-bindings 4.3, 5.2, 6.3, 7.2).** Each transport now implements the
|
|
73
|
+
`HeaderPublisher` capability: `publish_with_headers(queue, body, headers)` carries the headers
|
|
74
|
+
out of band beside the `bq-*` / `bq_*` projection (Kafka record headers, Pulsar message
|
|
75
|
+
properties, Artemis AMQP 1.0 application-properties, Azure Service Bus application properties).
|
|
76
|
+
The projection wins a key collision, and the body is byte-identical to a plain `publish`.
|
|
77
|
+
`pop` surfaces every non-projection header on `ReceivedMessage.headers` (so
|
|
78
|
+
`babelqueue.otel.wrap_handler` links the consumer span to the producer's across these brokers);
|
|
79
|
+
the projection keys themselves are never leaked, and `bq-replay-bypass` (ADR-0027) passes
|
|
80
|
+
through. A Kafka retry parks the consumed record's `traceparent` / `tracestate` on the tier topic
|
|
81
|
+
(the re-inject consumer already keeps every header except `bq-delay` / `bq-original-topic`), so
|
|
82
|
+
a retried message stays in the original trace. Retry tier names, `retry_tiers_ms` and the
|
|
83
|
+
poison / DLQ shapes are unchanged. `babelqueue.headers.out_of_band_headers` is the shared helper.
|
|
84
|
+
- **`RedrivePublisher` capability and `redrive(bypass=True)` on the header-capable transports.**
|
|
85
|
+
Kafka, Pulsar, Artemis and Azure Service Bus becoming `HeaderPublisher`s means
|
|
86
|
+
`redrive(..., bypass=True)` now really stamps the `bq-replay-bypass` marker on them
|
|
87
|
+
(`RedriveItem.bypassed` is `True`; it was `False` before, because the transports could not carry
|
|
88
|
+
headers), and the write goes through the transport's header-carrying path. So that this path
|
|
89
|
+
loses nothing the ordinary redrive write keeps, `Kafka` and `Pulsar` implement the new optional
|
|
90
|
+
`publish_redriven(queue, body, headers, route_urn, key)`: Kafka's is the broker-confirmed settle
|
|
91
|
+
write (a delivery failure raises `KafkaDeliveryError`, the DLQ record is restored and not
|
|
92
|
+
committed past) and carries the DLQ record key unchanged; Pulsar's pins `bq-job` to the
|
|
93
|
+
dead-lettered delivery's routing property. `redrive` uses it when the transport has it.
|
|
94
|
+
- **`AsbTransport.settle_native_dead_letter(message, reason, description) -> bool`** (ADR-0039
|
|
95
|
+
section 5.1): the optional native-settle variant that reports what it did. It performs the same
|
|
96
|
+
settle as `settle_native` (which is unchanged) and returns `True` when the receiver's
|
|
97
|
+
`dead_letter_message` ran, `False` when it only abandoned the lock or had no handle.
|
|
98
|
+
|
|
99
|
+
## [1.24.0] - 2026-10-26
|
|
100
|
+
|
|
101
|
+
Implements conformance **1.8.0** (vendored from the `v1.8.0` release asset, verified with
|
|
102
|
+
`fetch.sh --check`; `BQ_CONFORMANCE_VERSION` is now `1.8.0` in `ci.yml` and `release.yml`). The
|
|
103
|
+
envelope stays at `schema_version: 1`; nothing here touches the wire (ADR-0037). The new
|
|
104
|
+
`version_gate_pulsar`, `version_gate_artemis`, `version_gate_sqs` and `version_gate_asb` sections run
|
|
105
|
+
and pass (17 cases each; Python has the `on_unknown_urn` option, so no row is skipped). A MINOR.
|
|
106
|
+
|
|
107
|
+
### Changed (behaviour)
|
|
108
|
+
- **With the DLQ off, SQS and Azure Service Bus no longer drop a version-gated message or retry a
|
|
109
|
+
poison body; they are left to the broker's native mechanism (ADR-0037 decision 1 rule 3 and
|
|
110
|
+
decision 2 "Go and Python"; broker-bindings 3.5, 4.5, 5.7; owner decision 2026-10-25).**
|
|
111
|
+
- *Version-gated message* (an unsupported `bq-schema-version`), `dead_letter` off and a strategy
|
|
112
|
+
other than `DELETE` or `DEAD_LETTER`: it used to be acknowledged and dropped. It is now **not**
|
|
113
|
+
acknowledged. SQS: no `DeleteMessage`; visibility expiry redelivers it (the gate rejects it
|
|
114
|
+
again) and the queue's `RedrivePolicy` moves it to the native DLQ after `maxReceiveCount`. Azure
|
|
115
|
+
Service Bus: an explicit native `DeadLetter` (reason `unsupported schema version`) into
|
|
116
|
+
`$DeadLetterQueue` instead of `Complete`.
|
|
117
|
+
- *Poison body* (invalid JSON or an identity-less object), `dead_letter` off and a strategy other
|
|
118
|
+
than `DELETE` or `DEAD_LETTER`: it used to take the bounded `FAIL` path (re-publish an empty
|
|
119
|
+
envelope up to `max_attempts`, then drop). On SQS and Azure Service Bus it is now settled
|
|
120
|
+
natively **on the first delivery** exactly as above (Azure Service Bus reason `poison`): no
|
|
121
|
+
re-publish, no retry, no ack, body unchanged, the event is logged. Every other transport
|
|
122
|
+
(Pulsar, Artemis, AMQP, Kafka, Redis, memory) keeps its behaviour; `DELETE` (ack and drop),
|
|
123
|
+
`dead_letter=True` and the `DEAD_LETTER` strategy (the raw record, then ack) are unchanged on
|
|
124
|
+
SQS and Azure Service Bus too.
|
|
125
|
+
- A deployment that relied on SQS/Azure Service Bus silently dropping these messages now needs a
|
|
126
|
+
`RedrivePolicy` (SQS) or accepts them in `$DeadLetterQueue` (Azure Service Bus), or sets
|
|
127
|
+
`dead_letter=True` / `on_unknown_urn=DELETE`.
|
|
128
|
+
|
|
129
|
+
### Fixed
|
|
130
|
+
- **SQS: the default dead-letter queue name is now `<queue>-dlq` (FIFO `<queue>-dlq.fifo`) (ADR-0037
|
|
131
|
+
decision 7).** The old default `<queue>.dlq` was never a valid Amazon SQS queue name (a name may hold only
|
|
132
|
+
alphanumerics, hyphens and underscores, plus one `.fifo` suffix), so a DLQ write by default could not find a
|
|
133
|
+
queue on real SQS. `dead_letter_suffix` now defaults to `None` ("not set"); unless `dead_letter_queue` is set
|
|
134
|
+
the runtime asks the transport's new optional `DeadLetterNamer` capability
|
|
135
|
+
(`dead_letter_queue_name(queue, suffix)`, exported from `babelqueue`), which `SqsTransport` implements. The
|
|
136
|
+
runtime also hands it an explicit `dead_letter_suffix`; on a FIFO queue the `.fifo` suffix always stays last,
|
|
137
|
+
an explicit suffix included: `orders.fifo` with `dead_letter_suffix="-x"` is `orders-x.fifo`, never
|
|
138
|
+
`orders.fifo-x` (an SQS FIFO name must end in `.fifo`; before, that explicit suffix produced the uncreatable
|
|
139
|
+
`orders.fifo-x`). An explicit queue name behaves as before; every other transport keeps `<queue>.dlq` (the
|
|
140
|
+
attribute `App.dead_letter_suffix` is `None` instead of `".dlq"` until set). Deployments
|
|
141
|
+
that relied on the SQS default must name the DLQ `<queue>-dlq`, or pass `dead_letter_suffix=".dlq"` /
|
|
142
|
+
`dead_letter_queue=` to keep the old name. An explicitly empty `dead_letter_suffix=""` now counts as "no suffix" on SQS (`-dlq`); before, it made the DLQ the source queue itself.
|
|
143
|
+
|
|
144
|
+
### Added
|
|
145
|
+
- **`babelqueue.transport.NativeSettler`**, an optional `Transport` capability
|
|
146
|
+
(`settle_native(message, reason, description)`). With the DLQ off the runtime calls it instead of
|
|
147
|
+
acknowledging a version-gated message (and, on the poison route, instead of the bounded retry) on
|
|
148
|
+
a transport that offers it; the decision follows the capability, never the transport's name.
|
|
149
|
+
`SqsTransport.settle_native` is a deliberate no-op on the wire; `AsbTransport.settle_native`
|
|
150
|
+
dead-letters natively (falling back to `abandon_message` for a receiver without
|
|
151
|
+
`dead_letter_message`). Transports without it are unchanged.
|
|
152
|
+
- **`babelqueue.transport.Abandoner` and `AsbTransport.abandon`.** When the dead-letter write of a
|
|
153
|
+
*version-gated* message fails, Azure Service Bus now `Abandon`s the peek-lock (broker-bindings 4.5,
|
|
154
|
+
"A failed write `Abandon`s it") so the broker redelivers it at once, instead of leaving it locked until
|
|
155
|
+
the lock lapses (up to 5 minutes). Only that branch abandons (owner decision 2026-10-25): `AsbTransport`
|
|
156
|
+
is deliberately **not** a `Rewinder`, so a graceful-shutdown release and a failed retry re-publish keep
|
|
157
|
+
their previous handling (a copy is re-published and the original completed), and a failed dead-letter
|
|
158
|
+
write of a poison body is still left to its lock. Tests pin all three. The `version_gate_asb` runner
|
|
159
|
+
asserts the `Abandon` on its `unsettled` rows.
|
|
160
|
+
- Conformance `version_gate_*` runners (`version_gate_pulsar` / `version_gate_artemis` through the
|
|
161
|
+
runtime, `version_gate_sqs` / `version_gate_asb` through the real transports) and their entries in
|
|
162
|
+
the results writer.
|
|
163
|
+
|
|
12
164
|
## [1.23.0] - 2026-10-25
|
|
13
165
|
|
|
14
166
|
Implements ADR-0036 (post-R1 parity) decisions 2, 4, 5 and 6. A MINOR: every change is a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: babelqueue
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.25.0
|
|
4
4
|
Summary: Polyglot Queues, Simplified — the Python core: the canonical BabelQueue wire-envelope codec, contracts and dead-letter helpers.
|
|
5
5
|
Project-URL: Homepage, https://babelqueue.com
|
|
6
6
|
Project-URL: Source, https://github.com/BabelQueue/babelqueue-python
|
|
@@ -212,8 +212,13 @@ untrimmed; Azure Service Bus and Artemis also accept the integer `1`). A message
|
|
|
212
212
|
anything else is not decoded: it reaches the runtime on the reserved route
|
|
213
213
|
`babelqueue:unsupported-schema-version:<v>` (the version percent-escaped) and is **never
|
|
214
214
|
retried** — it goes straight to the dead-letter queue (the incoming body, byte-identical,
|
|
215
|
-
with no `dead_letter` block) when `dead_letter` is enabled
|
|
216
|
-
|
|
215
|
+
with no `dead_letter` block) when `dead_letter` is enabled (or `on_unknown_urn=DEAD_LETTER`), and
|
|
216
|
+
`on_unknown_urn=DELETE` drops it, whatever `max_attempts` is. With `dead_letter` off, **SQS and Azure
|
|
217
|
+
Service Bus no longer drop it** (ADR-0037): it is left to the broker's native mechanism and logged
|
|
218
|
+
(SQS: no `DeleteMessage`, so visibility expiry redelivers it — the gate rejects it again — and the
|
|
219
|
+
queue's `RedrivePolicy` moves it to the native DLQ after `maxReceiveCount`; Azure Service Bus: an
|
|
220
|
+
explicit native `DeadLetter` with reason `unsupported schema version` into `$DeadLetterQueue`
|
|
221
|
+
instead of `Complete`). Kafka, Artemis and Pulsar still acknowledge and drop it. If the dead-letter write fails the message is
|
|
217
222
|
left unacknowledged (an error is logged), is never retried and never reaches a handler; the
|
|
218
223
|
dead-letter write is attempted again when the broker brings it back, which depends on the
|
|
219
224
|
broker:
|
|
@@ -253,10 +258,17 @@ a warning. A **per-message delay** at publish time is
|
|
|
253
258
|
standard queue; a larger value, or any per-message delay on a FIFO queue, raises
|
|
254
259
|
`BabelQueueError` instead of being truncated or dropped).
|
|
255
260
|
|
|
261
|
+
**Poison bodies.** A body that does not decode, or has no identity, is never retried by the SDK
|
|
262
|
+
(ADR-0037): `dead_letter=True` writes the ADR-0035 record to the DLQ (`<queue>-dlq` on SQS, `<queue>.dlq` elsewhere) and then deletes it,
|
|
263
|
+
`on_unknown_urn=DELETE` deletes it, and with the DLQ off it is **not deleted and not re-sent**: the
|
|
264
|
+
transport's `settle_native` capability leaves it to SQS on the first delivery (visibility expiry,
|
|
265
|
+
then the queue's `RedrivePolicy`; the event is logged). Azure Service Bus does the same with a native
|
|
266
|
+
`DeadLetter` (reason `poison`). Every other transport keeps the bounded retry up to `max_attempts`.
|
|
267
|
+
|
|
256
268
|
**Poison-loop risk:** a message that always fails is redelivered at once until
|
|
257
269
|
`max_attempts` is reached. If the receive count is unavailable the SDK cannot
|
|
258
270
|
advance attempts at all. **Configure a native `RedrivePolicy`**
|
|
259
|
-
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue
|
|
271
|
+
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue>-dlq`, the SQS default DLQ name: FIFO `<queue>-dlq.fifo`, because an SQS queue name cannot contain a dot; `dead_letter_queue` overrides it, and `dead_letter_suffix` replaces `-dlq`, placed before `.fifo` on a FIFO queue) on every SQS
|
|
260
272
|
queue as the broker-side backstop, and set a non-zero `retry_backoff` if the
|
|
261
273
|
handler's dependencies need time to recover.
|
|
262
274
|
|
|
@@ -158,8 +158,13 @@ untrimmed; Azure Service Bus and Artemis also accept the integer `1`). A message
|
|
|
158
158
|
anything else is not decoded: it reaches the runtime on the reserved route
|
|
159
159
|
`babelqueue:unsupported-schema-version:<v>` (the version percent-escaped) and is **never
|
|
160
160
|
retried** — it goes straight to the dead-letter queue (the incoming body, byte-identical,
|
|
161
|
-
with no `dead_letter` block) when `dead_letter` is enabled
|
|
162
|
-
|
|
161
|
+
with no `dead_letter` block) when `dead_letter` is enabled (or `on_unknown_urn=DEAD_LETTER`), and
|
|
162
|
+
`on_unknown_urn=DELETE` drops it, whatever `max_attempts` is. With `dead_letter` off, **SQS and Azure
|
|
163
|
+
Service Bus no longer drop it** (ADR-0037): it is left to the broker's native mechanism and logged
|
|
164
|
+
(SQS: no `DeleteMessage`, so visibility expiry redelivers it — the gate rejects it again — and the
|
|
165
|
+
queue's `RedrivePolicy` moves it to the native DLQ after `maxReceiveCount`; Azure Service Bus: an
|
|
166
|
+
explicit native `DeadLetter` with reason `unsupported schema version` into `$DeadLetterQueue`
|
|
167
|
+
instead of `Complete`). Kafka, Artemis and Pulsar still acknowledge and drop it. If the dead-letter write fails the message is
|
|
163
168
|
left unacknowledged (an error is logged), is never retried and never reaches a handler; the
|
|
164
169
|
dead-letter write is attempted again when the broker brings it back, which depends on the
|
|
165
170
|
broker:
|
|
@@ -199,10 +204,17 @@ a warning. A **per-message delay** at publish time is
|
|
|
199
204
|
standard queue; a larger value, or any per-message delay on a FIFO queue, raises
|
|
200
205
|
`BabelQueueError` instead of being truncated or dropped).
|
|
201
206
|
|
|
207
|
+
**Poison bodies.** A body that does not decode, or has no identity, is never retried by the SDK
|
|
208
|
+
(ADR-0037): `dead_letter=True` writes the ADR-0035 record to the DLQ (`<queue>-dlq` on SQS, `<queue>.dlq` elsewhere) and then deletes it,
|
|
209
|
+
`on_unknown_urn=DELETE` deletes it, and with the DLQ off it is **not deleted and not re-sent**: the
|
|
210
|
+
transport's `settle_native` capability leaves it to SQS on the first delivery (visibility expiry,
|
|
211
|
+
then the queue's `RedrivePolicy`; the event is logged). Azure Service Bus does the same with a native
|
|
212
|
+
`DeadLetter` (reason `poison`). Every other transport keeps the bounded retry up to `max_attempts`.
|
|
213
|
+
|
|
202
214
|
**Poison-loop risk:** a message that always fails is redelivered at once until
|
|
203
215
|
`max_attempts` is reached. If the receive count is unavailable the SDK cannot
|
|
204
216
|
advance attempts at all. **Configure a native `RedrivePolicy`**
|
|
205
|
-
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue
|
|
217
|
+
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue>-dlq`, the SQS default DLQ name: FIFO `<queue>-dlq.fifo`, because an SQS queue name cannot contain a dot; `dead_letter_queue` overrides it, and `dead_letter_suffix` replaces `-dlq`, placed before `.fifo` on a FIFO queue) on every SQS
|
|
206
218
|
queue as the broker-side backstop, and set a non-zero `retry_backoff` if the
|
|
207
219
|
handler's dependencies need time to recover.
|
|
208
220
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "babelqueue"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.25.0"
|
|
8
8
|
description = "Polyglot Queues, Simplified — the Python core: the canonical BabelQueue wire-envelope codec, contracts and dead-letter helpers."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -15,6 +15,7 @@ from . import dead_letter, gdpr, headers, idempotency, outbox, redrive, replay
|
|
|
15
15
|
from .app import BabelQueue
|
|
16
16
|
from .codec import SCHEMA_VERSION, SOURCE_LANG, EnvelopeCodec
|
|
17
17
|
from .contracts import HasTraceId, PolyglotMessage
|
|
18
|
+
from .dead_letter import DeadLetterEvent, DeadLetterObserver, NativeDeadLetterSettler
|
|
18
19
|
from .gdpr import Cipher, protect, unprotect
|
|
19
20
|
from .headers import headers_from_context
|
|
20
21
|
from .idempotency import IdempotencyStore, InMemoryStore
|
|
@@ -30,19 +31,23 @@ from .exceptions import BabelQueueError, DecryptError, UnknownUrnError
|
|
|
30
31
|
from .replay import HEADER_REPLAY_BYPASS, bypass_external_effects, is_replay
|
|
31
32
|
from .routing import UnknownUrnStrategy
|
|
32
33
|
from .transport import (
|
|
34
|
+
Abandoner,
|
|
33
35
|
BatchPopper,
|
|
36
|
+
DeadLetterNamer,
|
|
34
37
|
DeadLetterWriter,
|
|
35
38
|
HeaderPublisher,
|
|
36
39
|
InMemoryTransport,
|
|
40
|
+
NativeSettler,
|
|
37
41
|
ReceivedMessage,
|
|
38
42
|
Redeliverer,
|
|
43
|
+
RedrivePublisher,
|
|
39
44
|
Rewinder,
|
|
40
45
|
RoutedPublisher,
|
|
41
46
|
SettlePublisher,
|
|
42
47
|
Transport,
|
|
43
48
|
)
|
|
44
49
|
|
|
45
|
-
__version__ = "1.
|
|
50
|
+
__version__ = "1.25.0"
|
|
46
51
|
|
|
47
52
|
__all__ = [
|
|
48
53
|
"BabelQueue",
|
|
@@ -55,10 +60,17 @@ __all__ = [
|
|
|
55
60
|
"Transport",
|
|
56
61
|
"InMemoryTransport",
|
|
57
62
|
"ReceivedMessage",
|
|
63
|
+
"Abandoner",
|
|
58
64
|
"BatchPopper",
|
|
65
|
+
"DeadLetterNamer",
|
|
59
66
|
"DeadLetterWriter",
|
|
60
67
|
"HeaderPublisher",
|
|
68
|
+
"NativeSettler",
|
|
69
|
+
"NativeDeadLetterSettler",
|
|
70
|
+
"DeadLetterEvent",
|
|
71
|
+
"DeadLetterObserver",
|
|
61
72
|
"Redeliverer",
|
|
73
|
+
"RedrivePublisher",
|
|
62
74
|
"Rewinder",
|
|
63
75
|
"RoutedPublisher",
|
|
64
76
|
"SettlePublisher",
|