Weird behavior when Textual App started under multiprocessing.Process #601
-
Hello there 👋 I was experimenting to implement an app which would have two processes: a "background" one that would actually run the heavy logic of the app, and a "ui" one which would run the Textual app. Both would communicate through an IPC socket. So I started with a simple Textual app and tried to run it with from multiprocessing import Process
from textual.app import App
from textual.widgets import Placeholder
class SimpleApp(App):
async def on_mount(self) -> None:
await self.view.dock(Placeholder(), edge="left", size=40)
await self.view.dock(Placeholder(), Placeholder(), edge="top")
def run():
SimpleApp.run(log="textual.log")
if __name__ == '__main__':
p = Process(target=run)
p.start() The app starts well, but whenever I move the mouse, click somewhere, escape sequences (or whatever there are 🙃) are displayed under the UI: Textual App doesn't seem able to react to those events. My guess is that the console events are catched by the parent process rather than the Textual process, which cause this behavior. Is there something to do to circumvent this? Cheers! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
After digging, I found the source of the problem and the issue. On macOS and Windows, when using Then, the textual/src/textual/drivers/linux_driver.py Lines 27 to 32 in e406458 Therefore, events won't be catched by the Textual app since they will be forwarded to the main Anyway, the solution I found is to subclass the driver so it accepts an arbitrary file descriptor. This way, I can forward the main import functools
import sys
from multiprocessing import Process
from rich.console import Console
from textual._types import MessageTarget
from textual.app import App
from textual.drivers.linux_driver import LinuxDriver
from textual.widgets import Placeholder
class SimpleApp(App):
async def on_mount(self) -> None:
await self.view.dock(Placeholder(), edge="left", size=40)
await self.view.dock(Placeholder(), Placeholder(), edge="top")
class LinuxStdInDriver(LinuxDriver):
def __init__(self, stdin_fd: int, console: Console, target: MessageTarget) -> None:
super().__init__(console, target)
self.fileno = stdin_fd
def run(stdin_fileno: int):
driver_class = functools.partial(LinuxStdInDriver, stdin_fileno)
SimpleApp.run(driver=driver_class, log="textual.log")
if __name__ == '__main__':
stdin_fileno = sys.stdin.fileno()
p = Process(target=run, args=(stdin_fileno,))
p.start() The solution is quite clunky with the current API. Maybe it could be worth to be able to manually set the |
Beta Was this translation helpful? Give feedback.
-
I'm seeing something I think might be similar, although I'm on Linux. I'm not 100% sure it's the same or if its even caused by textual, but I think it is, and I at least want to document the issue even if I migrate it somewhere else later. What I'm seeing is that when I am in an ssh session, and I start tmux, run a textual app, and then detatch from tmux strange sequences of characters are printted across the screen. I can't select anything or interact with the terminal, except for ctrl C or enter which tries to enter the command and it fails. But then it keeps on typing as long as I have focus on the terminal with the ssh session in it. ... Apparently, if I type really fast, I can get a command in. But then the random characters start again, but only if my mouse moves... very strange. I'm thinking textual doing something with stdin in combo with tmux might be the issue, but idk. Not sure how many of these variables can be removed to make a MWE, haven't gotten to that yet. Will post more details as I have them. |
Beta Was this translation helpful? Give feedback.
After digging, I found the source of the problem and the issue. On macOS and Windows, when using
multiprocess.Process
, Python will spawn a new process, which creates new file descriptors, in particular forstdin
.Then, the
LinuxDriver
will be initialized inside the sub-process with this newstdin
, different from the main process:textual/src/textual/drivers/linux_driver.py
Lines 27 to 32 in e406458
Therefore, events won't be catched b…