feat(python): port dynamic-workflows to Python and add playground example#5
Draft
LuisDuarte1 wants to merge 1 commit into
Draft
feat(python): port dynamic-workflows to Python and add playground example#5LuisDuarte1 wants to merge 1 commit into
LuisDuarte1 wants to merge 1 commit into
Conversation
…mple
Adds a Python port of @cloudflare/dynamic-workflows alongside the existing
JS package, and an interactive browser playground example mirroring
examples/basic.
`packages/dynamic-workflows-py/`:
* Pure-Python `_core` module exporting the envelope helpers
(`_wrap_params`, `_unwrap_params`, `MissingDispatcherMetadataError`),
`dispatcher_binding_impl`, and `dispatch_workflow_core`. No `js` /
`workers` / `pyodide.ffi` imports so the helpers are host-testable.
* `_workerd` module with the workerd/Pyodide-bound layer:
`DynamicWorkflowBinding` (a single `WorkerEntrypoint` subclass minted
by `wrap_workflow_binding`), `create_dynamic_workflow_entrypoint`,
`dispatch_workflow`, and `WrappedWorkflow` / `WrappedInstance` tenant
facades. `create()` returns a plain JS object literal
`{id, status, pause, resume, terminate, restart, sendEvent}` with
methods bound to the underlying JS WorkflowInstance via
`Function.prototype.bind` — sync `.id`, RPC-callable method
handles, no second WorkerEntrypoint class, no factory ceremony for
instance handles.
* `__init__.py` falls back to the `_core`-only surface when imported
outside the Pyodide runtime, so host pytest doesn't need import shims.
* 40 host pytest tests covering envelope wrap/unwrap, the binding-impl
contract (mirrors JS `binding.test.ts`), and the dispatch-core flow
(mirrors JS `entrypoint.test.ts`).
`examples/python/`:
* Interactive playground dispatcher with the same UX as
`examples/basic`: editor + payload + step timeline + status polling.
SSE log streaming is omitted (no Python streaming-tail story yet);
progress is driven by polling `/api/status/:runId`.
* Demonstrates **both** trigger shapes, switchable in the dashboard UI:
- **tenant**: dispatcher RPCs into the tenant's
`Default.start_workflow()`, which calls `env.WORKFLOWS.create()`
from the tenant's own context. Models the multi-tenant SaaS case
where the tenant code is what triggers workflows.
- **direct**: dispatcher calls `wrap_workflow_binding(metadata).create()`
directly. The tenant only needs `TenantWorkflow(WorkflowEntrypoint)`
— no `Default(WorkerEntrypoint)` required.
* Per-run tenant source ships with the dispatcher metadata so workflow
replays survive isolate recycles without a Durable Object.
* Verified end-to-end against `pywrangler dev`: both modes complete the
full Dashboard → dispatcher → tenant → @step.do chain.
Workspace plumbing:
* Repo-root `pyproject.toml` declares a uv workspace with both Python
packages as members.
* `examples/python/pyproject.toml` resolves `dynamic-workflows` via
`[tool.uv.sources] dynamic-workflows = { workspace = true }`. The
`workers-py` and `workers-runtime-sdk` deps are pinned to a fork
commit that patches `pywrangler sync` to honor uv workspaces /
sources (upstream PR cloudflare/workers-py#107). No pre-built
wheels or `find-links` config needed.
* `.gitignore` updated with the standard Python / uv / pywrangler /
pytest / linter artifacts.
76c7e25 to
e8aac9d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NOTE: this is still a very clanker PR, also for workspaces to work in uv I had to patch pywrangler cloudflare/workers-py#107
Adds a Python port of @cloudflare/dynamic-workflows alongside the existing JS package, and an interactive browser playground example mirroring examples/basic.
packages/dynamic-workflows-py/:_coremodule exporting the envelope helpers (_wrap_params,_unwrap_params,MissingDispatcherMetadataError),dispatcher_binding_impl, anddispatch_workflow_core. Nojs/workers/pyodide.ffiimports so the helpers are host-testable._workerdmodule with the workerd/Pyodide-bound layer:DynamicWorkflowBindingandDynamicWorkflowInstanceStub(WorkerEntrypointsubclasses, sinceRpcTargethas no documented Python subclassing path),wrap_workflow_binding,create_dynamic_workflow_entrypoint,dispatch_workflow, andWrappedWorkflow/WrappedInstancetenant facades.__init__.pyfalls back to the_core-only surface when imported outside the Pyodide runtime, so host pytest doesn't need import shims.binding.test.ts), and the dispatch-core flow (mirrors JSentrypoint.test.ts).examples/python/:examples/basic: editor + payload + step timeline + status polling. SSE log streaming is omitted (no Python streaming-tail story yet); progress is driven by polling/api/status/:runId.Default.start_workflow(), which callsenv.WORKFLOWS.create()from the tenant's own context. Models the multi-tenant SaaS case where the tenant code is what triggers workflows.wrap_workflow_binding(metadata).create()directly. The tenant only needsTenantWorkflow(WorkflowEntrypoint)— noDefault(WorkerEntrypoint)required.pywrangler dev: both modes complete the full Dashboard → dispatcher → tenant → @step.do chain.Workspace plumbing:
pyproject.tomldeclares a uv workspace with both Python packages as members.examples/python/pyproject.tomlresolvesdynamic-workflowsvia[tool.uv.sources] dynamic-workflows = { workspace = true }. Theworkers-pyandworkers-runtime-sdkdeps are pinned to a fork commit that patchespywrangler syncto honor uv workspaces / sources (upstream PR feat(pywrangler): honor [tool.uv.sources] and [tool.uv.workspace] via uv export workers-py#107). No pre-built wheels orfind-linksconfig needed..gitignoreupdated with the standard Python / uv / pywrangler / pytest / linter artifacts.