Changelog¶
All notable changes to this project are documented here. The format follows Keep a Changelog and the project adheres to Semantic Versioning.
0.3.0 — 2026-05-05¶
Added¶
GraphObserver — push-model callback interface (db61b14)
- New
GraphObserverbase class ingraph.pywith four no-op methods: on_run_start(total_nodes)— fired once before the first node starts.on_node_start(node_name, step, node_index, total_nodes)— fired just before each node begins processing.on_node_end(node_name, step, node_index, total_nodes)— fired just after each node finishes.on_run_end(succeeded, error)— fired once after the entire run completes.Graph.observerattribute (Noneby default). Assign anyGraphObserversubclass instance before callingrun()to receive callbacks.- Observer callbacks are wired into
_reset,_run_sequential,_run_threaded, andrun(). In threaded mode, node name and index are captured via default arguments so each thread reports correctly. GraphObserverexported fromgraph.__all__and the top-level package__init__.- 16 new tests in
TestGraphObserver(test_graph.py) covering callback order, correct arguments, threaded mode, and base-class no-ops (7816dc5). - "Observer (push model)" subsection added to Patterns & Recipes guide with
a full
LogObserverexample and a thread-safety note (7816dc5).
0.2.0 — 2026-05-05¶
Added¶
ENV — live boolean properties (8d3c2de, 201a3a7)
ENV.THREADED— boolean property;TruewhenYIELDGRAPH_THREADEDis set to a truthy value (1,true,yes). Evaluated at call time, so changes toos.environare reflected immediately (important for tests).ENV.LOG_TRACEBACK— boolean property replacing the removedLOG.TRACEBACKfield; readsYIELDGRAPH_LOG_TRACEBACKat access time.ENV.LOG_DISABLED— new boolean property; setYIELDGRAPH_LOG_DISABLED=1to suppress all library logging output. WhenTrue,LoggingBehavior.log()returns immediately without emitting anything.ENV.THREADED_KEY,ENV.LOG_TRACEBACK_KEY,ENV.LOG_DISABLED_KEY— class attributes exposing raw key strings for directos.environmanipulation (e.g. in tests).ENV.TRUEISH_VALUES— tuple('1', 'true', 'yes')centralising the truthy-string logic used by all three properties.LOG.__getitem__— dictionary-style level lookup:LOG['DEBUG']→10,LOG['TRACE']→5. RaisesKeyErrorfor unknown names.LOG.__call__— reverse lookup by integer:LOG(10)→'DEBUG'. RaisesKeyErrorfor unknown numbers.LOG.*_LEVELnumeric constants —LOG.TRACE_LEVEL(5),LOG.DEBUG_LEVEL(10),LOG.INFO_LEVEL(20),LOG.WARNING_LEVEL(30),LOG.ERROR_LEVEL(40),LOG.CRITICAL_LEVEL(50).
Documentation (70a07f5)
node.pymodule docstring:.. warning::block documenting thatasync defgenerators silently produce zero output andasync defplain functions yield the coroutine object as a value. Points to threaded mode as the correct alternative for I/O concurrency.job.pymodule docstring:.. note::listing supported callable types and the two distinct async failure modes.- Patterns & Recipes guide: new "Async / asyncio — not supported" section
explaining both failure modes with code-level detail, the recommended
YIELDGRAPH_THREADED=1workaround, and anasyncio.run_in_executorhint for callers already inside an event loop.
Changed¶
_ENV_converted from a@dataclass(frozen=True)of string constants to a plain class with@propertyaccessors. All properties reados.environon every access.LOG.TRACEBACKfield removed; useENV.LOG_TRACEBACKinstead.Graph._reset():self._threaded = ENV.THREADEDreplaces the inlineos.environ.get(…).lower() in (…)expression.node.py:LOG.TRACEBACK→ENV.LOG_TRACEBACK; removed now-unusedLOGimport.LoggingBehavior.log()gains an early-return guard:if ENV.LOG_DISABLED: return.LoggingBehavior.name_to_level()removed; level resolution now done viaLOG.__getitem__.test_config.pyrewritten: droppedTestNameToLevel; addedTestLogGetItem,TestLogCall,TestENV(all three properties, truthy/falsy, runtime changes), andTestLoggingBehavior.test_log_disabled_silences_all_output. Test count: 27 → 43.test_graph.py:os.environ[ENV.THREADED]→os.environ[ENV.THREADED_KEY]throughout.- All stale
THREADED_ENV_VARandLOG.TRACEBACKcross-references removed from docstrings inconfig.py,graph.py, anddocs/guides/configuration.md. - Configuration guide
ENVsection rewritten to describe properties and key constants; added quick-reference table.
0.1.0 — 2026-05-04¶
First public release. The library was extracted from a monolithic main.py and
restructured as an installable package under src/yieldgraph/.
Added¶
Core library
Graph— directed graph that owns and executes an ordered collection ofNodeobjects. Supports sequential and threaded execution, cooperative cancellation, and fan-out branching viaadd_chain.Node— single processing step that pulls items from incomingEdgequeues, runs itsJob, and fans results to all outgoing edges. Exposes per-run counters (n_consumed,n_produced,n_queued), aprogressfraction, and a caught-exception list (errors).Job— execution wrapper around any callable. Normalises plain functions and generator functions into a uniform, cancellable generator protocol via the module-level helpers_as_generatorand_wrap.Edge—deque-based directed queue connecting two nodes. Added thread-safeput,get, andclosemethods backed by athreading.Conditionfor use in threaded pipelines.LoggingBehavior— mixin class providing consistentlog,log_trace,log_debug,log_info,log_warning,log_error,log_critical, andlog_exceptionmethods. Automatically switches tologuruwhen available.LOG— frozen dataclass with typed constants for all log levels (TRACE,DEBUG,INFO,WARNING,ERROR,CRITICAL) and theTRACEBACKflag.ENV— frozen dataclass exposing environment variable name constants (YIELDGRAPH_THREADED,YIELDGRAPH_LOG_TRACEBACK).START_NODE_NAME— sentinel string'__START__'that identifies the implicit pipeline entry edge.- Custom
TRACElog level (numeric5) registered with the standardloggingmodule.
Threaded execution mode
Graph._run_threaded— executes all nodes as concurrent daemon threads; seed edges are pre-filled and closed before threads start.Node.process_streaming— streaming variant ofNode.processused in threaded mode; blocks onEdge.getuntil upstream data arrives then closes output edges on completion.Edge.put/Edge.get/Edge.close— thread-safe producer/consumer API.YIELDGRAPH_THREADEDenvironment variable controls execution mode at runtime.
Test suite (168 tests across 4 modules)
tests/test_config.py— 27 tests forLOG,LoggingBehavior, andSTART_NODE_NAME.tests/test_job.py— 27 tests forJob,_as_generator, and_wrapincluding cancellation andrunningflag behaviour.tests/test_node.py— 59 tests forNodeconstruction, properties,process,_run_one,_fan_out, error collection, and cancellation.tests/test_graph.py— 55 tests forGraphconstruction,add_chain, sequential run, threaded run, fan-out, cancellation, and output collection.
Documentation
- MkDocs + Material site with GitHub Pages deployment via CI.
- Landing page with feature grid, Mermaid architecture diagram, and annotated quick-start.
- Getting Started guide — sources, transforms, tuple model, multi-step chains, output consumption.
- Patterns & Recipes guide — fan-out,
attach_to,initial_input, error handling, cooperative cancellation, progress monitoring, threaded mode, node labelling, graph reuse. - Configuration guide — environment variables, log levels,
LoggingBehaviormixin,ENVandLOGconstant objects. - Full API reference auto-generated from docstrings via
mkdocstrings. - CI workflow (
.github/workflows/ci.yml) — runs pytest and deploys docs to GitHub Pages on every push tomain.
Changed¶
- Restructured from a single
main.pyinto a proper Python package undersrc/yieldgraph/with__init__.py,config.py,edge.py,job.py,node.py, andgraph.py. - Renamed internal symbols across
NodeandJobfor clarity (e.g.THREADED_ENV_VAR->ENV.THREADED). - All docstrings rewritten to NumPy style with full parameter, return, and example sections.
Node.processnow accepts explicitedges_in/edges_outlists instead of reading from graph-level attributes.
Fixed¶
_ensure_tuplehelper extracted fromNodeso it can be tested and used independently; fixed edge case where a bare tuple value was double-wrapped.Node._run_onenow correctly setsgraph.cancelledonKeyboardInterruptinstead of re-raising, allowing the pipeline to exit cleanly.Edge.getcorrectly returnsNonewhen the edge is closed and empty, preventing consumer threads from blocking indefinitely.