exe 依赖添加
This commit is contained in:
83
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/__init__.py
vendored
Normal file
83
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/__init__.py
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
from __future__ import annotations
|
||||
|
||||
from .events import (
|
||||
QAsyncioEventLoopPolicy, QAsyncioEventLoop, QAsyncioHandle, QAsyncioTimerHandle
|
||||
)
|
||||
from .futures import QAsyncioFuture
|
||||
from .tasks import QAsyncioTask
|
||||
|
||||
from typing import Coroutine, Any
|
||||
|
||||
import asyncio
|
||||
|
||||
__all__ = [
|
||||
"QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
|
||||
"QAsyncioHandle", "QAsyncioTimerHandle",
|
||||
"QAsyncioFuture", "QAsyncioTask"
|
||||
]
|
||||
|
||||
|
||||
def run(coro: Coroutine | None = None,
|
||||
keep_running: bool = True, quit_qapp: bool = True, *, handle_sigint: bool = False,
|
||||
debug: bool | None = None) -> Any:
|
||||
"""
|
||||
Run the QtAsyncio event loop.
|
||||
|
||||
If there is no instance of a QCoreApplication, QGuiApplication or
|
||||
QApplication yet, a new instance of QCoreApplication is created.
|
||||
|
||||
:param coro: The coroutine to run. Optional if keep_running is
|
||||
True.
|
||||
:param keep_running: If True, QtAsyncio (the asyncio event loop) will
|
||||
continue running after the coroutine finished, or
|
||||
run "forever" if no coroutine was provided.
|
||||
If False, QtAsyncio will stop after the
|
||||
coroutine finished. A coroutine must be provided if
|
||||
this argument is set to False.
|
||||
:param quit_qapp: If True, the QCoreApplication will quit when
|
||||
QtAsyncio (the asyncio event loop) stops.
|
||||
If False, the QCoreApplication will remain active
|
||||
after QtAsyncio stops, and can continue to be used.
|
||||
:param handle_sigint: If True, the SIGINT signal will be handled by the
|
||||
event loop, causing it to stop.
|
||||
:param debug: If True, the event loop will run in debug mode.
|
||||
If False, the event loop will run in normal mode.
|
||||
If None, the default behavior is used.
|
||||
"""
|
||||
|
||||
# Event loop policies are expected to be deprecated with Python 3.13, with
|
||||
# subsequent removal in Python 3.15. At that point, part of the current
|
||||
# logic of the QAsyncioEventLoopPolicy constructor will have to be moved
|
||||
# here and/or to a loop factory class (to be provided as an argument to
|
||||
# asyncio.run()). In particular, this concerns the logic of setting up the
|
||||
# QCoreApplication and the SIGINT handler.
|
||||
#
|
||||
# More details:
|
||||
# https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553 # noqa: E501
|
||||
default_policy = asyncio.get_event_loop_policy()
|
||||
asyncio.set_event_loop_policy(
|
||||
QAsyncioEventLoopPolicy(quit_qapp=quit_qapp, handle_sigint=handle_sigint))
|
||||
|
||||
ret = None
|
||||
exc = None
|
||||
|
||||
if keep_running:
|
||||
if coro:
|
||||
asyncio.ensure_future(coro)
|
||||
asyncio.get_event_loop().run_forever()
|
||||
else:
|
||||
if coro:
|
||||
ret = asyncio.run(coro, debug=debug)
|
||||
else:
|
||||
exc = RuntimeError(
|
||||
"QtAsyncio was set not to keep running after the coroutine "
|
||||
"finished, but no coroutine was provided.")
|
||||
|
||||
asyncio.set_event_loop_policy(default_policy)
|
||||
|
||||
if ret:
|
||||
return ret
|
||||
if exc:
|
||||
raise exc
|
||||
739
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/events.py
vendored
Normal file
739
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/events.py
vendored
Normal file
@@ -0,0 +1,739 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
|
||||
QEventLoop, QObject, QTimer, QThread, Slot)
|
||||
|
||||
from . import futures
|
||||
from . import tasks
|
||||
|
||||
from typing import Any, Callable, TypeVar
|
||||
|
||||
import asyncio
|
||||
import collections.abc
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import enum
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import warnings
|
||||
|
||||
__all__ = [
|
||||
"QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
|
||||
"QAsyncioHandle", "QAsyncioTimerHandle",
|
||||
]
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
try:
|
||||
from typing import TypeVarTuple, Unpack
|
||||
except ImportError:
|
||||
from typing_extensions import TypeVarTuple, Unpack # type: ignore
|
||||
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
Context = contextvars.Context # type: ignore
|
||||
else:
|
||||
_Ts = None # type: ignore
|
||||
Context = contextvars.Context
|
||||
|
||||
|
||||
class QAsyncioExecutorWrapper(QObject):
|
||||
"""
|
||||
Executors in asyncio allow running synchronous code in a separate thread or
|
||||
process without blocking the event loop or interrupting the asynchronous
|
||||
program flow. Callables are scheduled for execution by calling submit() or
|
||||
map() on an executor object.
|
||||
|
||||
Executors require a bit of extra work for QtAsyncio, as we can't use
|
||||
naked Python threads; instead, we must make sure that the thread created
|
||||
by executor.submit() has an event loop. This is achieved by not submitting
|
||||
the callable directly, but a small wrapper that attaches a QEventLoop to
|
||||
the executor thread, and then creates a zero-delay singleshot timer to push
|
||||
the actual callable for the executor into this new event loop.
|
||||
"""
|
||||
def __init__(self, func: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None:
|
||||
super().__init__()
|
||||
self._loop: QEventLoop
|
||||
self._func = func
|
||||
self._args = args
|
||||
self._result: Any = None
|
||||
self._exception: BaseException | None = None
|
||||
|
||||
def _cb(self):
|
||||
try:
|
||||
# Call the synchronous callable that we submitted with submit() or
|
||||
# map().
|
||||
self._result = self._func(*self._args)
|
||||
except BaseException as e:
|
||||
self._exception = e
|
||||
self._loop.exit()
|
||||
|
||||
def do(self) -> Any:
|
||||
# This creates a new event loop and dispatcher for the thread, if not
|
||||
# already created.
|
||||
self._loop = QEventLoop()
|
||||
asyncio.events._set_running_loop(self._loop)
|
||||
|
||||
# The do() function will always be executed from the new executor
|
||||
# thread and never from outside, so using the overload without the
|
||||
# context argument is sufficient.
|
||||
QTimer.singleShot(0, lambda: self._cb())
|
||||
|
||||
self._loop.exec()
|
||||
if self._exception is not None:
|
||||
raise self._exception
|
||||
return self._result
|
||||
|
||||
def exit(self):
|
||||
self._loop.exit()
|
||||
|
||||
|
||||
class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
|
||||
"""
|
||||
Event loop policies are expected to be deprecated with Python 3.13, with
|
||||
subsequent removal in Python 3.15. At that point, part of the current
|
||||
logic of the QAsyncioEventLoopPolicy constructor will have to be moved
|
||||
to QtAsyncio.run() and/or to a loop factory class (to be provided as an
|
||||
argument to asyncio.run()). In particular, this concerns the logic of
|
||||
setting up the QCoreApplication and the SIGINT handler.
|
||||
|
||||
More details:
|
||||
https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553
|
||||
"""
|
||||
def __init__(self,
|
||||
quit_qapp: bool = True,
|
||||
handle_sigint: bool = False) -> None:
|
||||
super().__init__()
|
||||
self._application = QCoreApplication.instance() or QCoreApplication()
|
||||
|
||||
# Configure whether the QCoreApplication at the core of QtAsyncio
|
||||
# should be shut down when asyncio finishes. A special case where one
|
||||
# would want to disable this is test suites that want to reuse a single
|
||||
# QCoreApplication instance across all unit tests, which would fail if
|
||||
# this instance is shut down every time.
|
||||
self._quit_qapp = quit_qapp
|
||||
|
||||
self._event_loop: asyncio.AbstractEventLoop | None = None
|
||||
|
||||
if handle_sigint:
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
def get_event_loop(self) -> asyncio.AbstractEventLoop:
|
||||
if self._event_loop is None:
|
||||
self._event_loop = QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
|
||||
return self._event_loop
|
||||
|
||||
def set_event_loop(self, loop: asyncio.AbstractEventLoop | None) -> None:
|
||||
self._event_loop = loop
|
||||
|
||||
def new_event_loop(self) -> asyncio.AbstractEventLoop:
|
||||
return QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
|
||||
|
||||
def get_child_watcher(self) -> "asyncio.AbstractChildWatcher":
|
||||
raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
|
||||
|
||||
def set_child_watcher(self, watcher: "asyncio.AbstractChildWatcher") -> None:
|
||||
raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
|
||||
|
||||
|
||||
class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
|
||||
"""
|
||||
Implements the asyncio API:
|
||||
https://docs.python.org/3/library/asyncio-eventloop.html
|
||||
"""
|
||||
|
||||
class ShutDownThread(QThread):
|
||||
"""
|
||||
Used to shut down the default executor when calling
|
||||
shutdown_default_executor(). As the executor is a ThreadPoolExecutor,
|
||||
it must be shut down in a separate thread as all the threads from the
|
||||
thread pool must join, which we want to do without blocking the event
|
||||
loop.
|
||||
"""
|
||||
|
||||
def __init__(self, future: futures.QAsyncioFuture, loop: "QAsyncioEventLoop") -> None:
|
||||
super().__init__()
|
||||
self._future = future
|
||||
self._loop = loop
|
||||
self.started.connect(self.shutdown)
|
||||
|
||||
def run(self) -> None:
|
||||
pass
|
||||
|
||||
def shutdown(self) -> None:
|
||||
try:
|
||||
self._loop._default_executor.shutdown(wait=True)
|
||||
if not self._loop.is_closed():
|
||||
self._loop.call_soon_threadsafe(self._future.set_result, None)
|
||||
except Exception as e:
|
||||
if not self._loop.is_closed():
|
||||
self._loop.call_soon_threadsafe(self._future.set_exception, e)
|
||||
|
||||
def __init__(self,
|
||||
application: QCoreApplication, quit_qapp: bool = True) -> None:
|
||||
asyncio.BaseEventLoop.__init__(self)
|
||||
QObject.__init__(self)
|
||||
|
||||
self._application: QCoreApplication = application
|
||||
|
||||
# Configure whether the QCoreApplication at the core of QtAsyncio
|
||||
# should be shut down when asyncio finishes. A special case where one
|
||||
# would want to disable this is test suites that want to reuse a single
|
||||
# QCoreApplication instance across all unit tests, which would fail if
|
||||
# this instance is shut down every time.
|
||||
self._quit_qapp = quit_qapp
|
||||
|
||||
self._thread = QThread.currentThread()
|
||||
|
||||
self._closed = False
|
||||
|
||||
# These two flags are used to determine whether the loop was stopped
|
||||
# from inside the loop (i.e., coroutine or callback called stop()) or
|
||||
# from outside the loop (i.e., the QApplication is being shut down, for
|
||||
# example, by the user closing the window or by calling
|
||||
# QApplication.quit()). The different cases can trigger slightly
|
||||
# different behaviors (see the comments where the flags are used).
|
||||
# There are two variables for this as in a third case the loop is still
|
||||
# running and both flags are False.
|
||||
self._quit_from_inside = False
|
||||
self._quit_from_outside = False
|
||||
|
||||
# A set of all asynchronous generators that are currently running.
|
||||
self._asyncgens: set[collections.abc.AsyncGenerator] = set()
|
||||
|
||||
# Starting with Python 3.11, this must be an instance of
|
||||
# ThreadPoolExecutor.
|
||||
self._default_executor = concurrent.futures.ThreadPoolExecutor()
|
||||
|
||||
# The exception handler, if set with set_exception_handler(). The
|
||||
# exception handler is currently called in two places: One, if an
|
||||
# asynchonrous generator raises an exception when closed, and two, if
|
||||
# an exception is raised during the execution of a task. Currently, the
|
||||
# default exception handler just prints the exception to the console.
|
||||
self._exception_handler: Callable | None = self.default_exception_handler
|
||||
|
||||
# The task factory, if set with set_task_factory(). Otherwise, a new
|
||||
# task is created with the QAsyncioTask constructor.
|
||||
self._task_factory: Callable | None = None
|
||||
|
||||
# The future that is currently being awaited with run_until_complete().
|
||||
self._future_to_complete: futures.QAsyncioFuture | None = None
|
||||
|
||||
self._debug = bool(os.getenv("PYTHONASYNCIODEBUG", False))
|
||||
|
||||
self._application.aboutToQuit.connect(self._about_to_quit_cb)
|
||||
|
||||
# Running and stopping the loop
|
||||
|
||||
def _run_until_complete_cb(self, future: futures.QAsyncioFuture) -> None:
|
||||
"""
|
||||
A callback that stops the loop when the future is done, used when
|
||||
running the loop with run_until_complete().
|
||||
"""
|
||||
if not future.cancelled():
|
||||
if isinstance(future.exception(), (SystemExit, KeyboardInterrupt)):
|
||||
return
|
||||
future.get_loop().stop()
|
||||
|
||||
def run_until_complete(self,
|
||||
future: futures.QAsyncioFuture) -> Any: # type: ignore[override]
|
||||
if self.is_closed():
|
||||
raise RuntimeError("Event loop is closed")
|
||||
if self.is_running():
|
||||
raise RuntimeError("Event loop is already running")
|
||||
|
||||
arg_was_coro = not asyncio.futures.isfuture(future)
|
||||
future = asyncio.tasks.ensure_future(future, loop=self) # type: ignore[assignment]
|
||||
future.add_done_callback(self._run_until_complete_cb)
|
||||
self._future_to_complete = future
|
||||
|
||||
try:
|
||||
self.run_forever()
|
||||
except Exception as e:
|
||||
if arg_was_coro and future.done() and not future.cancelled():
|
||||
future.exception()
|
||||
raise e
|
||||
finally:
|
||||
future.remove_done_callback(self._run_until_complete_cb)
|
||||
if not future.done():
|
||||
raise RuntimeError("Event loop stopped before Future completed")
|
||||
|
||||
return future.result()
|
||||
|
||||
def run_forever(self) -> None:
|
||||
if self.is_closed():
|
||||
raise RuntimeError("Event loop is closed")
|
||||
if self.is_running():
|
||||
raise RuntimeError("Event loop is already running")
|
||||
asyncio.events._set_running_loop(self)
|
||||
self._application.exec()
|
||||
asyncio.events._set_running_loop(None)
|
||||
|
||||
def _about_to_quit_cb(self):
|
||||
""" A callback for the aboutToQuit signal of the QCoreApplication. """
|
||||
if not self._quit_from_inside:
|
||||
# If the aboutToQuit signal is emitted, the user is closing the
|
||||
# application window or calling QApplication.quit(). In this case,
|
||||
# we want to close the event loop, and we consider this a quit from
|
||||
# outside the loop.
|
||||
self._quit_from_outside = True
|
||||
self.close()
|
||||
|
||||
def stop(self) -> None:
|
||||
if self._future_to_complete is not None:
|
||||
if self._future_to_complete.done():
|
||||
self._future_to_complete = None
|
||||
else:
|
||||
# Do not stop the loop if there is a future still being awaited
|
||||
# with run_until_complete().
|
||||
return
|
||||
|
||||
self._quit_from_inside = True
|
||||
|
||||
# The user might want to keep the QApplication running after the event
|
||||
# event loop finishes, which they can control with the quit_qapp
|
||||
# argument.
|
||||
if self._quit_qapp:
|
||||
self._application.quit()
|
||||
|
||||
def is_running(self) -> bool:
|
||||
return self._thread.loopLevel() > 0
|
||||
|
||||
def is_closed(self) -> bool:
|
||||
return self._closed
|
||||
|
||||
def close(self) -> None:
|
||||
if self.is_running() and not self._quit_from_outside:
|
||||
raise RuntimeError("Cannot close a running event loop")
|
||||
if self.is_closed():
|
||||
return
|
||||
if self._default_executor is not None:
|
||||
self._default_executor.shutdown(wait=False)
|
||||
self._closed = True
|
||||
|
||||
async def shutdown_asyncgens(self) -> None:
|
||||
if not len(self._asyncgens):
|
||||
return
|
||||
|
||||
results = await asyncio.tasks.gather(
|
||||
*[asyncgen.aclose() for asyncgen in self._asyncgens],
|
||||
return_exceptions=True)
|
||||
|
||||
for result, asyncgen in zip(results, self._asyncgens):
|
||||
if isinstance(result, Exception):
|
||||
self.call_exception_handler({
|
||||
"message": f"Closing asynchronous generator {asyncgen}"
|
||||
f"raised an exception",
|
||||
"exception": result,
|
||||
"asyncgen": asyncgen})
|
||||
|
||||
self._asyncgens.clear()
|
||||
|
||||
async def shutdown_default_executor(self, # type: ignore[override]
|
||||
timeout: int | float | None = None) -> None:
|
||||
shutdown_successful = False
|
||||
if timeout is not None:
|
||||
deadline_timer = QDeadlineTimer(int(timeout * 1000))
|
||||
else:
|
||||
deadline_timer = QDeadlineTimer(QDeadlineTimer.ForeverConstant.Forever)
|
||||
|
||||
if self._default_executor is None:
|
||||
return
|
||||
future = self.create_future()
|
||||
thread = QAsyncioEventLoop.ShutDownThread(future, self)
|
||||
thread.start()
|
||||
try:
|
||||
await future
|
||||
finally:
|
||||
shutdown_successful = thread.wait(deadline_timer)
|
||||
|
||||
if timeout is not None and not shutdown_successful:
|
||||
warnings.warn(
|
||||
f"Could not shutdown the default executor within {timeout} seconds",
|
||||
RuntimeWarning, stacklevel=2)
|
||||
self._default_executor.shutdown(wait=False)
|
||||
|
||||
# Scheduling callbacks
|
||||
|
||||
def _call_soon_impl(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts],
|
||||
context: Context | None = None,
|
||||
is_threadsafe: bool | None = False) -> asyncio.Handle:
|
||||
return self._call_later_impl(0, callback, *args, context=context,
|
||||
is_threadsafe=is_threadsafe)
|
||||
|
||||
def call_soon(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts],
|
||||
context: Context | None = None) -> asyncio.Handle:
|
||||
return self._call_soon_impl(callback, *args, context=context, is_threadsafe=False)
|
||||
|
||||
def call_soon_threadsafe(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts],
|
||||
context: Context | None = None) -> asyncio.Handle:
|
||||
if self.is_closed():
|
||||
raise RuntimeError("Event loop is closed")
|
||||
if context is None:
|
||||
context = contextvars.copy_context()
|
||||
return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
|
||||
|
||||
def _call_later_impl(self, delay: float, callback: Callable[[Unpack[_Ts]], object],
|
||||
*args: Unpack[_Ts], context: Context | None = None,
|
||||
is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
|
||||
if not isinstance(delay, (int, float)):
|
||||
raise TypeError("delay must be an int or float")
|
||||
return self._call_at_impl(self.time() + delay, callback, *args,
|
||||
context=context, is_threadsafe=is_threadsafe)
|
||||
|
||||
def call_later(self, delay: float, callback: Callable[[Unpack[_Ts]], object],
|
||||
*args: Unpack[_Ts], context: Context | None = None) -> asyncio.TimerHandle:
|
||||
return self._call_later_impl(delay, callback, *args, context=context, is_threadsafe=False)
|
||||
|
||||
def _call_at_impl(self, when: float, callback: Callable[[Unpack[_Ts]], object],
|
||||
*args: Unpack[_Ts], context: Context | None = None,
|
||||
is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
|
||||
""" All call_at() and call_later() methods map to this method. """
|
||||
if not isinstance(when, (int, float)):
|
||||
raise TypeError("when must be an int or float")
|
||||
return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
|
||||
|
||||
def call_at(self, when: float, callback: Callable[[Unpack[_Ts]], object],
|
||||
*args: Unpack[_Ts], context: Context | None = None) -> asyncio.TimerHandle:
|
||||
return self._call_at_impl(when, callback, *args, context=context, is_threadsafe=False)
|
||||
|
||||
def time(self) -> float:
|
||||
return QDateTime.currentMSecsSinceEpoch() / 1000.0
|
||||
|
||||
# Creating Futures and Tasks
|
||||
|
||||
def create_future(self) -> futures.QAsyncioFuture: # type: ignore[override]
|
||||
return futures.QAsyncioFuture(loop=self)
|
||||
|
||||
def create_task(self, # type: ignore[override]
|
||||
coro: collections.abc.Generator | collections.abc.Coroutine,
|
||||
*, name: str | None = None,
|
||||
context: contextvars.Context | None = None) -> tasks.QAsyncioTask:
|
||||
if self._task_factory is None:
|
||||
task = tasks.QAsyncioTask(coro, loop=self, name=name, context=context)
|
||||
else:
|
||||
task = self._task_factory(self, coro, context=context)
|
||||
task.set_name(name)
|
||||
|
||||
return task
|
||||
|
||||
def set_task_factory(self, factory: Callable | None) -> None:
|
||||
if factory is not None and not callable(factory):
|
||||
raise TypeError("The task factory must be a callable or None")
|
||||
self._task_factory = factory
|
||||
|
||||
def get_task_factory(self) -> Callable | None:
|
||||
return self._task_factory
|
||||
|
||||
# Opening network connections
|
||||
|
||||
async def create_connection(
|
||||
self, protocol_factory, host=None, port=None,
|
||||
*, ssl=None, family=0, proto=0,
|
||||
flags=0, sock=None, local_addr=None,
|
||||
server_hostname=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None,
|
||||
happy_eyeballs_delay=None, interleave=None):
|
||||
raise NotImplementedError("QAsyncioEventLoop.create_connection() is not implemented yet")
|
||||
|
||||
async def create_datagram_endpoint(self, protocol_factory,
|
||||
local_addr=None, remote_addr=None, *,
|
||||
family=0, proto=0, flags=0,
|
||||
reuse_address=None, reuse_port=None,
|
||||
allow_broadcast=None, sock=None):
|
||||
raise NotImplementedError(
|
||||
"QAsyncioEventLoop.create_datagram_endpoint() is not implemented yet")
|
||||
|
||||
async def create_unix_connection(
|
||||
self, protocol_factory, path=None, *,
|
||||
ssl=None, sock=None,
|
||||
server_hostname=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None):
|
||||
raise NotImplementedError(
|
||||
"QAsyncioEventLoop.create_unix_connection() is not implemented yet")
|
||||
|
||||
# Creating network servers
|
||||
|
||||
async def create_server(
|
||||
self, protocol_factory, host=None, port=None,
|
||||
*, family=socket.AF_UNSPEC,
|
||||
flags=socket.AI_PASSIVE, sock=None, backlog=100,
|
||||
ssl=None, reuse_address=None, reuse_port=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None,
|
||||
start_serving=True):
|
||||
raise NotImplementedError("QAsyncioEventLoop.create_server() is not implemented yet")
|
||||
|
||||
async def create_unix_server(
|
||||
self, protocol_factory, path=None, *,
|
||||
sock=None, backlog=100, ssl=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None,
|
||||
start_serving=True):
|
||||
raise NotImplementedError("QAsyncioEventLoop.create_unix_server() is not implemented yet")
|
||||
|
||||
async def connect_accepted_socket(
|
||||
self, protocol_factory, sock,
|
||||
*, ssl=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None):
|
||||
raise NotImplementedError(
|
||||
"QAsyncioEventLoop.connect_accepted_socket() is not implemented yet")
|
||||
|
||||
# Transferring files
|
||||
|
||||
async def sendfile(self, transport, file, offset=0, count=None,
|
||||
*, fallback=True):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sendfile() is not implemented yet")
|
||||
|
||||
# TLS Upgrade
|
||||
|
||||
async def start_tls(self, transport, protocol, sslcontext, *,
|
||||
server_side=False,
|
||||
server_hostname=None,
|
||||
ssl_handshake_timeout=None,
|
||||
ssl_shutdown_timeout=None):
|
||||
raise NotImplementedError("QAsyncioEventLoop.start_tls() is not implemented yet")
|
||||
|
||||
# Watching file descriptors
|
||||
|
||||
def add_reader(self, fd, callback, *args):
|
||||
raise NotImplementedError("QAsyncioEventLoop.add_reader() is not implemented yet")
|
||||
|
||||
def remove_reader(self, fd):
|
||||
raise NotImplementedError("QAsyncioEventLoop.remove_reader() is not implemented yet")
|
||||
|
||||
def add_writer(self, fd, callback, *args):
|
||||
raise NotImplementedError("QAsyncioEventLoop.add_writer() is not implemented yet")
|
||||
|
||||
def remove_writer(self, fd):
|
||||
raise NotImplementedError("QAsyncioEventLoop.remove_writer() is not implemented yet")
|
||||
|
||||
# Working with socket objects directly
|
||||
|
||||
async def sock_recv(self, sock, nbytes):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_recv() is not implemented yet")
|
||||
|
||||
async def sock_recv_into(self, sock, buf):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_recv_into() is not implemented yet")
|
||||
|
||||
async def sock_recvfrom(self, sock, bufsize):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom() is not implemented yet")
|
||||
|
||||
async def sock_recvfrom_into(self, sock, buf, nbytes=0):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom_into() is not implemented yet")
|
||||
|
||||
async def sock_sendall(self, sock, data):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_sendall() is not implemented yet")
|
||||
|
||||
async def sock_sendto(self, sock, data, address):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_sendto() is not implemented yet")
|
||||
|
||||
async def sock_connect(self, sock, address):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_connect() is not implemented yet")
|
||||
|
||||
async def sock_accept(self, sock):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_accept() is not implemented yet")
|
||||
|
||||
async def sock_sendfile(self, sock, file, offset=0, count=None, *,
|
||||
fallback=None):
|
||||
raise NotImplementedError("QAsyncioEventLoop.sock_sendfile() is not implemented yet")
|
||||
|
||||
# DNS
|
||||
|
||||
async def getaddrinfo(self, host, port, *,
|
||||
family=0, type=0, proto=0, flags=0):
|
||||
raise NotImplementedError("QAsyncioEventLoop.getaddrinfo() is not implemented yet")
|
||||
|
||||
async def getnameinfo(self, sockaddr, flags=0):
|
||||
raise NotImplementedError("QAsyncioEventLoop.getnameinfo() is not implemented yet")
|
||||
|
||||
# Working with pipes
|
||||
|
||||
async def connect_read_pipe(self, protocol_factory, pipe):
|
||||
raise NotImplementedError("QAsyncioEventLoop.connect_read_pipe() is not implemented yet")
|
||||
|
||||
async def connect_write_pipe(self, protocol_factory, pipe):
|
||||
raise NotImplementedError("QAsyncioEventLoop.connect_write_pipe() is not implemented yet")
|
||||
|
||||
# Unix signals
|
||||
|
||||
def add_signal_handler(self, sig, callback, *args):
|
||||
raise NotImplementedError("QAsyncioEventLoop.add_signal_handler() is not implemented yet")
|
||||
|
||||
def remove_signal_handler(self, sig):
|
||||
raise NotImplementedError(
|
||||
"QAsyncioEventLoop.remove_signal_handler() is not implemented yet")
|
||||
|
||||
# Executing code in thread or process pools
|
||||
|
||||
def run_in_executor(self, executor: concurrent.futures.ThreadPoolExecutor | None,
|
||||
func: Callable[[Unpack[_Ts]], _T],
|
||||
*args: Unpack[_Ts]) -> asyncio.Future[_T]:
|
||||
if self.is_closed():
|
||||
raise RuntimeError("Event loop is closed")
|
||||
if executor is None:
|
||||
executor = self._default_executor
|
||||
|
||||
# Executors require a bit of extra work for QtAsyncio, as we can't use
|
||||
# naked Python threads; instead, we must make sure that the thread
|
||||
# created by executor.submit() has an event loop. This is achieved by
|
||||
# not submitting the callable directly, but a small wrapper that
|
||||
# attaches a QEventLoop to the executor thread, and then pushes the
|
||||
# actual callable for the executor into this new event loop.
|
||||
wrapper = QAsyncioExecutorWrapper(func, *args)
|
||||
return asyncio.futures.wrap_future(executor.submit(wrapper.do), loop=self)
|
||||
|
||||
def set_default_executor(self,
|
||||
executor: concurrent.futures.ThreadPoolExecutor | None) -> None:
|
||||
if not isinstance(executor, concurrent.futures.ThreadPoolExecutor):
|
||||
raise TypeError("The executor must be a ThreadPoolExecutor")
|
||||
self._default_executor = executor
|
||||
|
||||
# Error Handling API
|
||||
|
||||
def set_exception_handler(self, handler: Callable | None) -> None:
|
||||
if handler is not None and not callable(handler):
|
||||
raise TypeError("The handler must be a callable or None")
|
||||
self._exception_handler = handler
|
||||
|
||||
def get_exception_handler(self) -> Callable | None:
|
||||
return self._exception_handler
|
||||
|
||||
def default_exception_handler(self, context: dict[str, Any]) -> None:
|
||||
# TODO
|
||||
if context["message"]:
|
||||
print(f"{context['message']} from task {context['task']._name},"
|
||||
"read the following traceback:")
|
||||
print(context["traceback"])
|
||||
|
||||
def call_exception_handler(self, context: dict[str, Any]) -> None:
|
||||
if self._exception_handler is not None:
|
||||
self._exception_handler(context)
|
||||
|
||||
# Enabling debug mode
|
||||
|
||||
def get_debug(self) -> bool:
|
||||
# TODO: Part of the asyncio API but currently unused. More details:
|
||||
# https://docs.python.org/3/library/asyncio-dev.html#asyncio-debug-mode
|
||||
return self._debug
|
||||
|
||||
def set_debug(self, enabled: bool) -> None:
|
||||
self._debug = enabled
|
||||
|
||||
# Running subprocesses
|
||||
|
||||
async def subprocess_exec(self, protocol_factory, *args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
**kwargs):
|
||||
raise NotImplementedError("QAsyncioEventLoop.subprocess_exec() is not implemented yet")
|
||||
|
||||
async def subprocess_shell(self, protocol_factory, cmd, *,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
**kwargs):
|
||||
raise NotImplementedError("QAsyncioEventLoop.subprocess_shell() is not implemented yet")
|
||||
|
||||
|
||||
class QAsyncioHandle():
|
||||
"""
|
||||
The handle enqueues a callback to be executed by the event loop, and allows
|
||||
for this callback to be cancelled before it is executed. This callback will
|
||||
typically execute the step function for a task. This makes the handle one
|
||||
of the main components of asyncio.
|
||||
"""
|
||||
class HandleState(enum.Enum):
|
||||
PENDING = enum.auto()
|
||||
CANCELLED = enum.auto()
|
||||
DONE = enum.auto()
|
||||
|
||||
def __init__(self, callback: Callable, args: tuple,
|
||||
loop: QAsyncioEventLoop, context: contextvars.Context | None,
|
||||
is_threadsafe: bool | None = False) -> None:
|
||||
self._callback = callback
|
||||
self._cb_args = args # renamed from _args to avoid conflict with TimerHandle._args
|
||||
self._loop = loop
|
||||
self._context = context
|
||||
self._is_threadsafe = is_threadsafe
|
||||
self._timeout = 0
|
||||
self._state = QAsyncioHandle.HandleState.PENDING
|
||||
self._start()
|
||||
|
||||
def _start(self) -> None:
|
||||
self._schedule_event(self._timeout, lambda: self._cb())
|
||||
|
||||
def _schedule_event(self, timeout: int, func: Callable) -> None:
|
||||
# Do not schedule events from asyncio when the app is quit from outside
|
||||
# the event loop, as this would cause events to be enqueued after the
|
||||
# event loop was destroyed.
|
||||
if not self._loop.is_closed() and not self._loop._quit_from_outside:
|
||||
if self._is_threadsafe:
|
||||
# This singleShot overload will push func into self._loop
|
||||
# instead of the current thread's loop. This allows scheduling
|
||||
# a callback from a different thread, which is necessary for
|
||||
# thread-safety.
|
||||
# https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading
|
||||
QTimer.singleShot(timeout, self._loop, func)
|
||||
else:
|
||||
QTimer.singleShot(timeout, func)
|
||||
|
||||
@Slot()
|
||||
def _cb(self) -> None:
|
||||
"""
|
||||
A slot, enqueued into the event loop, that wraps around the actual
|
||||
callback, typically the step function of a task.
|
||||
"""
|
||||
if self._state == QAsyncioHandle.HandleState.PENDING:
|
||||
if self._context is not None:
|
||||
self._context.run(self._callback, *self._cb_args)
|
||||
else:
|
||||
self._callback(*self._cb_args)
|
||||
self._state = QAsyncioHandle.HandleState.DONE
|
||||
|
||||
def cancel(self) -> None:
|
||||
if self._state == QAsyncioHandle.HandleState.PENDING:
|
||||
# The old timer that was created in _start will still trigger but
|
||||
# _cb won't do anything, therefore the callback is effectively
|
||||
# cancelled.
|
||||
self._state = QAsyncioHandle.HandleState.CANCELLED
|
||||
|
||||
def cancelled(self) -> bool:
|
||||
return self._state == QAsyncioHandle.HandleState.CANCELLED
|
||||
|
||||
|
||||
class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
|
||||
def __init__(self, when: float, callback: Callable, args: tuple,
|
||||
loop: QAsyncioEventLoop, context: contextvars.Context | None,
|
||||
is_threadsafe: bool | None = False) -> None:
|
||||
QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
|
||||
|
||||
self._when = when
|
||||
time = self._loop.time()
|
||||
|
||||
# PYSIDE-2644: Timeouts should be rounded up or down instead of only up
|
||||
# as happens with int(). Otherwise, a timeout of e.g. 0.9 would be
|
||||
# handled as 0, where 1 would be more appropriate.
|
||||
self._timeout = round(max(self._when - time, 0) * 1000)
|
||||
|
||||
QAsyncioHandle._start(self)
|
||||
|
||||
def _start(self) -> None:
|
||||
"""
|
||||
Overridden so that timer.start() is only called once at the end of the
|
||||
constructor for both QtHandle and QtTimerHandle.
|
||||
"""
|
||||
pass
|
||||
|
||||
def when(self) -> float:
|
||||
return self._when
|
||||
119
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/futures.py
vendored
Normal file
119
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/futures.py
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
from __future__ import annotations
|
||||
|
||||
from . import events
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import enum
|
||||
|
||||
|
||||
class QAsyncioFuture():
|
||||
""" https://docs.python.org/3/library/asyncio-future.html """
|
||||
|
||||
# Declare that this class implements the Future protocol. The field must
|
||||
# exist and be boolean - True indicates 'await' or 'yield from', False
|
||||
# indicates 'yield'.
|
||||
_asyncio_future_blocking = False
|
||||
|
||||
class FutureState(enum.Enum):
|
||||
PENDING = enum.auto()
|
||||
CANCELLED = enum.auto()
|
||||
DONE_WITH_RESULT = enum.auto()
|
||||
DONE_WITH_EXCEPTION = enum.auto()
|
||||
|
||||
def __init__(self, *, loop: "events.QAsyncioEventLoop | None" = None,
|
||||
context: contextvars.Context | None = None) -> None:
|
||||
self._loop: "events.QAsyncioEventLoop"
|
||||
if loop is None:
|
||||
self._loop = asyncio.events.get_event_loop() # type: ignore[assignment]
|
||||
else:
|
||||
self._loop = loop
|
||||
self._context = context
|
||||
|
||||
self._state = QAsyncioFuture.FutureState.PENDING
|
||||
self._result: Any = None
|
||||
self._exception: BaseException | None = None
|
||||
|
||||
self._cancel_message: str | None = None
|
||||
|
||||
# List of callbacks that are called when the future is done.
|
||||
self._callbacks: list[Callable] = list()
|
||||
|
||||
def __await__(self):
|
||||
if not self.done():
|
||||
self._asyncio_future_blocking = True
|
||||
yield self
|
||||
if not self.done():
|
||||
raise RuntimeError("await was not used with a Future or Future-like object")
|
||||
return self.result()
|
||||
|
||||
__iter__ = __await__
|
||||
|
||||
def _schedule_callbacks(self, context: contextvars.Context | None = None):
|
||||
""" A future can optionally have callbacks that are called when the future is done. """
|
||||
for cb in self._callbacks:
|
||||
self._loop.call_soon(
|
||||
cb, self, context=context if context else self._context)
|
||||
|
||||
def result(self) -> Any | Exception:
|
||||
if self._state == QAsyncioFuture.FutureState.DONE_WITH_RESULT:
|
||||
return self._result
|
||||
if self._state == QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION and self._exception:
|
||||
raise self._exception
|
||||
if self._state == QAsyncioFuture.FutureState.CANCELLED:
|
||||
if self._cancel_message:
|
||||
raise asyncio.CancelledError(self._cancel_message)
|
||||
else:
|
||||
raise asyncio.CancelledError
|
||||
raise asyncio.InvalidStateError
|
||||
|
||||
def set_result(self, result: Any) -> None:
|
||||
self._result = result
|
||||
self._state = QAsyncioFuture.FutureState.DONE_WITH_RESULT
|
||||
self._schedule_callbacks()
|
||||
|
||||
def set_exception(self, exception: Exception) -> None:
|
||||
self._exception = exception
|
||||
self._state = QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION
|
||||
self._schedule_callbacks()
|
||||
|
||||
def done(self) -> bool:
|
||||
return self._state != QAsyncioFuture.FutureState.PENDING
|
||||
|
||||
def cancelled(self) -> bool:
|
||||
return self._state == QAsyncioFuture.FutureState.CANCELLED
|
||||
|
||||
def add_done_callback(self, cb: Callable, *,
|
||||
context: contextvars.Context | None = None) -> None:
|
||||
if self.done():
|
||||
self._loop.call_soon(
|
||||
cb, self, context=context if context else self._context)
|
||||
else:
|
||||
self._callbacks.append(cb)
|
||||
|
||||
def remove_done_callback(self, cb: Callable) -> int:
|
||||
original_len = len(self._callbacks)
|
||||
self._callbacks = [_cb for _cb in self._callbacks if _cb != cb]
|
||||
return original_len - len(self._callbacks)
|
||||
|
||||
def cancel(self, msg: str | None = None) -> bool:
|
||||
if self.done():
|
||||
return False
|
||||
self._state = QAsyncioFuture.FutureState.CANCELLED
|
||||
self._cancel_message = msg
|
||||
self._schedule_callbacks()
|
||||
return True
|
||||
|
||||
def exception(self) -> BaseException | None:
|
||||
if self._state == QAsyncioFuture.FutureState.CANCELLED:
|
||||
raise asyncio.CancelledError
|
||||
if self.done():
|
||||
return self._exception
|
||||
raise asyncio.InvalidStateError
|
||||
|
||||
def get_loop(self) -> asyncio.AbstractEventLoop:
|
||||
return self._loop
|
||||
216
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/tasks.py
vendored
Normal file
216
etl_billiards/dist/ETL_Manager/_internal/PySide6/QtAsyncio/tasks.py
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
from __future__ import annotations
|
||||
|
||||
from . import events
|
||||
from . import futures
|
||||
import traceback
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
import asyncio
|
||||
import collections.abc
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
|
||||
|
||||
class QAsyncioTask(futures.QAsyncioFuture):
|
||||
""" https://docs.python.org/3/library/asyncio-task.html """
|
||||
|
||||
def __init__(self, coro: collections.abc.Generator | collections.abc.Coroutine, *,
|
||||
loop: "events.QAsyncioEventLoop | None" = None, name: str | None = None,
|
||||
context: contextvars.Context | None = None) -> None:
|
||||
super().__init__(loop=loop, context=context)
|
||||
self._source_traceback = None # required for Python < 3.11
|
||||
|
||||
self._state: futures.QAsyncioFuture.FutureState = futures.QAsyncioFuture.FutureState.PENDING
|
||||
self._exception: Optional[BaseException] = None
|
||||
|
||||
self._coro = coro # The coroutine for which this task was created.
|
||||
self._name = name if name else "QtTask"
|
||||
|
||||
# The task creates a handle for its coroutine. The handle enqueues the
|
||||
# task's step function as its callback in the event loop.
|
||||
self._loop.call_soon(self._step, context=self._context)
|
||||
|
||||
# The task step function executes the coroutine until it finishes,
|
||||
# raises an exception or returns a future. If a future was returned,
|
||||
# the task will await its completion (or exception). If the task is
|
||||
# cancelled while it awaits a future, this future must also be
|
||||
# cancelled in order for the cancellation to be successful.
|
||||
self._future_to_await: asyncio.Future | None = None
|
||||
|
||||
self._cancelled = False # PYSIDE-2644; see _step
|
||||
self._cancel_count = 0
|
||||
self._cancel_message: str | None = None
|
||||
# Store traceback in case of Exception. Useful when exception happens in coroutine
|
||||
self._tb: str | None = None
|
||||
|
||||
# https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
|
||||
asyncio._register_task(self) # type: ignore[arg-type]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
state: str = "Unknown"
|
||||
if self._state == futures.QAsyncioFuture.FutureState.PENDING:
|
||||
state = "Pending"
|
||||
elif self._state == futures.QAsyncioFuture.FutureState.DONE_WITH_RESULT:
|
||||
state = "Done"
|
||||
elif self._state == futures.QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION:
|
||||
state = f"Done with exception ({repr(self._exception)})"
|
||||
elif self._state == futures.QAsyncioFuture.FutureState.CANCELLED:
|
||||
state = "Cancelled"
|
||||
|
||||
return f"Task '{self.get_name()}' with state: {state}"
|
||||
|
||||
class QtTaskApiMisuseError(Exception):
|
||||
pass
|
||||
|
||||
def set_result(self, result: Any) -> None: # type: ignore[override]
|
||||
# This function is not inherited from the Future APIs.
|
||||
raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set results")
|
||||
|
||||
def set_exception(self, exception: Any) -> None: # type: ignore[override]
|
||||
# This function is not inherited from the Future APIs.
|
||||
raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set exceptions")
|
||||
|
||||
def _step(self,
|
||||
exception_or_future: BaseException | futures.QAsyncioFuture | None = None) -> None:
|
||||
"""
|
||||
The step function is the heart of a task. It is scheduled in the event
|
||||
loop repeatedly, executing the coroutine "step" by "step" (i.e.,
|
||||
iterating through the asynchronous generator) until it finishes with an
|
||||
exception or successfully. Each step can optionally receive an
|
||||
exception or a future as a result from a previous step to handle.
|
||||
"""
|
||||
|
||||
if self.done():
|
||||
return
|
||||
result = None
|
||||
self._future_to_await = None
|
||||
|
||||
if self._cancelled:
|
||||
exception_or_future = asyncio.CancelledError(self._cancel_message)
|
||||
self._cancelled = False
|
||||
|
||||
if asyncio.futures.isfuture(exception_or_future):
|
||||
try:
|
||||
exception_or_future.result()
|
||||
except BaseException as e:
|
||||
exception_or_future = e
|
||||
|
||||
try:
|
||||
asyncio._enter_task(self._loop, self) # type: ignore[arg-type]
|
||||
|
||||
# It is at this point that the coroutine is resumed for the current
|
||||
# step (i.e. asynchronous generator iteration). It will now be
|
||||
# executed until it yields (and potentially returns a future),
|
||||
# raises an exception, is cancelled, or finishes successfully.
|
||||
|
||||
if isinstance(exception_or_future, BaseException):
|
||||
# If the coroutine doesn't handle this exception, it propagates
|
||||
# to the caller.
|
||||
result = self._coro.throw(exception_or_future)
|
||||
else:
|
||||
result = self._coro.send(None)
|
||||
except StopIteration as e:
|
||||
self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_RESULT
|
||||
self._result = e.value
|
||||
except (concurrent.futures.CancelledError, asyncio.exceptions.CancelledError) as e:
|
||||
self._state = futures.QAsyncioFuture.FutureState.CANCELLED
|
||||
self._exception = e
|
||||
except BaseException as e:
|
||||
self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION
|
||||
self._exception = e
|
||||
self._tb = traceback.format_exc()
|
||||
else:
|
||||
if asyncio.futures.isfuture(result):
|
||||
# If the coroutine yields a future, the task will await its
|
||||
# completion, and at that point the step function will be
|
||||
# called again.
|
||||
result.add_done_callback(
|
||||
self._step, context=self._context) # type: ignore[arg-type]
|
||||
|
||||
# The task will await the completion (or exception) of this
|
||||
# future. If the task is cancelled while it awaits a future,
|
||||
# this future must also be cancelled.
|
||||
self._future_to_await = result
|
||||
|
||||
if self._cancelled:
|
||||
# PYSIDE-2644: If the task was cancelled at this step and a
|
||||
# new future was created to be awaited, then it should be
|
||||
# cancelled as well. Otherwise, in some scenarios like a
|
||||
# loop inside the task and with bad timing, if the new
|
||||
# future is not cancelled, the task would continue running
|
||||
# in this loop despite having been cancelled. This bad
|
||||
# timing can occur especially if the first future finishes
|
||||
# very quickly.
|
||||
self._future_to_await.cancel(self._cancel_message)
|
||||
elif result is None:
|
||||
# If no future was yielded, we schedule the step function again
|
||||
# without any arguments.
|
||||
self._loop.call_soon(self._step, context=self._context)
|
||||
else:
|
||||
# This is not supposed to happen.
|
||||
exception = RuntimeError(f"Bad task result: {result}")
|
||||
self._loop.call_soon(self._step, exception, context=self._context)
|
||||
finally:
|
||||
asyncio._leave_task(self._loop, self) # type: ignore[arg-type]
|
||||
|
||||
if self._exception:
|
||||
message = str(self._exception)
|
||||
if message == "None":
|
||||
message = ""
|
||||
else:
|
||||
message = "An exception occurred during task execution"
|
||||
self._loop.call_exception_handler({
|
||||
"message": message,
|
||||
"exception": self._exception,
|
||||
"task": self,
|
||||
"future": (exception_or_future
|
||||
if asyncio.futures.isfuture(exception_or_future)
|
||||
else None),
|
||||
"traceback": self._tb
|
||||
})
|
||||
|
||||
if self.done():
|
||||
self._schedule_callbacks()
|
||||
|
||||
# https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
|
||||
asyncio._unregister_task(self) # type: ignore[arg-type]
|
||||
|
||||
def get_stack(self, *, limit=None) -> list[Any]:
|
||||
# TODO
|
||||
raise NotImplementedError("QtTask.get_stack is not implemented")
|
||||
|
||||
def print_stack(self, *, limit=None, file=None) -> None:
|
||||
# TODO
|
||||
raise NotImplementedError("QtTask.print_stack is not implemented")
|
||||
|
||||
def get_coro(self) -> collections.abc.Generator | collections.abc.Coroutine:
|
||||
return self._coro
|
||||
|
||||
def get_name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def set_name(self, value) -> None:
|
||||
self._name = str(value)
|
||||
|
||||
def cancel(self, msg: str | None = None) -> bool:
|
||||
if self.done():
|
||||
return False
|
||||
self._cancel_count += 1
|
||||
self._cancel_message = msg
|
||||
if self._future_to_await is not None:
|
||||
# A task that is awaiting a future must also cancel this future in
|
||||
# order for the cancellation to be successful.
|
||||
self._future_to_await.cancel(msg)
|
||||
self._cancelled = True # PYSIDE-2644; see _step
|
||||
return True
|
||||
|
||||
def uncancel(self) -> int:
|
||||
if self._cancel_count > 0:
|
||||
self._cancel_count -= 1
|
||||
return self._cancel_count
|
||||
|
||||
def cancelling(self) -> int:
|
||||
return self._cancel_count
|
||||
Reference in New Issue
Block a user