Я пытаюсь написать веб-приложение Tornado, которое выполняет локальную команду асинхронно, в качестве сопрограммы. Это урезанная пример кода:Запуск asyncio.subprocess.Process from Tornado RequestHandler
#! /usr/bin/env python3
import shlex
import asyncio
import logging
from tornado.web import Application, url, RequestHandler
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
logging.getLogger('asyncio').setLevel(logging.DEBUG)
async def run():
command = "python3 /path/to/my/script.py"
logging.debug('Calling command: {}'.format(command))
process = asyncio.create_subprocess_exec(
*shlex.split(command),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT
)
logging.debug(' - process created')
result = await process
stdout, stderr = result.communicate()
output = stdout.decode()
return output
def run_sync(self, path):
command = "python3 /path/to/my/script.py"
logging.debug('Calling command: {}'.format(command))
try:
result = subprocess.run(
*shlex.split(command),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True
)
except subprocess.CalledProcessError as ex:
raise RunnerError(ex.output)
else:
return result.stdout
class TestRunner(RequestHandler):
async def get(self):
result = await run()
self.write(result)
url_list = [
url(r"/test", TestRunner),
]
HTTPServer(Application(url_list, debug=True)).listen(8080)
logging.debug("Tornado server started at port {}.".format(8080))
IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop')
IOLoop.instance().start()
Когда /path/to/my/script.py
вызывается непосредственно он выполняет, как ожидалось. Кроме того, когда у меня есть TestHandler.get
, реализованный как обычный синхронный метод (см. run_sync
), он выполняется правильно. Однако, при запуске приложения выше и вызова /test
, журнал показывает:
DEBUG:asyncio:Using selector: EpollSelector
DEBUG:asyncio:execute program 'python3' stdout=stderr=<pipe>
DEBUG:asyncio:process 'python3' created: pid 21835
Однако ps
показывает, что процесс повешен:
$ ps -ef | grep 21835
berislav 21835 21834 0 19:19 pts/2 00:00:00 [python3] <defunct>
У меня есть ощущение, что я не реализующее право цикл, или я делаю это неправильно, но все examples Я видел, как использовать asyncio.get_event_loop().run_until_complete(your_coro())
, и я не мог найти многого о объединении асинчо и торнадо. Все предложения приветствуются!
Привет, Бен, спасибо за ответ! Вы указали мне не в одном, а в двух направлениях с вашим ответом, и оба, похоже, работают нормально - один использует «AsyncIOMainLoop» (хотя он должен быть явно закрыт с помощью 'asyncio.get_event_loop(). Close()' at конец), а другой - «Subprocess» Tornado, о котором я не знал и вам нужно изучить немного больше. Документация по последнему не очень полна, хотя, я понимаю, что можно связываться с подпроцессом, используя опцию 'STREAM' из' stdin'/'stdout', это правильно? –
Да, используйте опцию потока для связи с подпроцессом. –