feat(pywrangler): honor [tool.uv.sources] and [tool.uv.workspace] via uv export#107
Open
LuisDuarte1 wants to merge 1 commit into
Open
feat(pywrangler): honor [tool.uv.sources] and [tool.uv.workspace] via uv export#107LuisDuarte1 wants to merge 1 commit into
LuisDuarte1 wants to merge 1 commit into
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
Author
|
I have read the CLA Document and I hereby sign the CLA |
… uv export When the project's pyproject.toml has either `[tool.uv.sources]` or `[tool.uv.workspace]`, `parse_requirements` now resolves dependencies via `uv export --no-dev --no-editable --no-hashes --no-emit-project` instead of reading `[project.dependencies]` directly. This honors local-path sources and workspace members, so consumers can depend on in-repo Python packages without having to publish them to PyPI or pre-build wheels into a `find-links` directory. Workspace-relative paths emitted by `uv export` (e.g. `./packages/foo`) are rewritten to absolute paths in the requirements file so the inner `uv pip install -r` resolves them regardless of cwd. `_install_requirements_to_vendor` drops `--no-build` when any requirement is a local path, so pure-Python workspace members can be built on the fly into a wheel and vendored into `python_modules/`. Non-workspace deps continue to be resolved from the Pyodide wheel index and `--no-build` is preserved in their case. Behavior is unchanged for projects that don't use sources or workspaces.
d6daf3b to
da951b4
Compare
ryanking13
reviewed
May 19, 2026
Comment on lines
+284
to
+290
| # `--no-build-package` for each *remote* dep instead. | ||
| local_paths = { | ||
| Path(r).resolve() | ||
| for r in requirements | ||
| if Path(r).is_absolute() and Path(r).exists() | ||
| } | ||
| if local_paths: |
Contributor
There was a problem hiding this comment.
This is somewhat tricky part. We don't want to allow users from building non-pure python wheels as they will end up having a native (linux, macos) wheel which cannot run in the worker environment.
But I do agree that we need a way to support local packages for development/workspace purpose. I think we should find a way to combine features such as --no-build, --no-source, --no-build-package to make it work properly.
Contributor
There was a problem hiding this comment.
I had a draft PR for this but didn't make it work (#81)
LuisDuarte1
added a commit
to LuisDuarte1/dynamic-workflows-fork
that referenced
this pull request
May 19, 2026
…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.
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.
When the project's pyproject.toml has either
[tool.uv.sources]or[tool.uv.workspace],parse_requirementsnow resolves dependencies viauv export --no-dev --no-editable --no-hashes --no-emit-projectinstead of reading[project.dependencies]directly. This honors local-path sources and workspace members, so consumers can depend on in-repo Python packages without having to publish them to PyPI or pre-build wheels into afind-linksdirectory.Workspace-relative paths emitted by
uv export(e.g../packages/foo) are rewritten to absolute paths in the requirements file so the inneruv pip install -rresolves them regardless of cwd._install_requirements_to_vendordrops--no-buildwhen any requirement is a local path, so pure-Python workspace members can be built on the fly into a wheel and vendored intopython_modules/. Non-workspace deps continue to be resolved from the Pyodide wheel index and--no-buildis preserved in their case.Behavior is unchanged for projects that don't use sources or workspaces.