diff options
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/tui')
4 files changed, 31 insertions, 23 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/tui/chat.py b/packages/meshbay-cli/src/meshbay_cli/tui/chat.py index e4ea1fe..989ffe2 100644 --- a/packages/meshbay-cli/src/meshbay_cli/tui/chat.py +++ b/packages/meshbay-cli/src/meshbay_cli/tui/chat.py @@ -21,6 +21,7 @@ import os import time from pathlib import Path +from meshbay_common.paths import long_path from rich.segment import Segment from rich.text import Text from textual import events @@ -412,9 +413,10 @@ class ChatRoomScreen(Screen): async def _put(self, arg: str) -> None: path = Path(os.path.abspath(os.path.expanduser(arg))) - if not path.is_file(): + if not os.path.isfile(long_path(path)): self.note(f"! {safe(arg)}: " + ("a folder; /put sends one file." - if path.is_dir() else "no such file."), style="bold red") + if os.path.isdir(long_path(path)) else "no such file."), + style="bold red") return name = path.name if problems := refused_names([name], []): @@ -430,9 +432,9 @@ class ChatRoomScreen(Screen): if found is None: return _, folder = found - size = path.stat().st_size + size = os.stat(long_path(path)).st_size advance, queued = self._progress("↑", name, size) - with open(path, "rb") as fh: + with open(long_path(path), "rb") as fh: def read(offset: int, length: int) -> bytes: fh.seek(offset) return fh.read(length) @@ -475,7 +477,8 @@ class ChatRoomScreen(Screen): style="bold red") return target = Path.cwd() / name.rsplit("/", 1)[-1] - if target.exists() and not target.with_name(target.name + PART_SUFFIX).exists(): + if os.path.exists(long_path(target)) and not os.path.exists( + long_path(target.with_name(target.name + PART_SUFFIX))): self.note(f"! {safe(str(target))} is here already; nothing was downloaded.", style="bold red") return diff --git a/packages/meshbay-cli/src/meshbay_cli/tui/clipboard.py b/packages/meshbay-cli/src/meshbay_cli/tui/clipboard.py index 1a1f0d7..e0bd315 100644 --- a/packages/meshbay-cli/src/meshbay_cli/tui/clipboard.py +++ b/packages/meshbay-cli/src/meshbay_cli/tui/clipboard.py @@ -12,6 +12,7 @@ skipped, never an error. import asyncio import os import shutil +import subprocess import sys TIMEOUT = 3.0 @@ -40,24 +41,25 @@ def _encode(text: str) -> bytes: return ("" + text).encode("utf-16-le") if sys.platform == "win32" else text.encode() -async def _run(cmd: list[str], data: bytes | None) -> bytes | None: +def _run_blocking(cmd: list[str], data: bytes | None) -> bytes | None: if shutil.which(cmd[0]) is None: return None env = {**os.environ, "LC_CTYPE": "UTF-8"} if sys.platform == "darwin" else None try: - proc = await asyncio.create_subprocess_exec( - *cmd, stdin=asyncio.subprocess.PIPE if data is not None else asyncio.subprocess.DEVNULL, - stdout=asyncio.subprocess.PIPE if data is None else asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, env=env) - except OSError: - return None - try: - out, _ = await asyncio.wait_for(proc.communicate(data), TIMEOUT) - except TimeoutError: - proc.kill() - await proc.wait() + proc = subprocess.run( + cmd, input=data, stdin=None if data is not None else subprocess.DEVNULL, + stdout=subprocess.PIPE if data is None else subprocess.DEVNULL, + stderr=subprocess.DEVNULL, env=env, timeout=TIMEOUT, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0)) + except (OSError, subprocess.TimeoutExpired): return None - return (out or b"") if proc.returncode == 0 else None + return (proc.stdout or b"") if proc.returncode == 0 else None + + +async def _run(cmd: list[str], data: bytes | None) -> bytes | None: + # In a thread, not asyncio's subprocesses: on Windows the client runs a + # selector loop (aiortc's ICE needs one), which cannot start a process. + return await asyncio.to_thread(_run_blocking, cmd, data) async def read_text() -> str | None: diff --git a/packages/meshbay-cli/src/meshbay_cli/tui/panes.py b/packages/meshbay-cli/src/meshbay_cli/tui/panes.py index d1e3dd0..417e0c6 100644 --- a/packages/meshbay-cli/src/meshbay_cli/tui/panes.py +++ b/packages/meshbay-cli/src/meshbay_cli/tui/panes.py @@ -13,6 +13,7 @@ import stat from dataclasses import dataclass, field from pathlib import Path +from meshbay_common.paths import long_path from rich.text import Text from textual.app import ComposeResult from textual.binding import Binding @@ -274,7 +275,7 @@ class LocalPane(Pane): def _scan(path: Path) -> list[Row]: rows = [] - with os.scandir(path) as it: + with os.scandir(long_path(path)) as it: for e in it: try: st = e.stat() diff --git a/packages/meshbay-cli/src/meshbay_cli/tui/screens.py b/packages/meshbay-cli/src/meshbay_cli/tui/screens.py index 0306325..39b5df3 100644 --- a/packages/meshbay-cli/src/meshbay_cli/tui/screens.py +++ b/packages/meshbay-cli/src/meshbay_cli/tui/screens.py @@ -14,9 +14,11 @@ the node's operator) or none is, then asked once for the batch. import asyncio import contextlib +import os import threading from pathlib import Path +from meshbay_common.paths import long_path from rich.text import Text from textual.app import ComposeResult from textual.binding import Binding @@ -214,8 +216,8 @@ class MainScreen(Screen): problems.append(str(e)) for p in problems: self.app.notify(p, severity="warning", markup=False) - existing = [t for _, t in plan if t.exists() - and not t.with_name(t.name + PART_SUFFIX).exists()] + existing = [t for _, t in plan if os.path.exists(long_path(t)) + and not os.path.exists(long_path(t.with_name(t.name + PART_SUFFIX)))] overwrite = False if existing: names = ", ".join(safe(t.name) for t in existing[:5]) + ( @@ -229,7 +231,7 @@ class MainScreen(Screen): if not overwrite: plan = [(e, t) for e, t in plan if t not in existing] for d in dirs: - await asyncio.to_thread(d.mkdir, parents=True, exist_ok=True) + await asyncio.to_thread(os.makedirs, long_path(d), exist_ok=True) remote.marks.clear() remote.redraw() session = await self.pool.session(group) @@ -320,7 +322,7 @@ class MainScreen(Screen): self.strip.update_job(job, state="running") lock = threading.Lock() try: - with open(item.local, "rb") as fh: + with open(long_path(item.local), "rb") as fh: def read(offset: int, length: int) -> bytes: with lock: fh.seek(offset) |