Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
7.0.0 - 2026-05-19¶
Control-flow model redesigned to mirror Python semantics. Breaking for code that relied on Q.return_() propagating to the outermost run() from nested pipelines. See Migrating to 7.0 for the full migration guide.
Added¶
Q.exit_()— new control-flow signal that propagates through everyQboundary and every signal carve-out (except_/finally_/gather/drive_gen); absorbed only at the outermostrun(). Like Python'ssys.exit(). Same lazy callable forms asQ.return_().- §7.5 spec section, §7.4 carve-out table, §3.1 recursion limits clause, §2.5 awaitable-detection contract, §17.5 PEP-479 table, §17.7 drive_gen calling-convention asymmetry.
- Regression tests for engine bug fixes; TDD audit reports under
spec-audit/.
Changed — Breaking¶
Q.return_()returns from the current Q only (Pythonreturnsemantics), not the outermost. Migration: useQ.exit_()for old "exit entire pipeline" behavior.Q.return_()in agatherworker becomes that worker's tuple position element.Q.return_()in adrive_genfnbecomes the pipeline CV; subsequent steps run.Q.return_()inside a nested-Qexcept_/finally_handler is absorbed by the nested Q (handler returns the value); plain callable handlers still raise QuentException.Q.break_()inif_()predicate propagates outward to the nearest iteration scope (was QuentException).
Changed — Non-breaking¶
- §7 Control Flow rewritten end-to-end around the three-signal model.
- §4.2, §5.4, §5.5, §5.6, §5.10, §5.11, §6.1, §6.2, §6.4, §7.2, §7.3, §11.3, §11.4, §11.5, §11.6, §11.7, §13.1, §13.10, §14.1, §16.3, §17.1, §17.3 — spec wording corrections and additions.
Fixed¶
except_(reraise=True)bridge-contract violations on the async path (sync handler bypass; async handler context restoration auto-chained).finally_raising during signal propagation now preserves signal as__context__per Python try/finally semantics.- Concurrent
foreach/foreach_dodiscard logging asymmetry (vs gather). gathertriage_Break/_Returnpriority and incorrectBaseException-over-regulars warning.Q.exit_()in concurrentforeach/foreach_dowas being wrapped as QuentException.- Sync pipeline + async
finally_+ absorbedQ.return_()was re-raising the absorbed signal. - Lazy callable raising a control-flow signal now wraps as QuentException (was leaking raw signal).
Q.exit_()initerate*/flat_iterate*terminals now yields as final item and stops (was leaking raw_Exit).- Async
drive_genQ.return_()raised on awaited fn result. - Async
Q.exit_()outermost absorption.
6.1.1 - 2026-03-22¶
Added¶
Changed¶
Fixed¶
6.1.0 - 2026-03-19¶
Added¶
Changed¶
Fixed¶
6.0.0 - 2026-03-18¶
Changed¶
- Identity makeover: Chain → Q — the core class is now
Q(formerlyChain).ChainExcInfo→QuentExcInfo,ChainIterator→QuentIterator. The.decorator()method is now.as_decorator(). Internal module_chain.pyrenamed to_q.py. All documentation, examples, and tests updated.'chain'/'chaining'kept in pyproject.toml keywords for SEO.
5.3.0 - 2026-03-17¶
Added¶
- Trio and Curio event loop detection --
_has_running_loopnow detects any running async event loop (asyncio, trio, curio) without importing them. Usessys.modulesto check if the library is already loaded, then probes its loop API. Zero overhead when a library is not loaded (~50ns dict lookup).
Fixed¶
- Dual-protocol detection under non-asyncio runtimes --
with_()context manager protocol selection now uses the standard_should_use_async_protocolpath for trio and curio, removing the previoushasattr-based workaround in the async generator. - Missing
predicate_trueimport in benchmark scripts.
5.2.0 - 2026-03-17¶
Added¶
flat_iterate()/flat_iterate_do()-- new flatmap iteration terminals with optionalflushcallback.flat_iterateflattens each element's sub-iterable one level;flat_iterate_dorunsfnas a side-effect, yielding original items. Full sync/async support matchingiterate()behavior.- Bare
with_()--with_()now accepts an optionalfn; the bare form (nofn) uses the context value directly as the pipeline value. RaisesTypeErrorif bare form is used outside iteration. - Deferred
with_in iteration --iterate/iterate_do/flat_iterate/flat_iterate_dodetect the last_WithOplink and defer context manager entry to iteration time. The CM exits in the generator'sfinallyblock, with CM exit ordering before deferredfinally_(). Supports exception info forwarding and suppression semantics.
5.1.0 - 2026-03-17¶
Added¶
- Context API --
_context.pywithContextVar-backed_ctx_set/_ctx_getand copy-on-write dict semantics. Dual instance/class dispatch onQvia_SetDescriptor/_GetDescriptor. - Deferred
finally_()in iteration --iterate()/iterate_do()defer the pipeline'sfinally_()handler to the generator'sfinally:block, ensuring cleanup runs after iteration ends (not before it begins). from_steps()classmethod -- dynamic pipeline construction from a sequence of steps.- Cross-platform CI matrix -- 3 OS x 5 Python versions + free-threaded builds, bandit SAST scanning, release build provenance attestation.
Changed¶
- Concurrency refactoring -- extracted
_make_dispatch()and_create_tasks_py310()into_concurrency.py. ReplacedNullwith_UNPROCESSEDsentinel in concurrent result arrays. ImprovedBaseExceptiontriage to select earliest-index exception. - Engine hardening -- thread-safe execution counter with
Lock(PEP 703 compatibility).kwargs-only dispatch now replaces (not merges) root link build-time args. Added debug logging for control flow signals. - Renamed
_UnpicklableMixinto_UncopyableMixin--Nullpickling now allowed.
Fixed¶
on_step=Nonelookup bug -- added_UNSET_ON_STEPsentinel to fix incorrecton_stepcallback detection.- Async
__exit__during control flow signals -- properly await async__exit__in_with_ops.pywhen control flow signals are raised. - Traceback injection hardening -- guarded against
KeyboardInterrupt/SystemExitduring traceback enhancement. - Documentation fixes -- corrected traceback visualization examples and
Q(callable).run(value)examples.
5.0.0 - 2026-03-16¶
Added¶
- Sync/async bridge contract -- write pipeline code once, run it sync or async automatically. Execution starts synchronously; on the first awaitable result, the engine transitions to async and stays there. Fully sync pipelines have zero async overhead.
Qclass -- fluent pipeline builder with a singly-linked list of steps. Build-time append (O(1)), run-time immutability. Thread-safe execution on fully constructed pipelines, including free-threaded Python (PEP 703).- Core pipeline steps --
.then()for value-transforming steps,.do()for side-effect steps (result discarded, current value passes through),.root()viaQ(v, *args, **kwargs)constructor with run-time override via.run(v). - Calling conventions -- two-rule dispatch applied uniformly across all contexts: (1) explicit args/kwargs suppress current value, (2) default passthrough calls
fn(current_value),fn()if absent, or returns literal as-is. - Iteration operations --
foreach(fn, concurrency=, executor=)transforms each element;foreach_do(fn, concurrency=, executor=)runs side-effects per element, keeping originals. Three-tier execution: sync fast path, mid-operation async transition (hands off the live iterator with partial results), and full async path. - Concurrency --
gather(*fns, concurrency=-1, executor=)fans out multiple functions on the same value, always concurrent.ThreadPoolExecutoron sync path,asyncio.TaskGroup(3.11+) orasyncio.gather(3.10) on async path. Bounded concurrency via positive integer, unbounded via-1. Optional user-providedExecutorfor sync operations. - Context manager integration --
with_(fn)enters current value as a context manager, callsfnwith the context value, replaces pipeline value;with_do(fn)discards result. Supports sync CMs, async CMs, and dual-protocol objects (async preferred when an async event loop is running). - Conditional steps --
if_(predicate).then(fn)/if_(predicate).do(fn)with optionalelse_(v)/else_do(fn). Predicate follows the standard calling convention; omitting predicate tests truthiness of current value. Literal predicates supported. - Error handling -- single
except_(fn, exceptions=, reraise=)andfinally_(fn)per pipeline.except_handler receivesQuentExcInfo(exc, root_value)as current value.finally_always runs, receives root value, return value discarded. Handler failures follow Python'stry/except/finallysemantics. - Control flow signals --
Q.return_(v)for early exit (propagates through nested pipelines to outermost),Q.break_(v)for iteration termination (break value appended to partial results). Both areBaseExceptionsubclasses to bypassexcept Exception. - Iterator output --
iterate(fn=)/iterate_do(fn=)returnQuentIterator, a dual sync/async iterator. Callable for reuse with different run arguments.return_()andbreak_()supported during iteration. - Pipeline composition --
clone()deep-copies the pipeline structure (recursive for nested pipelines) while sharing callables by reference.as_decorator()wraps a pipeline as a function decorator (cloned internally).from_steps(*steps)for dynamic pipeline construction. - Instrumentation --
Q.on_stepclass-level callback with signature(q, step_name, input_value, result, elapsed_ns). Zero overhead when disabled. Subclass-safe viatype(q).on_steplookup. - Traceback enhancement -- synthetic
<quent>frame injection with pipeline visualization and<----error marker on the failing step. Internal frame cleaning. Recursive cleaning of__cause__,__context__, andExceptionGroupsub-exceptions. Exception notes on Python 3.11+.repr()sanitization (ANSI stripping, control character removal, length truncation). - Named pipelines --
.name(label)for traceback identification; renders asQ[label](root)in visualizations and exception notes. - Debug logging -- step-level logging via the
quentlogger with per-execution hex IDs for correlation. Gated byisEnabledFor(DEBUG)for zero cost when disabled. - Environment controls --
QUENT_NO_TRACEBACK=1disables all traceback modifications;QUENT_TRACEBACK_VALUES=0suppresses argument values in visualizations and debug logs. ExceptionGroupsupport -- native on Python 3.11+, polyfill on 3.10 (with.subgroup(),.split(),.derive()). Used bygather()and concurrentforeach/foreach_dowhen multiple workers fail.- Nested pipeline support -- pipelines used as steps follow standard calling conventions; control flow signals propagate across nesting boundaries.
- Security --
copy.copy()/copy.deepcopy()blocked on pipelines and internal objects (useclone()instead). Repr sanitization guards against log injection (CWE-117). - Type safety -- full
mypy --strictcompliance,py.typedmarker (PEP 561), typed public API (Q,QuentExcInfo,QuentIterator,QuentException,__version__). - Python 3.10 through 3.14 support, including free-threaded builds. Zero runtime dependencies on Python 3.11+ (
typing_extensionsrequired only on 3.10). - Build-time validation -- non-callable values with args raise
TypeError, duplicateexcept_/finally_raiseQuentException, pendingif_()without.then()/.do()caught atrun()/as_decorator()/iterate().