Skip to content

Add wsgi.py analogous to asgi.py#145

Open
hoodmane wants to merge 2 commits into
cloudflare:mainfrom
hoodmane:hoodmane/wsgi
Open

Add wsgi.py analogous to asgi.py#145
hoodmane wants to merge 2 commits into
cloudflare:mainfrom
hoodmane:hoodmane/wsgi

Conversation

@hoodmane

@hoodmane hoodmane commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that we could add more features to these gateway servers, I think it is better to put it in the subdirectory following the regular packages structure (same for asgi.py). We can still have an asgi.py in the top-level directory which just imports from this directory for backward compatibility.

return True

def _fill(self) -> None:
from pyodide.ffi import run_sync

@ryanking13 ryanking13 Jul 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we guard against Pyodide 0.26.0a2 which doesn't support run_sync?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do guard.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I assume you mean 0.26?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I assume you mean 0.26?

oh yeah right.

Comment thread packages/runtime-sdk/src/wsgi.py Outdated
def close(self) -> None:
if not self.closed:
try:
self._reader.releaseLock()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that workerd raises error when there is a pending read. It is in the ReadableStream spec, but it seems the behavior is different in Node.js / workerd (so I believe AI agents are not very good at it).

so probably this function should make sure there is no pending read by calling run_sync(await self._reader.cancel() first.

Also, we need to make sure the lock is released when close is not called.

def _can_stream() -> bool:
"""Return True if we can lazily stream the body via run_sync"""
try:
from pyodide.ffi import can_run_sync

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be always true in workerd with Pyodide >= 0.28.2, otherwise it will not exist at all.


def build_environ(
req: "Request | js.Request",
env: Any,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe give dict[str, Any] type?

"SCRIPT_NAME": "",
"PATH_INFO": path_info,
"QUERY_STRING": query_string,
"SERVER_NAME": url.hostname or "localhost",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this ever be empty?

"wsgi.run_once": False,
# Cloudflare-specific extension so handlers can reach bindings, mirroring
# the `scope["env"]` convention used by asgi.py.
"workers.env": env,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(No action required for this PR) We should properly document this later and never change.

Comment thread packages/runtime-sdk/src/wsgi.py Outdated
raise AssertionError("start_response() called more than once")

response_state["status"] = status
response_state["headers"] = list(response_headers)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to convert to list?

Comment thread packages/runtime-sdk/src/wsgi.py Outdated

result = app(environ, start_response)
try:
body_chunks = list(write_buffer)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already a list.

Comment thread packages/runtime-sdk/src/wsgi.py Outdated
raise RuntimeError("The WSGI application did not call start_response()")

return _make_js_response(
response_state["status"], response_state["headers"], b"".join(body_chunks)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says that the write_buffer is only used in the legacy frameworks but it seems we are always using it here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this would store all the data in the memory. I think we should find a way to properly stream it

@ryanking13

ryanking13 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Thanks for working on this. I think we need a proper testing strategy with enough coverage before merging this, as we release every commit. Otherwise, we can create a temporary branch that we can incrementally improve until we are confident enough to be merged to main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants