Skip to content

Commit

Permalink
add 'tanco share' and ui shell
Browse files Browse the repository at this point in the history
  • Loading branch information
tangentstorm committed Jan 27, 2024
1 parent 81c3865 commit 67c5d87
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 13 deletions.
37 changes: 37 additions & 0 deletions tanco/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

observers: dict[str, list[asyncio.Queue]] = {}

clients: dict[str, asyncio.Queue] = {}


# == sessions =================================================

Expand Down Expand Up @@ -224,6 +226,7 @@ async def show_attempt(code, uid):
@app.websocket('/a/<code>/live')
# TODO: @require_uid (raises RuntimeError: Not within a request context)
async def attempt_live(code):
"""this is for browsers to interact with the server"""
ws = quart.websocket
q = asyncio.Queue()
global observers
Expand All @@ -237,6 +240,40 @@ async def attempt_live(code):
observers[code].remove(q)


@app.websocket('/a/<code>/share')
# TODO: @require_uid (raises RuntimeError: Not within a request context)
async def attempt_share(code):
"""this is for the command line client to interact with the server"""
ws = quart.websocket
q = asyncio.Queue()
global clients
clients[code] = q
try:
while True:
cmd = await q.get()
print('sending cmd to ws:', cmd)
await ws.send(cmd)
ws_res = await ws.receive()
print('ws_res:', ws_res)
await notify(code, f'<pre id="shell-output">{ws_res}</pre>', wrap=False)
except asyncio.CancelledError:
clients.pop(code)


@app.route('/a/<code>/shell', methods=['POST'])
@require_uid
async def attempt_shell(code, uid):
frm = await quart.request.form
cmd = frm.get('cmd')
if not cmd:
return 'no cmd given', 400
if q := clients.get(code):
await q.put('send ' + cmd)
else:
return 'no client connected', 400
return "ok"

Check failure on line 274 in tanco/app.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/app.py:274:12: Q000 Double quotes found but single quotes preferred


@platonic('/a/<code>/t/<name>', 'test.html')
@require_uid
async def show_test(**kw):
Expand Down
19 changes: 14 additions & 5 deletions tanco/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ async def ws_talk(self, ws: w.WebSocketCommonProtocol):
if end_cmd in msg:
await ws.send("WARNING: ';' in message, ignoring")
continue
print('RECV:', msg)
toks = shlex.split(msg)
if toks[0] == 'send':
if toks and toks[0] == 'send':
cmd = shlex.join(toks[1:])
tgt.stdin.write(cmd + f'\n{end_cmd}\n')
tgt.stdin.flush()
Expand All @@ -287,22 +288,30 @@ async def ws_talk(self, ws: w.WebSocketCommonProtocol):
res = "RECV: " + msg

Check failure on line 288 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:288:23: Q000 Double quotes found but single quotes preferred
await ws.send(res)

def do_share(self, arg):
def do_share(self, _arg):
"""hand control of your working directory over to the tanco server"""
print("TODO: implement `tanco share`")
code = runner.load_config().attempt

async def share():
url = self.client.url.replace('http', 'ws', 1) + f"a/{code}/share"

Check failure on line 296 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:296:62: Q000 Double quotes found but single quotes preferred
print(f"connecting to {url}")

Check failure on line 297 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:297:19: Q000 Double quotes found but single quotes preferred
ws = await w.connect(url)
await self.ws_talk(ws)

asyncio.run(share())

def do_bind(self, arg):
"""serve target program on a given port (default 1234)"""
port = int(arg) if arg else 1234

async def serve():
async def bind():
async with w.serve(self.ws_talk, "localhost", port):

Check failure on line 308 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:308:46: Q000 Double quotes found but single quotes preferred
print("serving websocket on port", port)

Check failure on line 309 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:309:23: Q000 Double quotes found but single quotes preferred
print("you can talk to it with:")

Check failure on line 310 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:310:23: Q000 Double quotes found but single quotes preferred
print(f"python -m websockets ws://localhost:{port}/")

Check failure on line 311 in tanco/driver.py

View workflow job for this annotation

GitHub Actions / test

Ruff (Q000)

tanco/driver.py:311:23: Q000 Double quotes found but single quotes preferred
await asyncio.Future()

asyncio.run(serve())
asyncio.run(bind())

@staticmethod
def do_test(arg):
Expand Down
7 changes: 4 additions & 3 deletions tanco/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,16 @@ a:hover { color: #0162c2ff; background: #f3f0e5; }
text-align: left;
border: solid #333 1px; padding: 4px; position:absolute }

#shell.hidden { display: none }
#shell {
--shell-width: 500px;
background: #999; border: solid #333 1px;
padding: 10px; margin: 10px;
width: calc(var(--shell-width) + 20px); }
width: calc(var(--shell-width) + 10px); }
#shell-output { background:#eee; height: 256px;
var(--shell-width); height: 10lh;
width: var(--shell-width); height: 22lh;
overflow-y: scroll; overflow-x: auto; }
#shell [name="input"] { width: calc(var(--shell-width) - 80px); }
#shell [name="cmd"] { width: calc(var(--shell-width) - 60px); }
#shell button { width: 50px; }

.diff pre { display: block; margin: 0; padding: 0; }
Expand Down
6 changes: 3 additions & 3 deletions tanco/templates/attempt.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ <h3>passed tests</h3>
{% include "state.html" %}
<div id="shell">
<pre id="shell-output"></pre>
<form hx-post="/a/{{ data.code }}/shell">
<input name="input" type="text"/>
<form hx-post="/a/{{ data.code }}/shell"
hx-swap="none">
<input name="cmd" type="text"/>
<button type="submit">send</button>

</form>
</div>
<div id="test-detail"></div>
Expand Down
3 changes: 3 additions & 0 deletions tanco/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<link rel="stylesheet" href="/static/style.css"/>
<script>
htmx.config.scrollIntoViewOnBoost = false;
function toggleShell() {
document.getElementById('shell').classList.toggle('hidden');
}
</script>
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions tanco/templates/state.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ <h3 style="display: inline-block">state:</h3> {{ data.state.name.lower() }}{% if
Run <code>tanco back</code> on the client to temporarily forget the latest test.<br/>
Requires <code>tanco share</code>.
</span></button>
<button class="has-tooltip">shell
<button class="has-tooltip" type="button" onclick="toggleShell()">shell
<span class="tooltip">
Run <code>tanco back</code> on the client to temporarily forget the latest test.<br/>
Toggles websocket shell for talking to your target program.<br/>
Requires <code>tanco share</code>.
</span></button>
</span>

0 comments on commit 67c5d87

Please sign in to comment.