Drop Python 3.4 and switch to async / await syntax for asyncio. Fixes #1425

This commit is contained in:
grossmj committed 2018-10-15 17:05:49 +07:00
1 parent 8baa480b79
commit 76af98404a
119 files changed
+2432 -3084

No files matched your search

+6 -8
View File
@@ -50,8 +50,7 @@ class Response(aiohttp.web.Response):
self.content_length = None
super().enable_chunked_encoding()
@asyncio.coroutine
def prepare(self, request):
async def prepare(self, request):
if log.getEffectiveLevel() == logging.DEBUG:
log.info("%s %s", request.method, request.path_qs)
log.debug("%s", dict(request.headers))
@@ -61,7 +60,7 @@ class Response(aiohttp.web.Response):
log.debug(dict(self.headers))
if hasattr(self, 'body') and self.body is not None and self.headers["CONTENT-TYPE"] == "application/json":
log.debug(json.loads(self.body.decode('utf-8')))
return (yield from super().prepare(request))
return (await super().prepare(request))
def html(self, answer):
"""
@@ -112,8 +111,7 @@ class Response(aiohttp.web.Response):
raise aiohttp.web.HTTPBadRequest(text="{}".format(e))
self.body = json.dumps(answer, indent=4, sort_keys=True).encode('utf-8')
@asyncio.coroutine
def file(self, path, status=200, set_content_length=True):
async def file(self, path, status=200, set_content_length=True):
"""
Return a file as a response
"""
@@ -138,14 +136,14 @@ class Response(aiohttp.web.Response):
try:
with open(path, 'rb') as fobj:
yield from self.prepare(self._request)
await self.prepare(self._request)
while True:
data = fobj.read(4096)
if not data:
break
yield from self.write(data)
yield from self.drain()
await self.write(data)
await self.drain()
except FileNotFoundError:
raise aiohttp.web.HTTPNotFound()
+11 -14
View File
@@ -36,13 +36,12 @@ from ..crash_report import CrashReport
from ..config import Config
@asyncio.coroutine
def parse_request(request, input_schema, raw):
async def parse_request(request, input_schema, raw):
"""Parse body of request and raise HTTP errors in case of problems"""
request.json = {}
if not raw:
body = yield from request.read()
body = await request.read()
if body:
try:
request.json = json.loads(body.decode('utf-8'))
@@ -162,8 +161,7 @@ class Route(object):
func = asyncio.coroutine(func)
@asyncio.coroutine
def control_schema(request):
async def control_schema(request):
# This block is executed at each method call
server_config = Config.instance().get_section_config("Server")
@@ -177,12 +175,12 @@ class Route(object):
if api_version is None or raw is True:
response = Response(request=request, route=route, output_schema=output_schema)
request = yield from parse_request(request, None, raw)
yield from func(request, response)
request = await parse_request(request, None, raw)
await func(request, response)
return response
# API call
request = yield from parse_request(request, input_schema, raw)
request = await parse_request(request, input_schema, raw)
record_file = server_config.get("record")
if record_file:
try:
@@ -192,7 +190,7 @@ class Route(object):
except OSError as e:
log.warning("Could not write to the record file {}: {}".format(record_file, e))
response = Response(request=request, route=route, output_schema=output_schema)
yield from func(request, response)
await func(request, response)
except aiohttp.web.HTTPBadRequest as e:
response = Response(request=request, route=route)
response.set_status(e.status)
@@ -246,8 +244,7 @@ class Route(object):
return response
@asyncio.coroutine
def node_concurrency(request):
async def node_concurrency(request):
"""
To avoid strange effect we prevent concurrency
between the same instance of the node
@@ -264,15 +261,15 @@ class Route(object):
cls._node_locks.setdefault(lock_key, {"lock": asyncio.Lock(), "concurrency": 0})
cls._node_locks[lock_key]["concurrency"] += 1
with (yield from cls._node_locks[lock_key]["lock"]):
response = yield from control_schema(request)
async with cls._node_locks[lock_key]["lock"]:
response = await control_schema(request)
cls._node_locks[lock_key]["concurrency"] -= 1
# No more waiting requests, garbage collect the lock
if cls._node_locks[lock_key]["concurrency"] <= 0:
del cls._node_locks[lock_key]
else:
response = yield from control_schema(request)
response = await control_schema(request)
return response
cls._routes.append((method, route, node_concurrency))
+17 -21
View File
@@ -39,7 +39,6 @@ from ..compute import MODULES
from ..compute.port_manager import PortManager
from ..compute.qemu import Qemu
from ..controller import Controller
from ..utils.asyncio import asyncio_ensure_future
from gns3server.utils.static import get_static_dir
@@ -90,8 +89,7 @@ class WebServer:
return False
return True
@asyncio.coroutine
def shutdown_server(self):
async def shutdown_server(self):
"""
Cleanly shutdown the server.
"""
@@ -104,25 +102,25 @@ class WebServer:
if self._server:
self._server.close()
yield from self._server.wait_closed()
await self._server.wait_closed()
if self._app:
yield from self._app.shutdown()
await self._app.shutdown()
if self._handler:
try:
# aiohttp < 2.3
yield from self._handler.finish_connections(2) # Parameter is timeout
await self._handler.finish_connections(2) # Parameter is timeout
except AttributeError:
# aiohttp >= 2.3
yield from self._handler.shutdown(2) # Parameter is timeout
await self._handler.shutdown(2) # Parameter is timeout
if self._app:
yield from self._app.cleanup()
await self._app.cleanup()
yield from Controller.instance().stop()
await Controller.instance().stop()
for module in MODULES:
log.debug("Unloading module {}".format(module.__name__))
m = module.instance()
yield from m.unload()
await m.unload()
if PortManager.instance().tcp_ports:
log.warning("TCP ports are still used {}".format(PortManager.instance().tcp_ports))
@@ -133,7 +131,7 @@ class WebServer:
for task in asyncio.Task.all_tasks():
task.cancel()
try:
yield from asyncio.wait_for(task, 1)
await asyncio.wait_for(task, 1)
except BaseException:
pass
@@ -144,7 +142,7 @@ class WebServer:
def signal_handler(signame, *args):
log.warning("Server has got signal {}, exiting...".format(signame))
try:
asyncio_ensure_future(self.shutdown_server())
asyncio.ensure_future(self.shutdown_server())
except asyncio.CancelledError:
pass
@@ -179,14 +177,13 @@ class WebServer:
log.info("SSL is enabled")
return ssl_context
@asyncio.coroutine
def start_shell(self):
async def start_shell(self):
try:
from ptpython.repl import embed
except ImportError:
log.error("Unable to start a shell: the ptpython module must be installed!")
return
yield from embed(globals(), locals(), return_asyncio_coroutine=True, patch_stdout=True, history_filename=".gns3_shell_history")
await embed(globals(), locals(), return_asyncio_coroutine=True, patch_stdout=True, history_filename=".gns3_shell_history")
def _exit_handling(self):
"""
@@ -204,16 +201,15 @@ class WebServer:
atexit.register(close_asyncio_loop)
@asyncio.coroutine
def _on_startup(self, *args):
async def _on_startup(self, *args):
"""
Called when the HTTP server start
"""
yield from Controller.instance().start()
await Controller.instance().start()
# Because with a large image collection
# without md5sum already computed we start the
# computing with server start
asyncio_ensure_future(Qemu.instance().list_images())
asyncio.ensure_future(Qemu.instance().list_images())
def run(self):
"""
@@ -251,7 +247,7 @@ class WebServer:
if log.getEffectiveLevel() == logging.DEBUG:
# On debug version we enable info that
# coroutine is not called in a way await/yield from
# coroutine is not called in a way await/await
self._loop.set_debug(True)
for key, val in os.environ.items():
@@ -300,7 +296,7 @@ class WebServer:
self._exit_handling()
if server_config.getboolean("shell"):
asyncio_ensure_future(self.start_shell())
asyncio.ensure_future(self.start_shell())
try:
self._loop.run_forever()