Working PoC of calling tutor asynchronousl
This is ugly, though, because we are calling tutor via subprocess. Instead, we should be calling cli() in a separate thread.
This commit is contained in:
parent
ee8c9c4362
commit
35554c2cc2
@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
|
|
||||||
# import sys
|
# import sys
|
||||||
# import time
|
# import time
|
||||||
@ -10,10 +11,8 @@ import aiofiles
|
|||||||
# from contextlib import contextmanager
|
# from contextlib import contextmanager
|
||||||
|
|
||||||
import tutor.env
|
import tutor.env
|
||||||
from quart import Quart, render_template, request, websocket
|
from quart import Quart, render_template, request, websocket, redirect, url_for
|
||||||
from tutor import hooks
|
from tutor import hooks
|
||||||
|
|
||||||
# from tutor.commands.cli import cli
|
|
||||||
from tutor.types import Config
|
from tutor.types import Config
|
||||||
|
|
||||||
|
|
||||||
@ -56,6 +55,13 @@ app = Quart(
|
|||||||
|
|
||||||
|
|
||||||
def run(root: str, **app_kwargs: t.Any) -> None:
|
def run(root: str, **app_kwargs: t.Any) -> None:
|
||||||
|
# import module to trigger all the right imports and hooks
|
||||||
|
# TODO how do we handle this now that we call the tutor binary directly? Should we
|
||||||
|
# even deal with hooks at all? We have to to figure out plugin configuration,
|
||||||
|
# information, etc.
|
||||||
|
# pylint: disable=unused-import,import-outside-toplevel
|
||||||
|
from tutor.commands.cli import cli
|
||||||
|
|
||||||
hooks.Actions.CORE_READY.do() # discover plugins
|
hooks.Actions.CORE_READY.do() # discover plugins
|
||||||
hooks.Actions.PROJECT_ROOT_READY.do(root)
|
hooks.Actions.PROJECT_ROOT_READY.do(root)
|
||||||
app.logger.info("Dash tutor logs location: %s", TutorProject.tutor_stdout_path())
|
app.logger.info("Dash tutor logs location: %s", TutorProject.tutor_stdout_path())
|
||||||
@ -95,23 +101,38 @@ async def toggle_plugin(name: str):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
# @app.post("/tutor")
|
@app.post("/tutor/cli")
|
||||||
# async def run_tutor():
|
async def tutor_cli():
|
||||||
# """
|
# Run command asynchronously
|
||||||
# Run an arbitrary tutor command.
|
# TODO parse command from JSON request body
|
||||||
# """
|
# app.add_background_task(subprocess_exec, ["tutor", "config", "printvalue", "DOCKER_IMAGE_OPENEDX"])
|
||||||
# try:
|
# app.add_background_task(subprocess_exec, ["tutor" "local", "launch"])
|
||||||
# with capture_stdout() as stdout:
|
# await subprocess_exec(["tutor", "config", "printvalue", "pouac"])
|
||||||
# # pylint: disable=no-value-for-parameter
|
await subprocess_exec(["tutor", "dev", "dc", "run", "pouac"])
|
||||||
# cli(["config", "printvalue", "DOCKER_IMAGE_OPENEDX"])
|
return redirect(url_for("tutor_logs"))
|
||||||
# except SystemExit as e:
|
|
||||||
# if e.code == 0:
|
|
||||||
# # success!
|
async def subprocess_exec(command: list[str]):
|
||||||
# return {}
|
path = TutorProject.tutor_stdout_path()
|
||||||
# else:
|
# if os.path.exists(path):
|
||||||
# # TODO Return 500?
|
# # TODO return 400? We can't run two commands at the same time
|
||||||
# return {}
|
# return {}
|
||||||
# # TODO
|
with open(path, "w", encoding="utf8") as stdout:
|
||||||
|
# Print command
|
||||||
|
# TODO this doesn't seem to work. For some reason, the command is added at the
|
||||||
|
# bottom of the file!!!
|
||||||
|
stdout.write(f"$ {shlex.join(command)}\n")
|
||||||
|
# Run command
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
*command,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stdout,
|
||||||
|
stdin=asyncio.subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
while proc.returncode is None:
|
||||||
|
await proc.communicate()
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/tutor/logs")
|
@app.get("/tutor/logs")
|
||||||
@ -150,21 +171,3 @@ async def stream_file(path: str) -> t.Iterator[str]:
|
|||||||
yield content
|
yield content
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
# @contextmanager
|
|
||||||
# def capture_stdout():
|
|
||||||
# sys_stdout = sys.stdout
|
|
||||||
# try:
|
|
||||||
# while os.path.exists(TutorProject.tutor_stdout_path()):
|
|
||||||
# # TODO thread-safe, lock-based implementation that does not use sleep()
|
|
||||||
# await asyncio.sleep(0.1)
|
|
||||||
# with open(TutorProject.tutor_stdout_path(), "wb", encoding="utf8") as stdout:
|
|
||||||
# sys.stdout = stdout
|
|
||||||
# sys.stderr = stdout
|
|
||||||
# yield stdout
|
|
||||||
# finally:
|
|
||||||
# if os.path.exists(TutorProject.tutor_stdout_path()):
|
|
||||||
# # TODO more reliable implementation
|
|
||||||
# os.remove(TutorProject.tutor_stdout_path())
|
|
||||||
# sys.stdout = sys_stdout
|
|
||||||
|
|||||||
@ -3,4 +3,9 @@
|
|||||||
{% for plugin in installed_plugins %}
|
{% for plugin in installed_plugins %}
|
||||||
<li><a href="{{ url_for('plugin', name=plugin) }}">{{ plugin }}</a></li>
|
<li><a href="{{ url_for('plugin', name=plugin) }}">{{ plugin }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<form action="{{ url_for('tutor_cli') }}" method="POST">
|
||||||
|
<input type="hidden" name="args[]" value="local">
|
||||||
|
<input type="hidden" name="args[]" value="launch">
|
||||||
|
<button type="submit">Apply changes</button>
|
||||||
|
</form>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user