mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
backport py3.7 asyncio.run to support py3.6
This commit is contained in:
@@ -165,9 +165,9 @@ def build_ui():
|
|||||||
ClickException: Raised on any errors.
|
ClickException: Raised on any errors.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
import asyncio
|
from hyperglass.compat import aiorun
|
||||||
from hyperglass.configuration import params, frontend_params, CONFIG_PATH
|
|
||||||
from hyperglass.util import build_frontend
|
from hyperglass.util import build_frontend
|
||||||
|
from hyperglass.configuration import params, frontend_params, CONFIG_PATH
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
error("Error importing UI builder: {e}", e=e)
|
error("Error importing UI builder: {e}", e=e)
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ def build_ui():
|
|||||||
dev_mode = "development"
|
dev_mode = "development"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
build_success = asyncio.run(
|
build_success = aiorun(
|
||||||
build_frontend(
|
build_frontend(
|
||||||
dev_mode=params.developer_mode,
|
dev_mode=params.developer_mode,
|
||||||
dev_url=f"http://localhost:{str(params.listen_port)}/",
|
dev_url=f"http://localhost:{str(params.listen_port)}/",
|
||||||
|
116
hyperglass/compat.py
Normal file
116
hyperglass/compat.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
"""Functions for maintaining compatability with older Python versions."""
|
||||||
|
|
||||||
|
# Standard Library
|
||||||
|
import sys
|
||||||
|
import asyncio
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
try:
|
||||||
|
from asyncio import get_running_loop
|
||||||
|
except ImportError:
|
||||||
|
from asyncio.events import _get_running_loop as get_running_loop
|
||||||
|
|
||||||
|
RUNNING_PYTHON_VERSION = sys.version_info
|
||||||
|
|
||||||
|
# _patch_loop, _patched_run, and _cancel_all_tasks are taken directly
|
||||||
|
# from github.com/nickdavis:
|
||||||
|
# https://gist.github.com/nickdavies/4a37c6cd9dcc7041fddd2d2a81cee383
|
||||||
|
|
||||||
|
# These functions are a backport of the functionality added in
|
||||||
|
# Python 3.7 to support asyncio.run(), which is used in several areas
|
||||||
|
# of hyperglass. Because the LTS version of Ubuntu at this time (18.04)
|
||||||
|
# still ships with Python 3.6, compatibility with Python 3.6 is the
|
||||||
|
# goal.
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_loop(loop):
|
||||||
|
tasks = weakref.WeakSet()
|
||||||
|
|
||||||
|
task_factory = [None]
|
||||||
|
|
||||||
|
def _set_task_factory(factory):
|
||||||
|
task_factory[0] = factory
|
||||||
|
|
||||||
|
def _get_task_factory():
|
||||||
|
return task_factory[0]
|
||||||
|
|
||||||
|
def _safe_task_factory(loop, coro):
|
||||||
|
if task_factory[0] is None:
|
||||||
|
task = asyncio.Task(coro, loop=loop)
|
||||||
|
if task._source_traceback:
|
||||||
|
del task._source_traceback[-1]
|
||||||
|
else:
|
||||||
|
task = task_factory[0](loop, coro)
|
||||||
|
tasks.add(task)
|
||||||
|
return task
|
||||||
|
|
||||||
|
loop.set_task_factory(_safe_task_factory)
|
||||||
|
loop.set_task_factory = _set_task_factory
|
||||||
|
loop.get_task_factory = _get_task_factory
|
||||||
|
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
def _patched_run(main, *, debug=False):
|
||||||
|
try:
|
||||||
|
loop = get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = None
|
||||||
|
|
||||||
|
if loop is not None:
|
||||||
|
raise RuntimeError("asyncio.run() cannot be called from a running event loop")
|
||||||
|
|
||||||
|
if not asyncio.iscoroutine(main):
|
||||||
|
raise ValueError("a coroutine was expected, got {!r}".format(main))
|
||||||
|
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
tasks = _patch_loop(loop)
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.set_debug(debug)
|
||||||
|
return loop.run_until_complete(main)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
_cancel_all_tasks(loop, tasks)
|
||||||
|
loop.run_until_complete(loop.shutdown_asyncgens())
|
||||||
|
finally:
|
||||||
|
asyncio.set_event_loop(None)
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _cancel_all_tasks(loop, tasks):
|
||||||
|
to_cancel = [task for task in tasks if not task.done()]
|
||||||
|
|
||||||
|
if not to_cancel:
|
||||||
|
return
|
||||||
|
|
||||||
|
for task in to_cancel:
|
||||||
|
task.cancel()
|
||||||
|
|
||||||
|
loop.run_until_complete(
|
||||||
|
asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for task in to_cancel:
|
||||||
|
if task.cancelled():
|
||||||
|
continue
|
||||||
|
if task.exception() is not None:
|
||||||
|
loop.call_exception_handler(
|
||||||
|
{
|
||||||
|
"message": "unhandled exception during asyncio.run() shutdown",
|
||||||
|
"exception": task.exception(),
|
||||||
|
"task": task,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# If local system's python version is at least 3.6, use the backported
|
||||||
|
# asyncio runner.
|
||||||
|
if RUNNING_PYTHON_VERSION >= (3, 6):
|
||||||
|
aiorun = _patched_run
|
||||||
|
|
||||||
|
# If the local system's python version is at least 3.7, use the standard
|
||||||
|
# library's asyncio.run()
|
||||||
|
elif RUNNING_PYTHON_VERSION >= (3, 7):
|
||||||
|
aiorun = asyncio.run
|
@@ -4,7 +4,6 @@
|
|||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
import asyncio
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Third Party
|
# Third Party
|
||||||
@@ -15,6 +14,7 @@ from pydantic import ValidationError
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.util import log, check_path, set_app_path
|
from hyperglass.util import log, check_path, set_app_path
|
||||||
|
from hyperglass.compat import aiorun
|
||||||
from hyperglass.constants import (
|
from hyperglass.constants import (
|
||||||
CREDIT,
|
CREDIT,
|
||||||
LOG_LEVELS,
|
LOG_LEVELS,
|
||||||
@@ -80,9 +80,7 @@ async def _check_config_files(directory):
|
|||||||
|
|
||||||
STATIC_PATH = CONFIG_PATH / "static"
|
STATIC_PATH = CONFIG_PATH / "static"
|
||||||
|
|
||||||
CONFIG_MAIN, CONFIG_DEVICES, CONFIG_COMMANDS = asyncio.run(
|
CONFIG_MAIN, CONFIG_DEVICES, CONFIG_COMMANDS = aiorun(_check_config_files(CONFIG_PATH))
|
||||||
_check_config_files(CONFIG_PATH)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _set_log_level(debug, log_file=None):
|
def _set_log_level(debug, log_file=None):
|
||||||
@@ -166,7 +164,7 @@ async def _config_devices():
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
user_config = asyncio.run(_config_main())
|
user_config = aiorun(_config_main())
|
||||||
|
|
||||||
# Logging Config
|
# Logging Config
|
||||||
try:
|
try:
|
||||||
@@ -177,8 +175,8 @@ except KeyError:
|
|||||||
# Read raw debug value from config to enable debugging quickly.
|
# Read raw debug value from config to enable debugging quickly.
|
||||||
_set_log_level(_debug)
|
_set_log_level(_debug)
|
||||||
|
|
||||||
_user_commands = asyncio.run(_config_commands())
|
_user_commands = aiorun(_config_commands())
|
||||||
_user_devices = asyncio.run(_config_devices())
|
_user_devices = aiorun(_config_devices())
|
||||||
|
|
||||||
# Map imported user config files to expected schema:
|
# Map imported user config files to expected schema:
|
||||||
try:
|
try:
|
||||||
@@ -383,7 +381,7 @@ def _build_vrf_help():
|
|||||||
"title"
|
"title"
|
||||||
] = f"{vrf.display_name}: {command_params.display_name}"
|
] = f"{vrf.display_name}: {command_params.display_name}"
|
||||||
|
|
||||||
md = asyncio.run(
|
md = aiorun(
|
||||||
get_markdown(
|
get_markdown(
|
||||||
config_path=cmd,
|
config_path=cmd,
|
||||||
default=DEFAULT_DETAILS[command],
|
default=DEFAULT_DETAILS[command],
|
||||||
@@ -404,7 +402,7 @@ content_vrf = _build_vrf_help()
|
|||||||
|
|
||||||
content_help_params = copy.copy(content_params)
|
content_help_params = copy.copy(content_params)
|
||||||
content_help_params["title"] = params.web.help_menu.title
|
content_help_params["title"] = params.web.help_menu.title
|
||||||
content_help = asyncio.run(
|
content_help = aiorun(
|
||||||
get_markdown(
|
get_markdown(
|
||||||
config_path=params.web.help_menu,
|
config_path=params.web.help_menu,
|
||||||
default=DEFAULT_HELP,
|
default=DEFAULT_HELP,
|
||||||
@@ -414,7 +412,7 @@ content_help = asyncio.run(
|
|||||||
|
|
||||||
content_terms_params = copy.copy(content_params)
|
content_terms_params = copy.copy(content_params)
|
||||||
content_terms_params["title"] = params.web.terms.title
|
content_terms_params["title"] = params.web.terms.title
|
||||||
content_terms = asyncio.run(
|
content_terms = aiorun(
|
||||||
get_markdown(
|
get_markdown(
|
||||||
config_path=params.web.terms, default=DEFAULT_TERMS, params=content_terms_params
|
config_path=params.web.terms, default=DEFAULT_TERMS, params=content_terms_params
|
||||||
)
|
)
|
||||||
|
@@ -11,7 +11,7 @@ __license__ = "BSD 3-Clause Clear License"
|
|||||||
|
|
||||||
METADATA = (__name__, __version__, __author__, __copyright__, __license__)
|
METADATA = (__name__, __version__, __author__, __copyright__, __license__)
|
||||||
|
|
||||||
MIN_PYTHON_VERSION = (3, 7)
|
MIN_PYTHON_VERSION = (3, 6)
|
||||||
|
|
||||||
protocol_map = {80: "http", 8080: "http", 443: "https", 8443: "https"}
|
protocol_map = {80: "http", 8080: "http", 443: "https", 8443: "https"}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user