mirror of
https://github.com/mborgerson/xemu.git
synced 2026-03-09 07:34:38 +00:00
python: backport 'drop Python3.6 workarounds'
Now that the minimum version is 3.7, drop some of the 3.6-specific hacks we've been carrying. A single remaining compatibility hack concerning 3.6's lack of @asynccontextmanager is addressed in the following commit. Signed-off-by: John Snow <jsnow@redhat.com> cherry picked from commit python-qemu-qmp@3e8e34e594cfc6b707e6f67959166acde4b421b8 Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
@ -36,13 +36,10 @@ from typing import (
|
||||
from .error import QMPError
|
||||
from .util import (
|
||||
bottom_half,
|
||||
create_task,
|
||||
exception_summary,
|
||||
flush,
|
||||
is_closing,
|
||||
pretty_traceback,
|
||||
upper_half,
|
||||
wait_closed,
|
||||
)
|
||||
|
||||
|
||||
@ -682,8 +679,8 @@ class AsyncProtocol(Generic[T]):
|
||||
reader_coro = self._bh_loop_forever(self._bh_recv_message, 'Reader')
|
||||
writer_coro = self._bh_loop_forever(self._bh_send_message, 'Writer')
|
||||
|
||||
self._reader_task = create_task(reader_coro)
|
||||
self._writer_task = create_task(writer_coro)
|
||||
self._reader_task = asyncio.create_task(reader_coro)
|
||||
self._writer_task = asyncio.create_task(writer_coro)
|
||||
|
||||
self._bh_tasks = asyncio.gather(
|
||||
self._reader_task,
|
||||
@ -708,7 +705,7 @@ class AsyncProtocol(Generic[T]):
|
||||
if not self._dc_task:
|
||||
self._set_state(Runstate.DISCONNECTING)
|
||||
self.logger.debug("Scheduling disconnect.")
|
||||
self._dc_task = create_task(self._bh_disconnect())
|
||||
self._dc_task = asyncio.create_task(self._bh_disconnect())
|
||||
|
||||
@upper_half
|
||||
async def _wait_disconnect(self) -> None:
|
||||
@ -844,13 +841,13 @@ class AsyncProtocol(Generic[T]):
|
||||
if not self._writer:
|
||||
return
|
||||
|
||||
if not is_closing(self._writer):
|
||||
if not self._writer.is_closing():
|
||||
self.logger.debug("Closing StreamWriter.")
|
||||
self._writer.close()
|
||||
|
||||
self.logger.debug("Waiting for StreamWriter to close ...")
|
||||
try:
|
||||
await wait_closed(self._writer)
|
||||
await self._writer.wait_closed()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# It's hard to tell if the Stream is already closed or
|
||||
# not. Even if one of the tasks has failed, it may have
|
||||
|
||||
@ -40,7 +40,7 @@ from .legacy import QEMUMonitorProtocol, QMPBadPortError
|
||||
from .message import DeserializationError, Message, UnexpectedTypeError
|
||||
from .protocol import ConnectError, Runstate
|
||||
from .qmp_client import ExecInterruptedError, QMPClient
|
||||
from .util import create_task, pretty_traceback
|
||||
from .util import pretty_traceback
|
||||
|
||||
|
||||
# The name of the signal that is used to update the history list
|
||||
@ -225,7 +225,7 @@ class App(QMPClient):
|
||||
"""
|
||||
try:
|
||||
msg = Message(bytes(raw_msg, encoding='utf-8'))
|
||||
create_task(self._send_to_server(msg))
|
||||
asyncio.create_task(self._send_to_server(msg))
|
||||
except (DeserializationError, UnexpectedTypeError) as err:
|
||||
raw_msg = format_json(raw_msg)
|
||||
logging.info('Invalid message: %s', err.error_message)
|
||||
@ -246,7 +246,7 @@ class App(QMPClient):
|
||||
Initiates killing of app. A bridge between asynchronous and synchronous
|
||||
code.
|
||||
"""
|
||||
create_task(self._kill_app())
|
||||
asyncio.create_task(self._kill_app())
|
||||
|
||||
async def _kill_app(self) -> None:
|
||||
"""
|
||||
@ -393,7 +393,7 @@ class App(QMPClient):
|
||||
handle_mouse=True,
|
||||
event_loop=event_loop)
|
||||
|
||||
create_task(self.manage_connection(), self.aloop)
|
||||
self.aloop.create_task(self.manage_connection())
|
||||
try:
|
||||
main_loop.run()
|
||||
except Exception as err:
|
||||
|
||||
@ -1,25 +1,15 @@
|
||||
"""
|
||||
Miscellaneous Utilities
|
||||
|
||||
This module provides asyncio utilities and compatibility wrappers for
|
||||
Python 3.6 to provide some features that otherwise become available in
|
||||
Python 3.7+.
|
||||
|
||||
Various logging and debugging utilities are also provided, such as
|
||||
`exception_summary()` and `pretty_traceback()`, used primarily for
|
||||
adding information into the logging stream.
|
||||
This module provides asyncio and various logging and debugging
|
||||
utilities, such as `exception_summary()` and `pretty_traceback()`, used
|
||||
primarily for adding information into the logging stream.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import traceback
|
||||
from typing import (
|
||||
Any,
|
||||
Coroutine,
|
||||
Optional,
|
||||
TypeVar,
|
||||
cast,
|
||||
)
|
||||
from typing import TypeVar, cast
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
@ -79,95 +69,6 @@ def bottom_half(func: T) -> T:
|
||||
return func
|
||||
|
||||
|
||||
# -------------------------------
|
||||
# Section: Compatibility Wrappers
|
||||
# -------------------------------
|
||||
|
||||
|
||||
def create_task(coro: Coroutine[Any, Any, T],
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> 'asyncio.Future[T]':
|
||||
"""
|
||||
Python 3.6-compatible `asyncio.create_task` wrapper.
|
||||
|
||||
:param coro: The coroutine to execute in a task.
|
||||
:param loop: Optionally, the loop to create the task in.
|
||||
|
||||
:return: An `asyncio.Future` object.
|
||||
"""
|
||||
if sys.version_info >= (3, 7):
|
||||
if loop is not None:
|
||||
return loop.create_task(coro)
|
||||
return asyncio.create_task(coro) # pylint: disable=no-member
|
||||
|
||||
# Python 3.6:
|
||||
return asyncio.ensure_future(coro, loop=loop)
|
||||
|
||||
|
||||
def is_closing(writer: asyncio.StreamWriter) -> bool:
|
||||
"""
|
||||
Python 3.6-compatible `asyncio.StreamWriter.is_closing` wrapper.
|
||||
|
||||
:param writer: The `asyncio.StreamWriter` object.
|
||||
:return: `True` if the writer is closing, or closed.
|
||||
"""
|
||||
if sys.version_info >= (3, 7):
|
||||
return writer.is_closing()
|
||||
|
||||
# Python 3.6:
|
||||
transport = writer.transport
|
||||
assert isinstance(transport, asyncio.WriteTransport)
|
||||
return transport.is_closing()
|
||||
|
||||
|
||||
async def wait_closed(writer: asyncio.StreamWriter) -> None:
|
||||
"""
|
||||
Python 3.6-compatible `asyncio.StreamWriter.wait_closed` wrapper.
|
||||
|
||||
:param writer: The `asyncio.StreamWriter` to wait on.
|
||||
"""
|
||||
if sys.version_info >= (3, 7):
|
||||
await writer.wait_closed()
|
||||
return
|
||||
|
||||
# Python 3.6
|
||||
transport = writer.transport
|
||||
assert isinstance(transport, asyncio.WriteTransport)
|
||||
|
||||
while not transport.is_closing():
|
||||
await asyncio.sleep(0)
|
||||
|
||||
# This is an ugly workaround, but it's the best I can come up with.
|
||||
sock = transport.get_extra_info('socket')
|
||||
|
||||
if sock is None:
|
||||
# Our transport doesn't have a socket? ...
|
||||
# Nothing we can reasonably do.
|
||||
return
|
||||
|
||||
while sock.fileno() != -1:
|
||||
await asyncio.sleep(0)
|
||||
|
||||
|
||||
def asyncio_run(coro: Coroutine[Any, Any, T], *, debug: bool = False) -> T:
|
||||
"""
|
||||
Python 3.6-compatible `asyncio.run` wrapper.
|
||||
|
||||
:param coro: A coroutine to execute now.
|
||||
:return: The return value from the coroutine.
|
||||
"""
|
||||
if sys.version_info >= (3, 7):
|
||||
return asyncio.run(coro, debug=debug)
|
||||
|
||||
# Python 3.6
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.set_debug(debug)
|
||||
ret = loop.run_until_complete(coro)
|
||||
loop.close()
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# Section: Logging & Debugging
|
||||
# ----------------------------
|
||||
|
||||
@ -8,7 +8,6 @@ import avocado
|
||||
|
||||
from qemu.qmp import ConnectError, Runstate
|
||||
from qemu.qmp.protocol import AsyncProtocol, StateError
|
||||
from qemu.qmp.util import asyncio_run, create_task
|
||||
|
||||
|
||||
class NullProtocol(AsyncProtocol[None]):
|
||||
@ -124,7 +123,7 @@ def run_as_task(coro, allow_cancellation=False):
|
||||
if allow_cancellation:
|
||||
return
|
||||
raise
|
||||
return create_task(_runner())
|
||||
return asyncio.create_task(_runner())
|
||||
|
||||
|
||||
@contextmanager
|
||||
@ -271,7 +270,7 @@ class TestBase(avocado.Test):
|
||||
msg=f"Expected state '{state.name}'",
|
||||
)
|
||||
|
||||
self.runstate_watcher = create_task(_watcher())
|
||||
self.runstate_watcher = asyncio.create_task(_watcher())
|
||||
# Kick the loop and force the task to block on the event.
|
||||
await asyncio.sleep(0)
|
||||
|
||||
@ -589,7 +588,8 @@ class SimpleSession(TestBase):
|
||||
async def testSmoke(self):
|
||||
with TemporaryDirectory(suffix='.qmp') as tmpdir:
|
||||
sock = os.path.join(tmpdir, type(self.proto).__name__ + ".sock")
|
||||
server_task = create_task(self.server.start_server_and_accept(sock))
|
||||
server_task = asyncio.create_task(
|
||||
self.server.start_server_and_accept(sock))
|
||||
|
||||
# give the server a chance to start listening [...]
|
||||
await asyncio.sleep(0)
|
||||
|
||||
Reference in New Issue
Block a user