Porting matrix¶
Note
This page is generated from src/textual_wasm/substitutions.py by textual-wasm matrix,
and a test fails when the committed copy drifts from it. Every row was measured inside a real
Pyodide. Pyodide’s own documentation lists four modules as removed that import fine in the
version pinned here.
Porting matrix
What behaves differently under Pyodide 314.0.6. Pyodide’s own documentation lists termios, fcntl, pty and tty as removed; in this version all four import successfully. Every row below carries the snippet used to observe it, and a characterisation test re-runs those snippets inside a real Pyodide, so a runtime change fails the suite instead of quietly making this page wrong.
Run textual-wasm doctor <module:App> to find these in your own source, with a file:line for each.
Silent and wrong
No exception is raised and the behaviour is wrong. This is the class Pyodide’s own documentation does not cover, and the reason this project exists: every other kind of failure announces itself.
Capability |
Detected as |
What happens |
What to do instead |
|---|---|---|---|
|
|
Returns the correct result, but Pyodide’s WebLoop ignores the executor and runs the callable inline on the only thread. Code written to keep a UI responsive freezes the page instead. |
There is no thread to offload to. Make the work a coroutine and await it, or break it into chunks that yield with |
|
|
Blocks for the full duration on the only thread. In a browser the tab stops rendering, stops handling input and stops running timers for that period. |
Use |
|
|
|
Raw TCP does not exist in a browser. Use |
|
|
|
There is no job control. Textual gates this behind |
|
|
Succeeds and changes nothing. The call that follows it is the problem: putting a terminal into cbreak mode is what code does before writing a query escape sequence and reading the terminal’s reply, and no reply ever comes - so the read blocks forever on the only thread there is. Differs between Node and a browser. |
There is no terminal to interrogate. Take the size from the driver, which already has it, and choose rendering modes from a configuration value; there is nothing to ask. |
|
|
In Node it really shells out, via child_process.spawnSync, and returns the exit status. In a browser there is no such branch: it returns 0 and does nothing. The most dangerous entry in this registry - it passes every test run under Node and silently does nothing in production. Differs between Node and a browser. |
There is no shell in a browser. Whatever the command did has to move into Python, or behind a network call to a server that still has one. |
Fatal
Tears down the interpreter. Not a Python exception, so nothing catches it and nothing runs afterwards - including whatever you would have used to report it.
Capability |
Detected as |
What happens |
What to do instead |
|---|---|---|---|
|
|
|
Exit through |
Loud but misleading
Raises, and the message sends you somewhere else - a symptom, a private module, or a generic errno instead of the actual constraint. textual_wasm.diagnostics rewrites exactly these.
Capability |
Detected as |
What happens |
What to do instead |
|---|---|---|---|
|
|
Constructing a Thread succeeds; |
Use asyncio. In Textual specifically, |
|
|
The constructor succeeds and the failure is deferred to the first |
Same as |
|
|
|
There are no processes and no threads. The work has to happen inline. |
|
|
A bare ENOSYS with no mention of fork, WebAssembly or Pyodide. |
WebAssembly has no process model. There is nothing to substitute. |
|
|
Accurate but unhelpful, and buried under roughly forty traceback frames. Pyodide ships an |
Use |
|
|
Raises, because stdin is not a tty. Note the errno: Emscripten uses its own table, so this is 59, not the familiar 25. |
Use |
|
|
With no stdin handler installed, reads hit EOF immediately. In a browser the default handler is |
A TUI should read keys through Textual, not stdin. If you genuinely need a prompt, install one with |
|
|
Raises |
|
|
|
Raises |
A pseudo-terminal needs a kernel. Anything that would drive a child program through one needs a server; there is no in-page substitute. |
|
|
In a browser, |
A page cannot listen for connections; nothing in the sandbox can. An application that wants to be reached from outside needs a server, which is the architecture this project replaces. |
|
|
Works in a browser - Pyodide’s stub calls |
Use |
Absent
The module is not in the build, so it fails as an ordinary ImportError where you imported it. Listed because knowing before you deploy is the point.
Capability |
Detected as |
What happens |
What to do instead |
|---|---|---|---|
|
|
Absent from the build; the import fails at the import site. |
Textual does not use curses, so this usually means a dependency does. There is no replacement. |
Already clear
Raises with a message that says what is wrong and often how to fix it. Recorded so the runtime translator leaves them alone: replacing a good message with a generic one would be a regression.
Capability |
Detected as |
What happens |
What to do instead |
|---|---|---|---|
|
|
Raises immediately with a message that names the constraint exactly. |
Nothing to substitute; a browser has no processes. The message is already correct, so the runtime translator passes it through untouched. |
|
|
Pyodide patches the error to name the exact fix. Left alone deliberately. |
Load the tzdata package first, then |
What is not here
Things that work, and are commonly assumed not to:
requestsandhttpxwork in a browser. Pyodide patches httpx, and bundledurllib3ships an Emscripten backend that routes through JSPI, a worker, or XHR. CORS applies, and timeouts, certificates and proxies are not controllable.C-extension dependencies are no longer limited to what Pyodide bundles: a package can publish a
pyemscriptenwasm wheel to PyPI andmicropipinstalls it. The inverse is the new constraint - a package that only exists as a Pyodide-bundled native wheel pins you to Pyodide’s version of it.SharedArrayBufferdoes not enable Python threads. It enables the interrupt buffer and urllib3’s streaming worker.@work(thread=True)is unavailable in any configuration of this build.