Changelog¶
Changelog¶
1.2.0 - 2026-07-17¶
Standard event envelopes and consumer-side delivery guarantees: CloudEvents 1.0 support, metadata+data envelope routing, idempotent consumption, and W3C trace propagation. Additive only — no defaults changed; flat payloads and class-name route keys keep working exactly as before.
Added¶
__message_type__onSQSEvent: set it to decouple the route key from the class name, enabling namespaced/versioned types such ascom.acme.payment.approved.v1on pydantic routes. Own-class only (never inherited); underflexible_matchingan override matches its exact value (no case/format variants).- Dot-path discriminators:
FastSQS(discriminator="metadata.eventType")(andSQSRouter(...)) now traverse nested dicts, making{"metadata": {...}, "data": {...}}envelopes routable. A.in the discriminator always means traversal. CloudEvent[T]: generic pydantic base with the CloudEvents 1.0 attributes (specversion,id,source,type,time,subject,datacontenttype,dataschema) and a typeddata. Spec extension attributes (e.g.traceparent) are preserved viaextra="allow".SkipMessage: raise from a middlewarebeforeor from a handler to ack the record as success without (further) processing — never reported as a batch item failure, never redelivered. Deliberately NOT aFastSQSError, so blanket error handling cannot swallow an ack.IdempotencyMiddleware: consumer-side dedup for at-least-once delivery, keyed by a payload path (key_path="id"by default, dot-paths supported). Three-state acquire: completed duplicates skip, in-flight duplicates fail with the newIdempotencyInProgressError(so SQS redelivers after the in-flight attempt settles), and dead workers' claims expire via the IN_PROGRESS lease. Storage is pluggable behind the structuralIdempotencyStoreprotocol (try_acquire/mark_complete/forget;AcquireResultenum) —InMemoryIdempotencyStoreships for tests/dev.TracingMiddleware: parses W3Ctraceparent/tracestatefrom SQS message attributes (case-insensitive), falling back to top-level payload keys (the CloudEvents extension convention), into a typedTraceContextatctx.state.trace. Malformed values are ignored — tracing never fails a record.
Changed¶
TimingMiddlewarelogsstatus=SKIPPED(instead ofFAILED) for records acked viaSkipMessage.
1.1.5 - 2026-07-13¶
Fixed¶
handler()no longer strips the calling thread of its registered event loop. It previously ran each batch throughasyncio.run(), which closes the loop and unregisters it from the thread on exit; any loop consumer running later in the same process broke (e.g. Mangum serving an API Gateway request after an SQS batch in the same Lambda sandbox raisedRuntimeError: There is no current event loop).handler()now owns a persistent per-thread loop, created once and reused across invocations, and re-registers it withasyncio.set_event_loopon every call, so a foreignasyncio.run()in the same thread cannot strip it permanently. Callinghandler()inside a running loop still raises and points toasync_handler().
1.1.4 - 2026-06-23¶
Docs only — no code change.
Docs¶
- Redesigned the README header: centered title and tagline, a refreshed badge row (now including monthly downloads, the docs build status, and the Contributor Covenant), and a clearer top-level nav (Documentation, Quickstart, Changelog, Source, Issues).
1.1.3 - 2026-06-22¶
Docs & packaging only — no behavior change.
Packaging¶
- Repointed
project_urlsto thefastsqsorg after the repository transfer, and set Documentation to the docs site https://fastsqs.github.io instead of the GitHub README. The 1.1.2 PyPI page still linked to the oldimgabrieldev/fastsqsrepo; this release corrects the sidebar links. - Added a
docsoptional-dependency extra (mkdocs-material,mkdocstrings[python]) for building the documentation site.
Docs¶
- Trimmed the README to a concise overview that links out to the full docs site.
Internal¶
- Annotated the
**datakwargs onLoggingMiddleware.logasAny(typing only).
Tests¶
- The default
pytestrun no longer descends into the opt-intests/awsandtests/integrationtiers, so it stays dependency-free (the AWS tier importsboto3at conftest load). Run them explicitly with--run-aws/--run-integration. Unblocks the tag-driven release workflow's test gate.
1.1.2 - 2026-06-20¶
Docs & packaging only — no code change.
Packaging¶
- Migrated project metadata to PEP 621: a full
[project]table inpyproject.toml, andsetup.pyremoved (MANIFEST.inno longer ships it). - Trimmed
build-system.requiresto["setuptools>=77"]— dropped the runtimepydanticdependency (never used at build time) and the redundantwheel(already provided bysetuptools.build_meta). - License now declared as an SPDX expression (
license = "MIT"+license-files = ["LICENSE"], PEP 639), replacing the legacyLicense :: OSI Approved :: MIT Licenseclassifier. Renders the same on PyPI.
Docs¶
- Removed decorative emojis from the README, the
ordering_with_standard_queuesexample README, and its handler docstrings for a more conventional OSS tone.
1.1.1 - 2026-06-19¶
Docs & packaging only — no code change from 1.1.0.
Docs¶
- Documented the v1.1.0 surface in the README: EventBridge Pipes / bare-list
handler +
is_sqs_event, routers / subrouters / key-value routing + default handler, a "why fastsqs" section, runnable examples, contributing, and accuracy fixes (e.g. thefast-depends>=3,<4pin and the PascalCase FIFO-attribute note).
Packaging¶
- Added
project_urls(Documentation, Changelog, Repository, Issues) so they render in the PyPI sidebar.
1.1.0 - 2026-06-19¶
Added¶
is_sqs_event(event): returnsTruewheneventis an SQS batch — a bare list of records (the shape an EventBridge Pipes target receives) or a dict with aRecordskey (the Lambda SQS event source mapping). Lets a multiplexed Lambda route SQS vs non-SQS events (e.g. API Gateway) by shape.FastSQS.handler/async_handlernow accept a bare list of records (the EventBridge Pipes target shape) in addition to{"Records": [...]}.
Fixed¶
- A bare-list event containing a non-dict element (e.g. a malformed enrichment
array item: a JSON string/number/null) no longer crashes the whole batch with an
uncaught
AttributeError. The offending element is reported as its own batch-item failure and its siblings are processed normally. batchItemFailuresentries are never emitted with an empty-string ornullitemIdentifierwhen a record carries a present-but-empty/NonemessageId. SQS/EventBridge read an empty or null identifier as a whole-batch failure; all failure paths now coalesce the identifier to the"UNKNOWN"sentinel consistently (matching the per-record path).
1.0.0 - 2026-06-19¶
First stable release. fastsqs is now a focused, typed SQS-on-Lambda router: pydantic routing + dependency injection + a middleware system + native partial batch failure (standard + FIFO). SemVer applies from here. This release breaks the 0.x surface deliberately to land a clean v1.
Added¶
- Dependency injection (via
fast-depends): declareDepends(...)params on a handler and@app.route(...)wires them — no@inject. Sub-dependencies and type-checked injection included. - Typed
Context: framework fields are typed attributes (ctx.message_id,ctx.queue_type,ctx.record,ctx.lambda_context,ctx.route_path,ctx.message_type,ctx.fifo_info,ctx.handler_result); arbitrary middleware/handler scratch lives in a separatectx.statenamespace (ctx.state.foo/ctx.state["foo"]). NewStateandFifoInfotypes. QueueType.AUTO(the new default): infers FIFO vs standard from each batch'seventSourceARN(a.fifosuffix means FIFO).fifo_failure_mode("isolate_groups"|"halt_batch"): FIFO failure strategy, replacing theskip_group_on_errorboolean.FastSQSErrorbase exception;BatchFailedError.failuresexposes the failed item ids; exception chaining (raise … from e) preserves causes.SQSTestClientwidened:RecordSpecfor per-record FIFO groups in a batch, rawstr/bytesbodies (to reach the malformed-body path), and publicmake_record/make_eventbuilders infastsqs.testing.py.typedmarker (PEP 561) — annotations now reach downstream type checkers.
Changed (breaking)¶
- Python >= 3.10 (was 3.8).
Contextis a typed object, not adictsubclass.ctx["messageId"]and the other string-key reads/writes are gone; use the typed attributes andctx.statefor scratch. ctx keys are snake_case throughout.queue_typedefaults toAUTO(wasSTANDARD): a FIFO queue is no longer silently processed on the concurrent standard path (which broke ordering).- Single
discriminatorparam replaceskey+message_type_keyon bothFastSQSandSQSRouter(default still"type"). flexible_matchingdefaults toFalse(wasTrue); the UPPER/lower message-type variants are dropped, and a variant collision now raisesValueErrorinstead of warning.- Field normalization no longer uses a bespoke fuzzy normalizer; camelCase is
accepted via Pydantic alias generation (
populate_by_name). kebab-case keys are no longer auto-mapped. - Config renames:
enable_partial_batch_failure→partial_batch_failure;skip_group_on_error: bool→fifo_failure_mode: Literal[...]. - Exceptions renamed and reparented under
FastSQSError:RouteNotFound→RouteNotFoundError,InvalidMessage→InvalidMessageError. partial_batch_failure=FalseRAISESBatchFailedErroron any failure (whole batch redelivered) instead of silently reporting no failures (data loss).LoggingMiddleware(verbose) logsctx.statekeys only — no longer leaks framework/scratch internals into CloudWatch.TimingMsMiddleware→TimingMiddleware.- The
FastSQSconstructor is keyword-only. It also acceptsdebug(defaultFalse) for verbose per-record debug logging viaLoggingMiddleware. - Registering the SAME discriminator value as BOTH a pydantic route and a
key-value route now raises
ValueErrorat decoration time (previously the key-value handler was silently shadowed and unreachable).
Removed (breaking)¶
FastSQSconstructor paramstitle/description/version(FastAPI cargo-cult; never read).FastSQS.set_queue_type()(warm-app mutation footgun — setqueue_typeat construction) and theFastSQS.use()alias (useadd_middleware).SQSRouter.wildcard()(usedefault()) and the unusedSQSRouter.nameparam.SQSRouter'spayload_scopeparam (it was inert — all modes delivered the same payload; subrouters do not narrow it).- Middleware presets:
use_preset,MiddlewarePreset, thepresetsmodule. SQSEvent.from_sqs_record(unused, bypassed validation).- The base
Middleware._app/_logcoupling (middleware no longer reaches back into the app;TimingMiddlewareuses stdliblogging). - Removed from the public API:
RouteEntry,RouteValue,Handler,run_middleware_stack(now_run_middleware_stack), and theProcessingContextTypedDict. - The string-route annotation-sniffing validation branch (use explicit
model=). - (already gone in 0.5) in-process retry/
RetryConfig, the dead-letter / circuit-breaker / parallelization / visibility / queue-metrics / masking middleware, and the legacyrun_middlewaresrunner.
Packaging¶
- Real MIT
LICENSE(was a placeholder stub). Development Status :: 5 - Production/Stable.fast-dependscapped to>=3,<4.MANIFEST.inshipsLICENSE,CHANGELOG.md, andfastsqs/py.typed.
Kept (core)¶
Routing (pydantic + key-value), pydantic validation via SQSEvent, the middleware
before/after hook system with balanced unwind, partial batch failure (standard
+ FIFO), TimingMiddleware, LoggingMiddleware, subrouters, and the
SQSTestClient.