Skip to main content

taskflow-meter

ci conformance PyPI Python versions License

Monitoring interfaces — ASGI, WSGI, datasources, transports — for observing OpenStack TaskFlow flow execution progress.

pip install taskflow-meter

What it is for

TaskFlow knows exactly where a flow is — which atoms have run, which are running, how far along each task reports itself — but it offers no way to look at that from outside the process running the flow. taskflow-meter provides that view:

  • Read-only over your existing persistence backend. Task progress is written through to the persistence layer on every update_progress() call, so an existing deployment can be monitored with no changes to flow code.
  • Embeddable anywhere. Ships a hand-rolled ASGI callable and a hand-rolled WSGI callable with no web framework dependency, mountable at any sub-path inside FastAPI, Starlette, Flask, Django, or served standalone.
  • Live, in-process option. Attach a listener plus a per-atom progress tap to an engine for sub-second latency and full DAG topology.

Using it

Point a meter at a taskflow persistence backend and serve it:

from taskflow_meter.api.asgi import ASGIApp
from taskflow_meter.datasource.persistence import PersistenceDataSource
from taskflow_meter.meter import Meter

source = PersistenceDataSource(conf={"connection": "sqlite:///taskflow.db"})
meter = Meter(source)  # polls the backend, keeps an event stream
app = ASGIApp(meter)  # a plain ASGI 3 callable

app runs under any ASGI server (uvicorn module:app), or mounts inside an application you already have:

host.mount("/taskflow", ASGIApp(meter))  # Starlette / FastAPI

There is a WSGI callable too, with identical behaviour -- a conformance suite compares the two byte for byte:

from taskflow_meter.api.wsgi import WSGIApp

application = WSGIApp(meter)  # gunicorn module:application

app.wsgi_app = DispatcherMiddleware(  # Flask / werkzeug
    app.wsgi_app, {"/taskflow": WSGIApp(meter)}
)

Or serve it straight away, with no server dependency at all:

taskflow-meter serve --connection sqlite:///taskflow.db

That runs on wsgiref from the standard library and binds localhost. It is a development server -- put the WSGI callable behind gunicorn for anything real.

For a fleet, run the collector: the flows publish to a broker, one process writes to the meter's own database, and the API workers read it without polling anything.

taskflow-meter upgrade --store-url postgresql://host/meter
taskflow-meter collect --url amqp://broker// --store-url postgresql://host/meter
taskflow-meter serve   --store-url postgresql://host/meter

Inside OpenStack, --transport oslo-messaging puts the events on the notification bus the service is already configured for, instead of opening a broker connection of its own. One caveat for WSGI deployments: a synchronous worker holds a thread for as long as an SSE stream stays open, so use gevent or eventlet workers for streaming, or let clients poll /events?since_seq= instead.

Mounted apps never receive the ASGI lifespan scope, so the meter also starts itself on the first request. If your host application has a lifespan of its own, prefer meter.start() / meter.stop() from it.

Inside a service you already run

If your service composes its WSGI stack from api-paste.ini, dispatch a prefix to the meter and configure it in the service's own config file:

[composite:main]
use = egg:Paste#urlmap
/: your_api
/taskflow-meter: taskflow_meter

[app:taskflow_meter]
paste.app_factory = taskflow_meter.contrib.paste:app_factory
[taskflow_meter]
connection = mysql+pymysql://user:password@host/taskflow

Native adapters exist for the frameworks where the routes should live inside the host application rather than beside it, so its auth and middleware apply to them:

Host Adapter
FastAPI contrib.fastapi.meter_router(meter)
Flask contrib.flask.meter_blueprint(meter)
Django contrib.django.meter_urlpatterns(meter)
Pecan contrib.pecan.MeterController()
paste contrib.paste:app_factory

See the guide for all of them, and for how to read completion and current-task out of the API.

Watching from inside the process running the flow

If you control the code that runs the flow, attaching to the engine gets you what reading persistence cannot: progress readable in single-digit milliseconds instead of a poll interval, and the flow's graph, which taskflow never persists.

from taskflow_meter.collect import attach

with attach(engine) as watched:
    engine.run()
    meter = Meter(watched.store, poll=False)

Delivery happens on its own thread behind a bounded queue, so a task never waits on a publisher, and no failure downstream -- a broken webhook, a full queue, a publisher that raises -- can fail a task.

Endpoints

Path What it returns
GET /healthz Liveness, version, and poller counters
GET /api/v1/flows Flows, newest first, filterable and paged
GET /api/v1/flows/{run_id} One flow with its atoms
GET /api/v1/flows/{run_id}/atoms Just the atoms
GET /api/v1/flows/{run_id}/events Event history from ?since_seq=
GET /api/v1/flows/{run_id}/stream The same events as SSE, live

The stream honours Last-Event-ID, so a dropped connection resumes where it left off rather than leaving a hole. If the datasource keeps no history, the two event endpoints answer 501 and flow payloads omit their links, rather than serving an empty stream that cannot be told apart from silence.

Requirements

  • Python 3.10+
  • taskflow 4.2.0+
  • oslo.config 6.9.0+

Floors are deliberately low: this package is meant to be co-installed into a service whose dependency versions it does not get to choose. They are the oldest release of each library the suite actually passes against, not the oldest that looks plausible -- a lowest-direct CI job installs exactly these and runs the whole suite on them.

Everything else is optional, and only needed by the feature that imports it:

Extra Pulls in Needed for
sqlalchemy SQLAlchemy 1.4+, alembic 1.2+ The collector's own store
amqp kombu 5.1+ Publishing events to a broker
oslo-messaging oslo.messaging 6.0+ Publishing onto the service's own notification bus
all all three

The contrib adapters declare no dependency on their hosts -- a deployment mounting the meter in Django already has Django. They are tested against Django 3.2, Flask 2.3.3, FastAPI 0.100, Pecan 1.4 and PasteDeploy 2.0.

Documentation

docs/guide.md Deploying it: configuration, every host, and how to read completion and current-task out of the API
docs/design.md How it works and why -- what taskflow does and does not record, and the rules the embedding code obeys
docs/releasing.md Cutting a release
CHANGELOG.md What changed

Examples

Runnable, and covered by the test suite so they cannot rot: examples/.

Development

This project builds with Hatch (hatchling + hatch-vcs, so the version comes from git tags) and is developed with uv.

uv run --group dev pytest          # tests
uv run --group dev ruff check .    # lint
uv run --group dev ruff format .   # format
uv run --group dev mypy            # type check
uv build                           # build sdist + wheel

tox is available too, and is what CI reproduces:

uvx tox -e pep8          # ruff, hacking, mypy, and the test-tree check
uvx tox -e py312         # tests on one interpreter
uvx tox                  # the whole matrix

CI also runs the suite with every declared dependency floor installed exactly, which is the only job that checks those floors are real. To reproduce it:

uv lock --python 3.10 --resolution lowest-direct
uv sync --python 3.10 --group dev --all-extras --resolution lowest-direct
uv run --frozen --no-sync pytest

Because the version is derived from git history, a shallow clone or a checkout with no tags will build as 0.0.0. CI checks out with full history.

Where tests go

A unit test module mirrors the module it targets, so finding the tests for a file is mechanical rather than a search:

Module Its tests
taskflow_meter/diff.py tests/unit/test_diff.py
taskflow_meter/datasource/memory.py tests/unit/datasource/test_memory.py

Tests that do not target a single module go in a sibling tree instead -- tests/functional/, tests/integration/ or tests/conformance/. Anything misplaced fails tox -e pep8.

License

Apache-2.0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

taskflow_meter-1.2.0.tar.gz (148.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

taskflow_meter-1.2.0-py3-none-any.whl (108.8 kB view details)

Uploaded Python 3

File details

Details for the file taskflow_meter-1.2.0.tar.gz.

File metadata

  • Download URL: taskflow_meter-1.2.0.tar.gz
  • Upload date:
  • Size: 148.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for taskflow_meter-1.2.0.tar.gz
Algorithm Hash digest
SHA256 344d8dec3d5611425c2f8ee0ee990144da4684c6d82d8ccd0adce8cb0a49479c
MD5 5cab66fc8efd7520506d14d8dc8c1d8c
BLAKE2b-256 c230b616f8e05378e0a0ce0e2490847c57cf2d2b68c5e6f44b607b4f17250a7d

See more details on using hashes here.

Provenance

The following attestation bundles were made for taskflow_meter-1.2.0.tar.gz:

Publisher: release.yml on daipham3213/taskflow.meter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file taskflow_meter-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: taskflow_meter-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 108.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for taskflow_meter-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3af7bad76f2b251d09f6ca864e0a1c5cf005f943afa9d3f9e9edc6623e372493
MD5 27d06424ab35b752cb7c35af6523ba70
BLAKE2b-256 9e9a835a27aaebbc5c84c84b2db33faaa66e85c8c37684acd99684964db80c2b

See more details on using hashes here.

Provenance

The following attestation bundles were made for taskflow_meter-1.2.0-py3-none-any.whl:

Publisher: release.yml on daipham3213/taskflow.meter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.3.0

2 files

This release

1.2.0 This release

2 files

1.1.0

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page