Skip to content

Commit

Permalink
First rudimentary version of the web UI (#371)
Browse files Browse the repository at this point in the history
  • Loading branch information
achimnol authored Aug 26, 2023
1 parent bee8b7a commit 4d8cd2a
Show file tree
Hide file tree
Showing 37 changed files with 2,862 additions and 865 deletions.
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ trim_trailing_whitespace = false
max_line_length = 0
indent_style = space
indent_size = 3

[*.html]
max_line_length = 0
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,5 @@ Pipfile.lock
.python-version

.DS_Store
._.DS_Store
.vim/
9 changes: 6 additions & 3 deletions aiomonitor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,24 @@

from importlib.metadata import version

from .monitor import MONITOR_TERMUI_PORT # for backward compatibility
from .monitor import (
CONSOLE_PORT,
MONITOR_HOST,
MONITOR_PORT,
MONITOR_WEBUI_PORT,
Monitor,
monitor_cli,
start_monitor,
)
from .termui.commands import monitor_cli

__all__ = (
"Monitor",
"monitor_cli",
"start_monitor",
"monitor_cli",
"MONITOR_HOST",
"MONITOR_PORT",
"MONITOR_TERMUI_PORT",
"MONITOR_WEBUI_PORT",
"CONSOLE_PORT",
)
__version__ = version("aiomonitor")
4 changes: 2 additions & 2 deletions aiomonitor/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import argparse
import asyncio

from .monitor import MONITOR_HOST, MONITOR_PORT
from .monitor import MONITOR_HOST, MONITOR_TERMUI_PORT
from .telnet import TelnetClient


Expand Down Expand Up @@ -31,7 +31,7 @@ def main() -> None:
"-p",
"--port",
dest="monitor_port",
default=MONITOR_PORT,
default=MONITOR_TERMUI_PORT,
type=int,
help="monitor port number",
)
Expand Down
67 changes: 27 additions & 40 deletions aiomonitor/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@


class ConsoleProxy:
# Runs inside the monitored event loop

def __init__(
self,
stdin: Input,
Expand Down Expand Up @@ -49,7 +51,8 @@ async def interact(self) -> None:
await self._closed.wait()
finally:
self._closed.set()
self._conn_writer.write_eof()
if not self._conn_writer.is_closing():
self._conn_writer.write_eof()

async def _handle_user_input(self) -> None:
prompt_session: PromptSession[str] = PromptSession(
Expand Down Expand Up @@ -81,7 +84,7 @@ async def _handle_received(self) -> None:
return
self._stdout.write_raw(buf.decode("utf8"))
self._stdout.flush()
except asyncio.CancelledError:
except (ConnectionResetError, asyncio.CancelledError):
pass
finally:
self._closed.set()
Expand All @@ -93,65 +96,49 @@ async def start(
locals: Optional[Dict[str, Any]],
monitor_loop: asyncio.AbstractEventLoop,
) -> asyncio.AbstractServer:
ui_loop = asyncio.get_running_loop()
done = asyncio.Event()

async def _start(done: asyncio.Event) -> asyncio.AbstractServer:
async def _start() -> asyncio.AbstractServer:
# Runs inside the monitored event loop
def _factory(streams: Any = None) -> aioconsole.AsynchronousConsole:
return aioconsole.AsynchronousConsole(
locals=locals, streams=streams, loop=monitor_loop
)

try:
server = await aioconsole.start_interactive_server(
host=host,
port=port,
factory=_factory,
loop=monitor_loop,
)
finally:
ui_loop.call_soon_threadsafe(done.set)
server = await aioconsole.start_interactive_server(
host=host,
port=port,
factory=_factory,
loop=monitor_loop,
)
return server

console_future = asyncio.run_coroutine_threadsafe(
_start(done),
loop=monitor_loop,
console_future = asyncio.wrap_future(
asyncio.run_coroutine_threadsafe(
_start(),
loop=monitor_loop,
)
)
try:
await done.wait()
finally:
server = console_future.result()
return server
return await console_future


async def close(
server: asyncio.AbstractServer,
monitor_loop: asyncio.AbstractEventLoop,
) -> None:
ui_loop = asyncio.get_running_loop()
done = asyncio.Event()

async def _close(done: asyncio.Event) -> None:
async def _close() -> None:
# Runs inside the monitored event loop
try:
server.close()
await server.wait_closed()
except NotImplementedError:
pass
finally:
ui_loop.call_soon_threadsafe(done.set)

close_future = asyncio.run_coroutine_threadsafe(
_close(done),
loop=monitor_loop,
close_future = asyncio.wrap_future(
asyncio.run_coroutine_threadsafe(
_close(),
loop=monitor_loop,
)
)
try:
# NOTE: If the monitor is interrupted before the aioconsole session is closed,
# run_coroutine_threadsafe() seems to get deadlocked.
# In that case, you may need to send SIGINT multiple times to the
# aiomonitor-running process.
await done.wait()
finally:
close_future.result()
await close_future


async def proxy(sin: Input, sout: Output, host: str, port: int) -> None:
Expand Down
12 changes: 12 additions & 0 deletions aiomonitor/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

import asyncio
from contextvars import ContextVar
from typing import TYPE_CHECKING, TextIO

if TYPE_CHECKING:
from .monitor import Monitor

current_monitor: ContextVar[Monitor] = ContextVar("current_monitor")
current_stdout: ContextVar[TextIO] = ContextVar("current_stdout")
command_done: ContextVar[asyncio.Event] = ContextVar("command_done")
7 changes: 7 additions & 0 deletions aiomonitor/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import annotations


class MissingTask(Exception):
def __init__(self, task_id: str | int) -> None:
super().__init__(task_id)
self.task_id = task_id
Loading

0 comments on commit 4d8cd2a

Please sign in to comment.