mirror of
https://git.ugnet.gay/CrossTalk/azul.git
synced 2026-05-27 14:49:50 +00:00
init
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
def main() -> None:
|
||||
import run_all
|
||||
run_all.main(devmode = True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Webconsole</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new App('{{ wsurl }}');
|
||||
});
|
||||
|
||||
class App {
|
||||
constructor(wsurl) {
|
||||
this.ws = new WebSocket(wsurl);
|
||||
this.ws.onmessage = this._onWSMessage.bind(this);
|
||||
this.$input = document.getElementById('input');
|
||||
this.$output = document.getElementById('output');
|
||||
this.$input.addEventListener('keypress', this._onKeyPress.bind(this));
|
||||
this.hist = [];
|
||||
this.histIndex = 0;
|
||||
}
|
||||
|
||||
_onWSMessage(evt) {
|
||||
this.appendOutput(evt.data, 'out');
|
||||
}
|
||||
|
||||
appendOutput(text, cls) {
|
||||
const $elm = document.createElement('p');
|
||||
$elm.textContent = text;
|
||||
if (cls) $elm.setAttribute('class', cls);
|
||||
this.$output.appendChild($elm);
|
||||
this.$output.scrollTop = this.$output.scrollHeight;
|
||||
}
|
||||
|
||||
_onKeyPress(evt) {
|
||||
if (evt.shiftKey) return;
|
||||
const k = evt.keyCode;
|
||||
const $input = this.$input;
|
||||
if (k == 13) {
|
||||
const text = $input.value;
|
||||
$input.value = '';
|
||||
this.appendOutput(text, 'in');
|
||||
this.ws.send(text);
|
||||
|
||||
this.hist.push(text);
|
||||
if (this.hist.length > 150) {
|
||||
this.hist.splice(0, this.hist.length - 100);
|
||||
}
|
||||
this.histIndex = this.hist.length;
|
||||
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
} else if (k == 38) {
|
||||
this.histIndex -= 1;
|
||||
if (this.histIndex < 0) {
|
||||
this.histIndex = this.hist.length - 1;
|
||||
}
|
||||
if (this.histIndex >= 0) {
|
||||
$input.value = this.hist[this.histIndex];
|
||||
} else {
|
||||
$input.value = '';
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
} else if (k == 40) {
|
||||
this.histIndex += 1;
|
||||
if (this.histIndex >= this.hist.length) {
|
||||
this.histIndex = 0;
|
||||
}
|
||||
if (this.histIndex < this.hist.length) {
|
||||
$input.value = this.hist[this.histIndex];
|
||||
} else {
|
||||
$input.value = '';
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="output"></div>
|
||||
<textarea id="input"></textarea>
|
||||
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
margin: 1vh;
|
||||
background: #999999;
|
||||
}
|
||||
#output {
|
||||
height: 82vh;
|
||||
background: #DDDDDD;
|
||||
border: 1px solid #000000;
|
||||
padding: 0.3em;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
overflow: scroll;
|
||||
}
|
||||
#input {
|
||||
height: 15vh;
|
||||
width: 100%;
|
||||
}
|
||||
#output p {
|
||||
margin: 0em 0em 0.2em;
|
||||
padding: 0.2em 0.3em;
|
||||
}
|
||||
#output .in {
|
||||
font-weight: bold;
|
||||
}
|
||||
#output .in::before {
|
||||
color: #666666;
|
||||
content: "\3C \3C \3C ";
|
||||
}
|
||||
#output p:hover {
|
||||
background: #99CC99;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,121 @@
|
||||
from typing import Dict, Set, Tuple, Any, List, Optional
|
||||
import asyncio, sys, io, cProfile, pstats, traceback, util.misc
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.web import WSMsgType
|
||||
|
||||
from core.backend import Backend
|
||||
from core.http import render
|
||||
|
||||
def register(loop: asyncio.AbstractEventLoop, backend: Backend, http_app: web.Application) -> None:
|
||||
util.misc.add_to_jinja_env(http_app, 'dev', 'dev/tmpl')
|
||||
|
||||
http_app['webconsole'] = Webconsole(loop, backend)
|
||||
|
||||
http_app.router.add_get('/dev', handle_index)
|
||||
http_app.router.add_get('/dev/ws', handle_websocket)
|
||||
|
||||
async def handle_index(req: web.Request) -> web.Response:
|
||||
return render(req, 'dev:index.html', { 'wsurl': 'ws://localhost/dev/ws' })
|
||||
|
||||
async def handle_websocket(req: web.Request) -> web.StreamResponse:
|
||||
webconsole: Webconsole = req.app['webconsole']
|
||||
|
||||
ws = web.WebSocketResponse()
|
||||
await ws.prepare(req)
|
||||
|
||||
webconsole.websocks.add(ws)
|
||||
await ws.send_str("Webconsole on.")
|
||||
|
||||
async for msg in ws:
|
||||
type = msg.type
|
||||
data = msg.data
|
||||
if type == WSMsgType.ERROR:
|
||||
print("ws error with exception {}".format(ws.exception()))
|
||||
continue
|
||||
if type == WSMsgType.TEXT:
|
||||
data = data.strip()
|
||||
if data:
|
||||
ret = webconsole.run(data)
|
||||
if ret:
|
||||
await ws.send_str(ret)
|
||||
continue
|
||||
print("ws unknown", type, data[:30])
|
||||
|
||||
webconsole.websocks.remove(ws)
|
||||
|
||||
return ws
|
||||
|
||||
class Webconsole:
|
||||
__slots__ = ('loop', 'locals', 'objs', 'websocks', 'i')
|
||||
|
||||
loop: asyncio.AbstractEventLoop
|
||||
locals: Dict[str, object]
|
||||
objs: Dict[object, str]
|
||||
websocks: Set[web.WebSocketResponse]
|
||||
i: int
|
||||
|
||||
def __init__(self, loop: asyncio.AbstractEventLoop, backend: Backend) -> None:
|
||||
self.loop = loop
|
||||
self.locals = {
|
||||
'_': None,
|
||||
'be': backend,
|
||||
'dir': useful_dir,
|
||||
'dirfull': dir,
|
||||
'prof': profile,
|
||||
}
|
||||
self.objs = {}
|
||||
self.websocks = set()
|
||||
self.i = 0
|
||||
|
||||
backend._dev = self
|
||||
|
||||
def run(self, cmd: str) -> str:
|
||||
tmp = io.StringIO()
|
||||
sys.stdout = tmp
|
||||
try:
|
||||
self.locals['_'] = exec(compile(cmd + '\n', '<stdin>', 'single'), None, self.locals) # type: ignore
|
||||
except:
|
||||
(exctype, excvalue, tb) = sys.exc_info() # type: Tuple[Any, Any, Any]
|
||||
ret = '\n'.join(traceback.format_exception(exctype, excvalue, tb))
|
||||
else:
|
||||
ret = tmp.getvalue()
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
return ret
|
||||
|
||||
def connect(self, obj: object) -> None:
|
||||
varname = 'k{}'.format(self.i)
|
||||
self.i += 1
|
||||
self.objs[obj] = varname
|
||||
self.locals[varname] = obj
|
||||
msg = "# Connect: `{}`".format(varname)
|
||||
for ws in self.websocks:
|
||||
self.loop.create_task(ws.send_str(msg))
|
||||
|
||||
def disconnect(self, obj: object) -> None:
|
||||
varname = self.objs.pop(obj)
|
||||
msg = "# Disconnect: `{}`".format(varname)
|
||||
self.locals.pop(varname, None)
|
||||
for ws in self.websocks:
|
||||
self.loop.create_task(ws.send_str(msg))
|
||||
|
||||
_PROFILE: Optional[cProfile.Profile] = None
|
||||
|
||||
def profile(*restrictions: Any) -> None:
|
||||
global _PROFILE
|
||||
if _PROFILE is None:
|
||||
_PROFILE = cProfile.Profile()
|
||||
_PROFILE.enable()
|
||||
print("Profiling ON")
|
||||
return
|
||||
_PROFILE.disable()
|
||||
ps = pstats.Stats(_PROFILE).sort_stats('cumulative')
|
||||
ps.print_stats(*restrictions)
|
||||
_PROFILE = None
|
||||
|
||||
def useful_dir(*args: Any) -> List[str]:
|
||||
return [
|
||||
x for x in dir(*args)
|
||||
if not x.endswith('__')
|
||||
]
|
||||
Reference in New Issue
Block a user