Skip to main content

Winslow

PyPI Python CI License: MIT

A state and workflow management framework with a terminal UI.

Winslow lets you describe work as a set of small, dependency-aware tasks and then run them from a live terminal dashboard — or headless in CI. Its guiding idea is simple: every task knows both how to do its work and how to tell whether that work is already done. Winslow checks the latter before doing the former, so re-running a workflow only does what still needs doing.

If you've ever written a pile of scripts held together with "did this step already run?" checks, Winslow is that pattern, made first-class.


Demo

https://github.com/user-attachments/assets/23426158-0447-421b-ab16-caf4c0d9103e


Install

uv add "winslow[tui]"      # or: pip install 'winslow[tui]'

Requires Python 3.12+. The tui extra pulls in the terminal UI; for headless runs only (cron, CI), the bare winslow package is enough.

Quick start

A workflow is a directory containing a Workflow class and the Task classes that belong to it. The workflow.py filename marks the directory as a workflow package — every .py file next to it belongs to that workflow. Drop this in workflows/etl/workflow.py:

import os
from winslow import Workflow, Task


class Etl(Workflow):
    pass  # name defaults to "etl" (kebab-cased class name)


class DownloadData(Task):
    def run(self):
        # ... fetch the file ...
        download("s3://bucket/raw.csv", "/data/raw.csv")

    def check(self):
        return os.path.exists("/data/raw.csv")


class TransformData(Task):
    dependencies = DownloadData

    def run(self):
        transform("/data/raw.csv", "/data/clean.csv")

    def check(self):
        return os.path.exists("/data/clean.csv")

Then launch the terminal UI from that directory:

winslow run

Or run it headless (handy for cron and CI):

winslow run --mode headless --workflow etl

The core idea: run + check

These two methods are the whole contract:

  • run(self) makes a change to the world.
  • check(self) reports whether the desired end state already holds.

Before running a task, Winslow calls check(). If it's already true, the task is skipped; otherwise run() executes and Winslow calls check() again to confirm success. That makes workflows idempotent and resumable by construction — interrupt one halfway and re-run it, and it picks up where it left off. (Need to force a redo? winslow run --force-run skips the pre-check.)

A task that only verifies state can omit run() entirely — it becomes a read-only check.

Start as a status board, automate later

Because run() is optional, a workflow built only from checks changes nothing — it just reads your systems and reports what's true. That makes adoption incremental:

  1. Describe the runbook you already have as checks — one check() per step, automated or manual. The TUI is now a live status board over your process exactly as it exists today. Zero migration.
  2. Add run() to one task at a time, wherever automation pays off. The rest stay checks, and the workflow is usable at every step in between.

A task can even automate part of its work and leave the rest to a human — see the docs for how that plays out.

A few things you'll probably reach for

Each of these is optional — start with run/check and add the rest as you need it.

  • Dependencies & ordering. Declare dependencies = OtherTask (or a tuple / task-group name). Winslow builds the dependency graph, detects cycles, and runs things in the right order. Mark foundational steps is_premier and cleanup steps is_terminal.

  • Eligibility & guards. Override is_eligible() to skip a task in some environments, or can_run() to block it until a precondition holds. For reusable rules, attach composable constraints:

    class DeployTask(Task):
        runnability_constraints = [BusinessHoursOnly]
    
  • Parameterized tasks. One task class can fan out into many instances via Parameter declarations — e.g. one ProcessRegion task per region — each tracked and run independently.

  • A task filter language — that you can extend. Narrow the view (or a run) with expressions like build,test, !g deploy, or ~lint & !group nightly, in the UI's search box or via --filter on the CLI. Add your own commands by subclassing TaskFilter: define a !command and what it matches, and it's available everywhere filters are.

  • Declarative caching. Share expensive data — station lists, calendars, reference tables — through GlobalCache (process scope) and WorkflowCache (session scope) classes. Declare fields with @entry (lazy, eager, depends_on, ttl); eager fields load in parallel before the graph is built, and JsonFileStorage keeps a cache warm across processes.

  • Error telemetry. Report task and workflow errors to Sentry (winslow[sentry]) or OpenTelemetry (winslow[otel]) by setting their environment values — each error reaches the backends exactly once.

  • Live terminal UI. Watch tasks change state in real time, stream per-task logs, browse execution history, and run or re-check individual tasks by hand — all from the dashboard. Everything also works headless in --mode headless.

  • A pluggable UI. The dashboard and workflow views are assembled from plugins that fill named slots. Add your own tab or panel — or replace a built-in one — by subclassing UIPlugin, no fork required. Both UI plugins and custom filters can be autodiscovered or shipped as separate packages via entry points (winslow.tui_plugins, winslow.filter_plugins).

CLI at a glance

winslow run                                    # interactive terminal UI
winslow run --workflow etl                     # UI, pre-selecting a workflow
winslow run --mode headless --workflow etl     # headless run
winslow run --mode headless --check ...        # check completion without running
winslow run --dry-run ...                      # call dry_run() instead of run()
winslow run --clear-cache ...                  # invalidate every cache entry first
winslow show                                   # list workflows
winslow show --initialize --workflow etl       # initialize one workflow, list its tasks
winslow show --initialize --workflow etl --with-deps   # ...with each task's dependencies

Winslow discovers your workflows from the current directory; defining an Orchestrator subclass to customize global options is optional.

How it compares

Winslow is a local-first workflow tool. There is no server, no scheduler daemon, and no metadata database: done-ness is whatever check() observes right now — a file, a table, an API response, a merged PR — so if the world drifts, the next run sees it. Tasks run where you invoke winslow, and cron or CI is the intended trigger for unattended runs.

If your situation calls for distributed workers, a built-in scheduler, or a central run history, a different tool will serve you better. The tool selector compares Winslow with the common alternatives under the same rules — tick what your situation requires and see what fits.

Trust model

Treat a workflow directory like a Makefile or a conftest.py: running winslow in a directory runs that directory's code, imported at startup before any prompt. Plugin and filter autodiscovery is likewise opt-out — installed packages exposing winslow entry points load on startup; the plugin guide shows how to constrain or allowlist them. The full trust model and the vulnerability reporting process are in SECURITY.md.

Status

Winslow is early (0.x) — the API may still shift between minor versions. Full documentation lives at winslow-workflow.org.

Questions & feedback

Real-world use cases are especially welcome while the API is still settling — what you automate, and what fought you, both shape what 0.x becomes.

License

MIT

Metadata

Release files for winslow 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for winslow 0.5.1
File Size Uploaded Provenance
winslow-0.5.1.tar.gz 146.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for winslow 0.5.1
File Interpreter ABI Platform
winslow-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 342.3 kB

Release files / winslow-0.5.1.tar.gz

Download URL winslow-0.5.1.tar.gz
Size 146.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5c654645bd5d41966c4140e29eb20905b4fa6ff73fa5d4423520fb60c9797326
BLAKE2b-256 checksum
How to use checksums
af7ada1f667796befe3320aca6f4412d8e0edf62f705c04af8d354c1fa501e10
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / winslow-0.5.1-py3-none-any.whl

Download URL winslow-0.5.1-py3-none-any.whl
Size 196.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3fd034be2f0f942e2835be5164a29d1eb602d6e2ed85f120ed03757dc8953cd
BLAKE2b-256 checksum
How to use checksums
860e702f41eeb60718b472aaaf1e7af60a9fdb8581b1760fd8fd05314b856d26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for winslow 0.5.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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