From 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:10:44 -0400 Subject: venv --- .../python3.11/site-packages/uvloop/__init__.py | 168 + .../uvloop/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 7929 bytes .../uvloop/__pycache__/_noop.cpython-311.pyc | Bin 0 -> 366 bytes .../uvloop/__pycache__/_testbase.cpython-311.pyc | Bin 0 -> 31881 bytes .../uvloop/__pycache__/_version.cpython-311.pyc | Bin 0 -> 213 bytes venv/lib/python3.11/site-packages/uvloop/_noop.py | 3 + .../python3.11/site-packages/uvloop/_testbase.py | 550 ++++ .../python3.11/site-packages/uvloop/_version.py | 13 + .../python3.11/site-packages/uvloop/cbhandles.pxd | 39 + .../python3.11/site-packages/uvloop/cbhandles.pyx | 434 +++ venv/lib/python3.11/site-packages/uvloop/dns.pyx | 471 +++ .../lib/python3.11/site-packages/uvloop/errors.pyx | 113 + .../site-packages/uvloop/handles/async_.pxd | 11 + .../site-packages/uvloop/handles/async_.pyx | 56 + .../site-packages/uvloop/handles/basetransport.pxd | 54 + .../site-packages/uvloop/handles/basetransport.pyx | 293 ++ .../site-packages/uvloop/handles/check.pxd | 14 + .../site-packages/uvloop/handles/check.pyx | 72 + .../site-packages/uvloop/handles/fsevent.pxd | 12 + .../site-packages/uvloop/handles/fsevent.pyx | 116 + .../site-packages/uvloop/handles/handle.pxd | 48 + .../site-packages/uvloop/handles/handle.pyx | 395 +++ .../site-packages/uvloop/handles/idle.pxd | 14 + .../site-packages/uvloop/handles/idle.pyx | 72 + .../site-packages/uvloop/handles/pipe.pxd | 33 + .../site-packages/uvloop/handles/pipe.pyx | 226 ++ .../site-packages/uvloop/handles/poll.pxd | 25 + .../site-packages/uvloop/handles/poll.pyx | 233 ++ .../site-packages/uvloop/handles/process.pxd | 80 + .../site-packages/uvloop/handles/process.pyx | 792 +++++ .../site-packages/uvloop/handles/stream.pxd | 50 + .../site-packages/uvloop/handles/stream.pyx | 1015 ++++++ .../site-packages/uvloop/handles/streamserver.pxd | 26 + .../site-packages/uvloop/handles/streamserver.pyx | 150 + .../site-packages/uvloop/handles/tcp.pxd | 26 + .../site-packages/uvloop/handles/tcp.pyx | 228 ++ .../site-packages/uvloop/handles/timer.pxd | 18 + .../site-packages/uvloop/handles/timer.pyx | 89 + .../site-packages/uvloop/handles/udp.pxd | 22 + .../site-packages/uvloop/handles/udp.pyx | 409 +++ .../site-packages/uvloop/includes/__init__.py | 23 + .../includes/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 749 bytes .../site-packages/uvloop/includes/consts.pxi | 25 + .../site-packages/uvloop/includes/debug.pxd | 3 + .../site-packages/uvloop/includes/flowcontrol.pxd | 23 + .../site-packages/uvloop/includes/python.pxd | 31 + .../site-packages/uvloop/includes/stdlib.pxi | 175 + .../site-packages/uvloop/includes/system.pxd | 96 + .../site-packages/uvloop/includes/uv.pxd | 506 +++ .../uvloop/loop.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 11727344 bytes venv/lib/python3.11/site-packages/uvloop/loop.pxd | 229 ++ venv/lib/python3.11/site-packages/uvloop/loop.pyi | 297 ++ venv/lib/python3.11/site-packages/uvloop/loop.pyx | 3403 ++++++++++++++++++++ venv/lib/python3.11/site-packages/uvloop/lru.pyx | 79 + .../python3.11/site-packages/uvloop/pseudosock.pyx | 209 ++ venv/lib/python3.11/site-packages/uvloop/py.typed | 0 .../python3.11/site-packages/uvloop/request.pxd | 8 + .../python3.11/site-packages/uvloop/request.pyx | 65 + .../lib/python3.11/site-packages/uvloop/server.pxd | 19 + .../lib/python3.11/site-packages/uvloop/server.pyx | 136 + .../python3.11/site-packages/uvloop/sslproto.pxd | 138 + .../python3.11/site-packages/uvloop/sslproto.pyx | 950 ++++++ 62 files changed, 12785 insertions(+) create mode 100644 venv/lib/python3.11/site-packages/uvloop/__init__.py create mode 100644 venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/uvloop/_noop.py create mode 100644 venv/lib/python3.11/site-packages/uvloop/_testbase.py create mode 100644 venv/lib/python3.11/site-packages/uvloop/_version.py create mode 100644 venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/dns.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/errors.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/check.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/check.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/process.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/process.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/timer.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/timer.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/udp.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/handles/udp.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/__init__.py create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/__pycache__/__init__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/consts.pxi create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/debug.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/flowcontrol.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/python.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/stdlib.pxi create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/system.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/includes/uv.pxd create mode 100755 venv/lib/python3.11/site-packages/uvloop/loop.cpython-311-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.11/site-packages/uvloop/loop.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/loop.pyi create mode 100644 venv/lib/python3.11/site-packages/uvloop/loop.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/lru.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/pseudosock.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/py.typed create mode 100644 venv/lib/python3.11/site-packages/uvloop/request.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/request.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/server.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/server.pyx create mode 100644 venv/lib/python3.11/site-packages/uvloop/sslproto.pxd create mode 100644 venv/lib/python3.11/site-packages/uvloop/sslproto.pyx (limited to 'venv/lib/python3.11/site-packages/uvloop') diff --git a/venv/lib/python3.11/site-packages/uvloop/__init__.py b/venv/lib/python3.11/site-packages/uvloop/__init__.py new file mode 100644 index 0000000..9bb6592 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/__init__.py @@ -0,0 +1,168 @@ +import asyncio as __asyncio +import typing as _typing +import sys as _sys +import warnings as _warnings + +from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy + +from . import includes as __includes # NOQA +from .loop import Loop as __BaseLoop # NOQA +from ._version import __version__ # NOQA + + +__all__ = ('new_event_loop', 'install', 'EventLoopPolicy') + + +_T = _typing.TypeVar("_T") + + +class Loop(__BaseLoop, __asyncio.AbstractEventLoop): # type: ignore[misc] + pass + + +def new_event_loop() -> Loop: + """Return a new event loop.""" + return Loop() + + +def install() -> None: + """A helper function to install uvloop policy.""" + if _sys.version_info[:2] >= (3, 12): + _warnings.warn( + 'uvloop.install() is deprecated in favor of uvloop.run() ' + 'starting with Python 3.12.', + DeprecationWarning, + stacklevel=1, + ) + __asyncio.set_event_loop_policy(EventLoopPolicy()) + + +if _typing.TYPE_CHECKING: + def run( + main: _typing.Coroutine[_typing.Any, _typing.Any, _T], + *, + loop_factory: _typing.Optional[ + _typing.Callable[[], Loop] + ] = new_event_loop, + debug: _typing.Optional[bool]=None, + ) -> _T: + """The preferred way of running a coroutine with uvloop.""" +else: + def run(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs): + """The preferred way of running a coroutine with uvloop.""" + + async def wrapper(): + # If `loop_factory` is provided we want it to return + # either uvloop.Loop or a subtype of it, assuming the user + # is using `uvloop.run()` intentionally. + loop = __asyncio._get_running_loop() + if not isinstance(loop, Loop): + raise TypeError('uvloop.run() uses a non-uvloop event loop') + return await main + + vi = _sys.version_info[:2] + + if vi <= (3, 10): + # Copied from python/cpython + + if __asyncio._get_running_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 = loop_factory() + try: + __asyncio.set_event_loop(loop) + if debug is not None: + loop.set_debug(debug) + return loop.run_until_complete(wrapper()) + finally: + try: + _cancel_all_tasks(loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + if hasattr(loop, 'shutdown_default_executor'): + loop.run_until_complete( + loop.shutdown_default_executor() + ) + finally: + __asyncio.set_event_loop(None) + loop.close() + + elif vi == (3, 11): + if __asyncio._get_running_loop() is not None: + raise RuntimeError( + "asyncio.run() cannot be called from a running event loop") + + with __asyncio.Runner( + loop_factory=loop_factory, + debug=debug, + **run_kwargs + ) as runner: + return runner.run(wrapper()) + + else: + assert vi >= (3, 12) + return __asyncio.run( + wrapper(), + loop_factory=loop_factory, + debug=debug, + **run_kwargs + ) + + +def _cancel_all_tasks(loop: __asyncio.AbstractEventLoop) -> None: + # Copied from python/cpython + + to_cancel = __asyncio.all_tasks(loop) + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete( + __asyncio.gather(*to_cancel, 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, + }) + + +class EventLoopPolicy(__BasePolicy): + """Event loop policy. + + The preferred way to make your application use uvloop: + + >>> import asyncio + >>> import uvloop + >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + >>> asyncio.get_event_loop() + + """ + + def _loop_factory(self) -> Loop: + return new_event_loop() + + if _typing.TYPE_CHECKING: + # EventLoopPolicy doesn't implement these, but since they are marked + # as abstract in typeshed, we have to put them in so mypy thinks + # the base methods are overridden. This is the same approach taken + # for the Windows event loop policy classes in typeshed. + def get_child_watcher(self) -> _typing.NoReturn: + ... + + def set_child_watcher( + self, watcher: _typing.Any + ) -> _typing.NoReturn: + ... diff --git a/venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..ebc913e Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc differ diff --git a/venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc new file mode 100644 index 0000000..155eaae Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc differ diff --git a/venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc new file mode 100644 index 0000000..ca115de Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc differ diff --git a/venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc new file mode 100644 index 0000000..e31e416 Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc differ diff --git a/venv/lib/python3.11/site-packages/uvloop/_noop.py b/venv/lib/python3.11/site-packages/uvloop/_noop.py new file mode 100644 index 0000000..bfc14dc --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/_noop.py @@ -0,0 +1,3 @@ +def noop() -> None: + """Empty function to invoke CPython ceval loop.""" + return diff --git a/venv/lib/python3.11/site-packages/uvloop/_testbase.py b/venv/lib/python3.11/site-packages/uvloop/_testbase.py new file mode 100644 index 0000000..c4a7595 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/_testbase.py @@ -0,0 +1,550 @@ +"""Test utilities. Don't use outside of the uvloop project.""" + + +import asyncio +import asyncio.events +import collections +import contextlib +import gc +import logging +import os +import pprint +import re +import select +import socket +import ssl +import sys +import tempfile +import threading +import time +import unittest +import uvloop + + +class MockPattern(str): + def __eq__(self, other): + return bool(re.search(str(self), other, re.S)) + + +class TestCaseDict(collections.UserDict): + + def __init__(self, name): + super().__init__() + self.name = name + + def __setitem__(self, key, value): + if key in self.data: + raise RuntimeError('duplicate test {}.{}'.format( + self.name, key)) + super().__setitem__(key, value) + + +class BaseTestCaseMeta(type): + + @classmethod + def __prepare__(mcls, name, bases): + return TestCaseDict(name) + + def __new__(mcls, name, bases, dct): + for test_name in dct: + if not test_name.startswith('test_'): + continue + for base in bases: + if hasattr(base, test_name): + raise RuntimeError( + 'duplicate test {}.{} (also defined in {} ' + 'parent class)'.format( + name, test_name, base.__name__)) + + return super().__new__(mcls, name, bases, dict(dct)) + + +class BaseTestCase(unittest.TestCase, metaclass=BaseTestCaseMeta): + + def new_loop(self): + raise NotImplementedError + + def new_policy(self): + raise NotImplementedError + + def mock_pattern(self, str): + return MockPattern(str) + + async def wait_closed(self, obj): + if not isinstance(obj, asyncio.StreamWriter): + return + try: + await obj.wait_closed() + except (BrokenPipeError, ConnectionError): + pass + + def is_asyncio_loop(self): + return type(self.loop).__module__.startswith('asyncio.') + + def run_loop_briefly(self, *, delay=0.01): + self.loop.run_until_complete(asyncio.sleep(delay)) + + def loop_exception_handler(self, loop, context): + self.__unhandled_exceptions.append(context) + self.loop.default_exception_handler(context) + + def setUp(self): + self.loop = self.new_loop() + asyncio.set_event_loop_policy(self.new_policy()) + asyncio.set_event_loop(self.loop) + self._check_unclosed_resources_in_debug = True + + self.loop.set_exception_handler(self.loop_exception_handler) + self.__unhandled_exceptions = [] + + def tearDown(self): + self.loop.close() + + if self.__unhandled_exceptions: + print('Unexpected calls to loop.call_exception_handler():') + pprint.pprint(self.__unhandled_exceptions) + self.fail('unexpected calls to loop.call_exception_handler()') + return + + if not self._check_unclosed_resources_in_debug: + return + + # GC to show any resource warnings as the test completes + gc.collect() + gc.collect() + gc.collect() + + if getattr(self.loop, '_debug_cc', False): + gc.collect() + gc.collect() + gc.collect() + + self.assertEqual( + self.loop._debug_uv_handles_total, + self.loop._debug_uv_handles_freed, + 'not all uv_handle_t handles were freed') + + self.assertEqual( + self.loop._debug_cb_handles_count, 0, + 'not all callbacks (call_soon) are GCed') + + self.assertEqual( + self.loop._debug_cb_timer_handles_count, 0, + 'not all timer callbacks (call_later) are GCed') + + self.assertEqual( + self.loop._debug_stream_write_ctx_cnt, 0, + 'not all stream write contexts are GCed') + + for h_name, h_cnt in self.loop._debug_handles_current.items(): + with self.subTest('Alive handle after test', + handle_name=h_name): + self.assertEqual( + h_cnt, 0, + 'alive {} after test'.format(h_name)) + + for h_name, h_cnt in self.loop._debug_handles_total.items(): + with self.subTest('Total/closed handles', + handle_name=h_name): + self.assertEqual( + h_cnt, self.loop._debug_handles_closed[h_name], + 'total != closed for {}'.format(h_name)) + + asyncio.set_event_loop(None) + asyncio.set_event_loop_policy(None) + self.loop = None + + def skip_unclosed_handles_check(self): + self._check_unclosed_resources_in_debug = False + + def tcp_server(self, server_prog, *, + family=socket.AF_INET, + addr=None, + timeout=5, + backlog=1, + max_clients=10): + + if addr is None: + if family == socket.AF_UNIX: + with tempfile.NamedTemporaryFile() as tmp: + addr = tmp.name + else: + addr = ('127.0.0.1', 0) + + sock = socket.socket(family, socket.SOCK_STREAM) + + if timeout is None: + raise RuntimeError('timeout is required') + if timeout <= 0: + raise RuntimeError('only blocking sockets are supported') + sock.settimeout(timeout) + + try: + sock.bind(addr) + sock.listen(backlog) + except OSError as ex: + sock.close() + raise ex + + return TestThreadedServer( + self, sock, server_prog, timeout, max_clients) + + def tcp_client(self, client_prog, + family=socket.AF_INET, + timeout=10): + + sock = socket.socket(family, socket.SOCK_STREAM) + + if timeout is None: + raise RuntimeError('timeout is required') + if timeout <= 0: + raise RuntimeError('only blocking sockets are supported') + sock.settimeout(timeout) + + return TestThreadedClient( + self, sock, client_prog, timeout) + + def unix_server(self, *args, **kwargs): + return self.tcp_server(*args, family=socket.AF_UNIX, **kwargs) + + def unix_client(self, *args, **kwargs): + return self.tcp_client(*args, family=socket.AF_UNIX, **kwargs) + + @contextlib.contextmanager + def unix_sock_name(self): + with tempfile.TemporaryDirectory() as td: + fn = os.path.join(td, 'sock') + try: + yield fn + finally: + try: + os.unlink(fn) + except OSError: + pass + + def _abort_socket_test(self, ex): + try: + self.loop.stop() + finally: + self.fail(ex) + + +def _cert_fullname(test_file_name, cert_file_name): + fullname = os.path.abspath(os.path.join( + os.path.dirname(test_file_name), 'certs', cert_file_name)) + assert os.path.isfile(fullname) + return fullname + + +@contextlib.contextmanager +def silence_long_exec_warning(): + + class Filter(logging.Filter): + def filter(self, record): + return not (record.msg.startswith('Executing') and + record.msg.endswith('seconds')) + + logger = logging.getLogger('asyncio') + filter = Filter() + logger.addFilter(filter) + try: + yield + finally: + logger.removeFilter(filter) + + +def find_free_port(start_from=50000): + for port in range(start_from, start_from + 500): + sock = socket.socket() + with sock: + try: + sock.bind(('', port)) + except socket.error: + continue + else: + return port + raise RuntimeError('could not find a free port') + + +class SSLTestCase: + + def _create_server_ssl_context(self, certfile, keyfile=None): + if hasattr(ssl, 'PROTOCOL_TLS'): + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS) + else: + sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.load_cert_chain(certfile, keyfile) + return sslcontext + + def _create_client_ssl_context(self, *, disable_verify=True): + sslcontext = ssl.create_default_context() + sslcontext.check_hostname = False + if disable_verify: + sslcontext.verify_mode = ssl.CERT_NONE + return sslcontext + + @contextlib.contextmanager + def _silence_eof_received_warning(self): + # TODO This warning has to be fixed in asyncio. + logger = logging.getLogger('asyncio') + filter = logging.Filter('has no effect when using ssl') + logger.addFilter(filter) + try: + yield + finally: + logger.removeFilter(filter) + + +class UVTestCase(BaseTestCase): + + implementation = 'uvloop' + + def new_loop(self): + return uvloop.new_event_loop() + + def new_policy(self): + return uvloop.EventLoopPolicy() + + +class AIOTestCase(BaseTestCase): + + implementation = 'asyncio' + + def setUp(self): + super().setUp() + + if sys.version_info < (3, 12): + watcher = asyncio.SafeChildWatcher() + watcher.attach_loop(self.loop) + asyncio.set_child_watcher(watcher) + + def tearDown(self): + if sys.version_info < (3, 12): + asyncio.set_child_watcher(None) + super().tearDown() + + def new_loop(self): + return asyncio.new_event_loop() + + def new_policy(self): + return asyncio.DefaultEventLoopPolicy() + + +def has_IPv6(): + server_sock = socket.socket(socket.AF_INET6) + with server_sock: + try: + server_sock.bind(('::1', 0)) + except OSError: + return False + else: + return True + + +has_IPv6 = has_IPv6() + + +############################################################################### +# Socket Testing Utilities +############################################################################### + + +class TestSocketWrapper: + + def __init__(self, sock): + self.__sock = sock + + def recv_all(self, n): + buf = b'' + while len(buf) < n: + data = self.recv(n - len(buf)) + if data == b'': + raise ConnectionAbortedError + buf += data + return buf + + def starttls(self, ssl_context, *, + server_side=False, + server_hostname=None, + do_handshake_on_connect=True): + + assert isinstance(ssl_context, ssl.SSLContext) + + ssl_sock = ssl_context.wrap_socket( + self.__sock, server_side=server_side, + server_hostname=server_hostname, + do_handshake_on_connect=do_handshake_on_connect) + + if server_side: + ssl_sock.do_handshake() + + self.__sock.close() + self.__sock = ssl_sock + + def __getattr__(self, name): + return getattr(self.__sock, name) + + def __repr__(self): + return '<{} {!r}>'.format(type(self).__name__, self.__sock) + + +class SocketThread(threading.Thread): + + def stop(self): + self._active = False + self.join() + + def __enter__(self): + self.start() + return self + + def __exit__(self, *exc): + self.stop() + + +class TestThreadedClient(SocketThread): + + def __init__(self, test, sock, prog, timeout): + threading.Thread.__init__(self, None, None, 'test-client') + self.daemon = True + + self._timeout = timeout + self._sock = sock + self._active = True + self._prog = prog + self._test = test + + def run(self): + try: + self._prog(TestSocketWrapper(self._sock)) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + self._test._abort_socket_test(ex) + + +class TestThreadedServer(SocketThread): + + def __init__(self, test, sock, prog, timeout, max_clients): + threading.Thread.__init__(self, None, None, 'test-server') + self.daemon = True + + self._clients = 0 + self._finished_clients = 0 + self._max_clients = max_clients + self._timeout = timeout + self._sock = sock + self._active = True + + self._prog = prog + + self._s1, self._s2 = socket.socketpair() + self._s1.setblocking(False) + + self._test = test + + def stop(self): + try: + if self._s2 and self._s2.fileno() != -1: + try: + self._s2.send(b'stop') + except OSError: + pass + finally: + super().stop() + + def run(self): + try: + with self._sock: + self._sock.setblocking(0) + self._run() + finally: + self._s1.close() + self._s2.close() + + def _run(self): + while self._active: + if self._clients >= self._max_clients: + return + + r, w, x = select.select( + [self._sock, self._s1], [], [], self._timeout) + + if self._s1 in r: + return + + if self._sock in r: + try: + conn, addr = self._sock.accept() + except BlockingIOError: + continue + except socket.timeout: + if not self._active: + return + else: + raise + else: + self._clients += 1 + conn.settimeout(self._timeout) + try: + with conn: + self._handle_client(conn) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + self._active = False + try: + raise + finally: + self._test._abort_socket_test(ex) + + def _handle_client(self, sock): + self._prog(TestSocketWrapper(sock)) + + @property + def addr(self): + return self._sock.getsockname() + + +############################################################################### +# A few helpers from asyncio/tests/testutils.py +############################################################################### + + +def run_briefly(loop): + async def once(): + pass + gen = once() + t = loop.create_task(gen) + # Don't log a warning if the task is not done after run_until_complete(). + # It occurs if the loop is stopped or if a task raises a BaseException. + t._log_destroy_pending = False + try: + loop.run_until_complete(t) + finally: + gen.close() + + +def run_until(loop, pred, timeout=30): + deadline = time.time() + timeout + while not pred(): + if timeout is not None: + timeout = deadline - time.time() + if timeout <= 0: + raise asyncio.futures.TimeoutError() + loop.run_until_complete(asyncio.tasks.sleep(0.001)) + + +@contextlib.contextmanager +def disable_logger(): + """Context manager to disable asyncio logger. + + For example, it can be used to ignore warnings in debug mode. + """ + old_level = asyncio.log.logger.level + try: + asyncio.log.logger.setLevel(logging.CRITICAL + 1) + yield + finally: + asyncio.log.logger.setLevel(old_level) diff --git a/venv/lib/python3.11/site-packages/uvloop/_version.py b/venv/lib/python3.11/site-packages/uvloop/_version.py new file mode 100644 index 0000000..0667c6a --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/_version.py @@ -0,0 +1,13 @@ +# This file MUST NOT contain anything but the __version__ assignment. +# +# When making a release, change the value of __version__ +# to an appropriate value, and open a pull request against +# the correct branch (master if making a new feature release). +# The commit message MUST contain a properly formatted release +# log, and the commit must be signed. +# +# The release automation will: build and test the packages for the +# supported platforms, publish the packages on PyPI, merge the PR +# to the target branch, create a Git tag pointing to the commit. + +__version__ = '0.19.0' diff --git a/venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd b/venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd new file mode 100644 index 0000000..e594b13 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd @@ -0,0 +1,39 @@ +cdef class Handle: + cdef: + Loop loop + object context + bint _cancelled + + str meth_name + int cb_type + void *callback + object arg1, arg2, arg3, arg4 + + object __weakref__ + + readonly _source_traceback + + cdef inline _set_loop(self, Loop loop) + cdef inline _set_context(self, object context) + + cdef inline _run(self) + cdef _cancel(self) + + cdef _format_handle(self) + + +cdef class TimerHandle: + cdef: + object callback + tuple args + bint _cancelled + UVTimer timer + Loop loop + object context + tuple _debug_info + object __weakref__ + object _when + + cdef _run(self) + cdef _cancel(self) + cdef inline _clear(self) diff --git a/venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx b/venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx new file mode 100644 index 0000000..2914b42 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx @@ -0,0 +1,434 @@ +@cython.no_gc_clear +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class Handle: + def __cinit__(self): + self._cancelled = 0 + self.cb_type = 0 + self._source_traceback = None + + cdef inline _set_loop(self, Loop loop): + self.loop = loop + if UVLOOP_DEBUG: + loop._debug_cb_handles_total += 1 + loop._debug_cb_handles_count += 1 + if loop._debug: + self._source_traceback = extract_stack() + + cdef inline _set_context(self, object context): + if context is None: + context = Context_CopyCurrent() + self.context = context + + def __dealloc__(self): + if UVLOOP_DEBUG and self.loop is not None: + self.loop._debug_cb_handles_count -= 1 + if self.loop is None: + raise RuntimeError('Handle.loop is None in Handle.__dealloc__') + + def __init__(self): + raise TypeError( + '{} is not supposed to be instantiated from Python'.format( + self.__class__.__name__)) + + cdef inline _run(self): + cdef: + int cb_type + object callback + + if self._cancelled: + return + + cb_type = self.cb_type + + # Since _run is a cdef and there's no BoundMethod, + # we guard 'self' manually (since the callback + # might cause GC of the handle.) + Py_INCREF(self) + + try: + assert self.context is not None + Context_Enter(self.context) + + if cb_type == 1: + callback = self.arg1 + if callback is None: + raise RuntimeError( + 'cannot run Handle; callback is not set') + + args = self.arg2 + + if args is None: + callback() + else: + callback(*args) + + elif cb_type == 2: + (self.callback)(self.arg1) + + elif cb_type == 3: + (self.callback)(self.arg1, self.arg2) + + elif cb_type == 4: + (self.callback)(self.arg1, self.arg2, self.arg3) + + elif cb_type == 5: + (self.callback)( + self.arg1, self.arg2, self.arg3, self.arg4) + + else: + raise RuntimeError('invalid Handle.cb_type: {}'.format( + cb_type)) + + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + if cb_type == 1: + msg = 'Exception in callback {}'.format(callback) + else: + msg = 'Exception in callback {}'.format(self.meth_name) + + context = { + 'message': msg, + 'exception': ex, + 'handle': self, + } + + if self._source_traceback is not None: + context['source_traceback'] = self._source_traceback + + self.loop.call_exception_handler(context) + + finally: + context = self.context + Py_DECREF(self) + Context_Exit(context) + + cdef _cancel(self): + self._cancelled = 1 + self.callback = NULL + self.arg1 = self.arg2 = self.arg3 = self.arg4 = None + + cdef _format_handle(self): + # Mirrors `asyncio.base_events._format_handle`. + if self.cb_type == 1 and self.arg1 is not None: + cb = self.arg1 + if isinstance(getattr(cb, '__self__', None), aio_Task): + try: + return repr(cb.__self__) + except (AttributeError, TypeError, ValueError) as ex: + # Cython generates empty __code__ objects for coroutines + # that can crash asyncio.Task.__repr__ with an + # AttributeError etc. Guard against that. + self.loop.call_exception_handler({ + 'message': 'exception in Task.__repr__', + 'task': cb.__self__, + 'exception': ex, + 'handle': self, + }) + return repr(self) + + # Public API + + def __repr__(self): + info = [self.__class__.__name__] + + if self._cancelled: + info.append('cancelled') + + if self.cb_type == 1 and self.arg1 is not None: + func = self.arg1 + # Cython can unset func.__qualname__/__name__, hence the checks. + if hasattr(func, '__qualname__') and func.__qualname__: + cb_name = func.__qualname__ + elif hasattr(func, '__name__') and func.__name__: + cb_name = func.__name__ + else: + cb_name = repr(func) + + info.append(cb_name) + elif self.meth_name is not None: + info.append(self.meth_name) + + if self._source_traceback is not None: + frame = self._source_traceback[-1] + info.append('created at {}:{}'.format(frame[0], frame[1])) + + return '<' + ' '.join(info) + '>' + + def cancel(self): + self._cancel() + + def cancelled(self): + return self._cancelled + + +@cython.no_gc_clear +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class TimerHandle: + def __cinit__(self, Loop loop, object callback, object args, + uint64_t delay, object context): + + self.loop = loop + self.callback = callback + self.args = args + self._cancelled = 0 + + if UVLOOP_DEBUG: + self.loop._debug_cb_timer_handles_total += 1 + self.loop._debug_cb_timer_handles_count += 1 + + if context is None: + context = Context_CopyCurrent() + self.context = context + + if loop._debug: + self._debug_info = ( + format_callback_name(callback), + extract_stack() + ) + else: + self._debug_info = None + + self.timer = UVTimer.new( + loop, self._run, self, delay) + + self.timer.start() + self._when = self.timer.get_when() * 1e-3 + + # Only add to loop._timers when `self.timer` is successfully created + loop._timers.add(self) + + property _source_traceback: + def __get__(self): + if self._debug_info is not None: + return self._debug_info[1] + + def __dealloc__(self): + if UVLOOP_DEBUG: + self.loop._debug_cb_timer_handles_count -= 1 + if self.timer is not None: + raise RuntimeError('active TimerHandle is deallacating') + + cdef _cancel(self): + if self._cancelled == 1: + return + self._cancelled = 1 + self._clear() + + cdef inline _clear(self): + if self.timer is None: + return + + self.callback = None + self.args = None + + try: + self.loop._timers.remove(self) + finally: + self.timer._close() + self.timer = None # let the UVTimer handle GC + + cdef _run(self): + if self._cancelled == 1: + return + if self.callback is None: + raise RuntimeError('cannot run TimerHandle; callback is not set') + + callback = self.callback + args = self.args + + # Since _run is a cdef and there's no BoundMethod, + # we guard 'self' manually. + Py_INCREF(self) + + if self.loop._debug: + started = time_monotonic() + try: + assert self.context is not None + Context_Enter(self.context) + + if args is not None: + callback(*args) + else: + callback() + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + context = { + 'message': 'Exception in callback {}'.format(callback), + 'exception': ex, + 'handle': self, + } + + if self._debug_info is not None: + context['source_traceback'] = self._debug_info[1] + + self.loop.call_exception_handler(context) + else: + if self.loop._debug: + delta = time_monotonic() - started + if delta > self.loop.slow_callback_duration: + aio_logger.warning( + 'Executing %r took %.3f seconds', + self, delta) + finally: + context = self.context + Py_DECREF(self) + Context_Exit(context) + self._clear() + + # Public API + + def __repr__(self): + info = [self.__class__.__name__] + + if self._cancelled: + info.append('cancelled') + + if self._debug_info is not None: + callback_name = self._debug_info[0] + source_traceback = self._debug_info[1] + else: + callback_name = None + source_traceback = None + + if callback_name is not None: + info.append(callback_name) + elif self.callback is not None: + info.append(format_callback_name(self.callback)) + + if source_traceback is not None: + frame = source_traceback[-1] + info.append('created at {}:{}'.format(frame[0], frame[1])) + + return '<' + ' '.join(info) + '>' + + def cancelled(self): + return self._cancelled + + def cancel(self): + self._cancel() + + def when(self): + return self._when + + +cdef format_callback_name(func): + if hasattr(func, '__qualname__'): + cb_name = getattr(func, '__qualname__') + elif hasattr(func, '__name__'): + cb_name = getattr(func, '__name__') + else: + cb_name = repr(func) + return cb_name + + +cdef new_Handle(Loop loop, object callback, object args, object context): + cdef Handle handle + handle = Handle.__new__(Handle) + handle._set_loop(loop) + handle._set_context(context) + + handle.cb_type = 1 + + handle.arg1 = callback + handle.arg2 = args + + return handle + + +cdef new_MethodHandle(Loop loop, str name, method_t callback, object context, + object bound_to): + cdef Handle handle + handle = Handle.__new__(Handle) + handle._set_loop(loop) + handle._set_context(context) + + handle.cb_type = 2 + handle.meth_name = name + + handle.callback = callback + handle.arg1 = bound_to + + return handle + + +cdef new_MethodHandle1(Loop loop, str name, method1_t callback, object context, + object bound_to, object arg): + + cdef Handle handle + handle = Handle.__new__(Handle) + handle._set_loop(loop) + handle._set_context(context) + + handle.cb_type = 3 + handle.meth_name = name + + handle.callback = callback + handle.arg1 = bound_to + handle.arg2 = arg + + return handle + + +cdef new_MethodHandle2(Loop loop, str name, method2_t callback, object context, + object bound_to, object arg1, object arg2): + + cdef Handle handle + handle = Handle.__new__(Handle) + handle._set_loop(loop) + handle._set_context(context) + + handle.cb_type = 4 + handle.meth_name = name + + handle.callback = callback + handle.arg1 = bound_to + handle.arg2 = arg1 + handle.arg3 = arg2 + + return handle + + +cdef new_MethodHandle3(Loop loop, str name, method3_t callback, object context, + object bound_to, object arg1, object arg2, object arg3): + + cdef Handle handle + handle = Handle.__new__(Handle) + handle._set_loop(loop) + handle._set_context(context) + + handle.cb_type = 5 + handle.meth_name = name + + handle.callback = callback + handle.arg1 = bound_to + handle.arg2 = arg1 + handle.arg3 = arg2 + handle.arg4 = arg3 + + return handle + + +cdef extract_stack(): + """Replacement for traceback.extract_stack() that only does the + necessary work for asyncio debug mode. + """ + try: + f = sys_getframe() + # sys._getframe() might raise ValueError if being called without a frame, e.g. + # from Cython or similar C extensions. + except ValueError: + return None + if f is None: + return + + try: + stack = tb_StackSummary.extract(tb_walk_stack(f), + limit=DEBUG_STACK_DEPTH, + lookup_lines=False) + finally: + f = None + + stack.reverse() + return stack diff --git a/venv/lib/python3.11/site-packages/uvloop/dns.pyx b/venv/lib/python3.11/site-packages/uvloop/dns.pyx new file mode 100644 index 0000000..7aad631 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/dns.pyx @@ -0,0 +1,471 @@ +cdef __port_to_int(port, proto): + if type(port) is int: + return port + + if port is None or port == '' or port == b'': + return 0 + + try: + return int(port) + except (ValueError, TypeError): + pass + + if isinstance(port, bytes): + port = port.decode() + + if isinstance(port, str) and proto is not None: + if proto == uv.IPPROTO_TCP: + return socket_getservbyname(port, 'tcp') + elif proto == uv.IPPROTO_UDP: + return socket_getservbyname(port, 'udp') + + raise OSError('service/proto not found') + + +cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr): + # Converts sockaddr structs into what Python socket + # module can understand: + # - for IPv4 a tuple of (host, port) + # - for IPv6 a tuple of (host, port, flowinfo, scope_id) + + cdef: + char buf[128] # INET6_ADDRSTRLEN is usually 46 + int err + system.sockaddr_in *addr4 + system.sockaddr_in6 *addr6 + system.sockaddr_un *addr_un + + if addr.sa_family == uv.AF_INET: + addr4 = addr + + err = uv.uv_ip4_name(addr4, buf, sizeof(buf)) + if err < 0: + raise convert_error(err) + + return ( + PyUnicode_FromString(buf), + system.ntohs(addr4.sin_port) + ) + + elif addr.sa_family == uv.AF_INET6: + addr6 = addr + + err = uv.uv_ip6_name(addr6, buf, sizeof(buf)) + if err < 0: + raise convert_error(err) + + return ( + PyUnicode_FromString(buf), + system.ntohs(addr6.sin6_port), + system.ntohl(addr6.sin6_flowinfo), + addr6.sin6_scope_id + ) + + elif addr.sa_family == uv.AF_UNIX: + addr_un = addr + return system.MakeUnixSockPyAddr(addr_un) + + raise RuntimeError("cannot convert sockaddr into Python object") + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class SockAddrHolder: + cdef: + int family + system.sockaddr_storage addr + Py_ssize_t addr_size + + +cdef LruCache sockaddrs = LruCache(maxsize=DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE) + + +cdef __convert_pyaddr_to_sockaddr(int family, object addr, + system.sockaddr* res): + cdef: + int err + int addr_len + int scope_id = 0 + int flowinfo = 0 + char *buf + Py_ssize_t buflen + SockAddrHolder ret + + ret = sockaddrs.get(addr, None) + if ret is not None and ret.family == family: + memcpy(res, &ret.addr, ret.addr_size) + return + + ret = SockAddrHolder.__new__(SockAddrHolder) + if family == uv.AF_INET: + if not isinstance(addr, tuple): + raise TypeError('AF_INET address must be tuple') + if len(addr) != 2: + raise ValueError('AF_INET address must be tuple of (host, port)') + host, port = addr + if isinstance(host, str): + try: + # idna codec is rather slow, so we try ascii first. + host = host.encode('ascii') + except UnicodeEncodeError: + host = host.encode('idna') + if not isinstance(host, (bytes, bytearray)): + raise TypeError('host must be a string or bytes object') + + port = __port_to_int(port, None) + + ret.addr_size = sizeof(system.sockaddr_in) + err = uv.uv_ip4_addr(host, port, &ret.addr) + if err < 0: + raise convert_error(err) + + elif family == uv.AF_INET6: + if not isinstance(addr, tuple): + raise TypeError('AF_INET6 address must be tuple') + + addr_len = len(addr) + if addr_len < 2 or addr_len > 4: + raise ValueError( + 'AF_INET6 must be a tuple of 2-4 parameters: ' + '(host, port, flowinfo?, scope_id?)') + + host = addr[0] + if isinstance(host, str): + try: + # idna codec is rather slow, so we try ascii first. + host = host.encode('ascii') + except UnicodeEncodeError: + host = host.encode('idna') + if not isinstance(host, (bytes, bytearray)): + raise TypeError('host must be a string or bytes object') + + port = __port_to_int(addr[1], None) + + if addr_len > 2: + flowinfo = addr[2] + if addr_len > 3: + scope_id = addr[3] + + ret.addr_size = sizeof(system.sockaddr_in6) + + err = uv.uv_ip6_addr(host, port, &ret.addr) + if err < 0: + raise convert_error(err) + + (&ret.addr).sin6_flowinfo = flowinfo + (&ret.addr).sin6_scope_id = scope_id + + elif family == uv.AF_UNIX: + if isinstance(addr, str): + addr = addr.encode(sys_getfilesystemencoding()) + elif not isinstance(addr, bytes): + raise TypeError('AF_UNIX address must be a str or a bytes object') + + PyBytes_AsStringAndSize(addr, &buf, &buflen) + if buflen > 107: + raise ValueError( + f'unix socket path {addr!r} is longer than 107 characters') + + ret.addr_size = sizeof(system.sockaddr_un) + memset(&ret.addr, 0, sizeof(system.sockaddr_un)) + (&ret.addr).sun_family = uv.AF_UNIX + memcpy((&ret.addr).sun_path, buf, buflen) + + else: + raise ValueError( + f'expected AF_INET, AF_INET6, or AF_UNIX family, got {family}') + + ret.family = family + sockaddrs[addr] = ret + memcpy(res, &ret.addr, ret.addr_size) + + +cdef __static_getaddrinfo(object host, object port, + int family, int type, + int proto, + system.sockaddr *addr): + + if proto not in {0, uv.IPPROTO_TCP, uv.IPPROTO_UDP}: + return + + if _is_sock_stream(type): + proto = uv.IPPROTO_TCP + elif _is_sock_dgram(type): + proto = uv.IPPROTO_UDP + else: + return + + try: + port = __port_to_int(port, proto) + except Exception: + return + + hp = (host, port) + if family == uv.AF_UNSPEC: + try: + __convert_pyaddr_to_sockaddr(uv.AF_INET, hp, addr) + except Exception: + pass + else: + return (uv.AF_INET, type, proto) + + try: + __convert_pyaddr_to_sockaddr(uv.AF_INET6, hp, addr) + except Exception: + pass + else: + return (uv.AF_INET6, type, proto) + + else: + try: + __convert_pyaddr_to_sockaddr(family, hp, addr) + except Exception: + pass + else: + return (family, type, proto) + + +cdef __static_getaddrinfo_pyaddr(object host, object port, + int family, int type, + int proto, int flags): + + cdef: + system.sockaddr_storage addr + object triplet + + triplet = __static_getaddrinfo( + host, port, family, type, + proto, &addr) + if triplet is None: + return + + af, type, proto = triplet + + try: + pyaddr = __convert_sockaddr_to_pyaddr(&addr) + except Exception: + return + + # When the host is an IP while type is one of TCP or UDP, different libc + # implementations of getaddrinfo() behave differently: + # 1. When AI_CANONNAME is set: + # * glibc: returns ai_canonname + # * musl: returns ai_canonname + # * macOS: returns an empty string for ai_canonname + # 2. When AI_CANONNAME is NOT set: + # * glibc: returns an empty string for ai_canonname + # * musl: returns ai_canonname + # * macOS: returns an empty string for ai_canonname + # At the same time, libuv and CPython both uses libc directly, even though + # this different behavior is violating what is in the documentation. + # + # uvloop potentially should be a 100% drop-in replacement for asyncio, + # doing whatever asyncio does, especially when the libc implementations are + # also different in the same way. However, making our implementation to be + # consistent with libc/CPython would be complex and hard to maintain + # (including caching libc behaviors when flag is/not set), therefore we + # decided to simply normalize the behavior in uvloop for this very marginal + # case following the documentation, even though uvloop would behave + # differently to asyncio on macOS and musl platforms, when again the host + # is an IP and type is one of TCP or UDP. + # All other cases are still asyncio-compatible. + if flags & socket_AI_CANONNAME: + if isinstance(host, str): + canon_name = host + else: + canon_name = host.decode('ascii') + else: + canon_name = '' + + return ( + _intenum_converter(af, socket_AddressFamily), + _intenum_converter(type, socket_SocketKind), + proto, + canon_name, + pyaddr, + ) + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class AddrInfo: + cdef: + system.addrinfo *data + + def __cinit__(self): + self.data = NULL + + def __dealloc__(self): + if self.data is not NULL: + uv.uv_freeaddrinfo(self.data) # returns void + self.data = NULL + + cdef void set_data(self, system.addrinfo *data): + self.data = data + + cdef unpack(self): + cdef: + list result = [] + system.addrinfo *ptr + + if self.data is NULL: + raise RuntimeError('AddrInfo.data is NULL') + + ptr = self.data + while ptr != NULL: + if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6): + result.append(( + _intenum_converter(ptr.ai_family, socket_AddressFamily), + _intenum_converter(ptr.ai_socktype, socket_SocketKind), + ptr.ai_protocol, + ('' if ptr.ai_canonname is NULL else + (ptr.ai_canonname).decode()), + __convert_sockaddr_to_pyaddr(ptr.ai_addr) + )) + + ptr = ptr.ai_next + + return result + + @staticmethod + cdef int isinstance(object other): + return type(other) is AddrInfo + + +cdef class AddrInfoRequest(UVRequest): + cdef: + system.addrinfo hints + object callback + uv.uv_getaddrinfo_t _req_data + + def __cinit__(self, Loop loop, + bytes host, bytes port, + int family, int type, int proto, int flags, + object callback): + + cdef: + int err + char *chost + char *cport + + if host is None: + chost = NULL + else: + chost = host + + if port is None: + cport = NULL + else: + cport = port + + if cport is NULL and chost is NULL: + self.on_done() + msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8') + ex = socket_gaierror(socket_EAI_NONAME, msg) + callback(ex) + return + + memset(&self.hints, 0, sizeof(system.addrinfo)) + self.hints.ai_flags = flags + self.hints.ai_family = family + self.hints.ai_socktype = type + self.hints.ai_protocol = proto + + self.request = &self._req_data + self.callback = callback + self.request.data = self + + err = uv.uv_getaddrinfo(loop.uvloop, + self.request, + __on_addrinfo_resolved, + chost, + cport, + &self.hints) + + if err < 0: + self.on_done() + callback(convert_error(err)) + + +cdef class NameInfoRequest(UVRequest): + cdef: + object callback + uv.uv_getnameinfo_t _req_data + + def __cinit__(self, Loop loop, callback): + self.request = &self._req_data + self.callback = callback + self.request.data = self + + cdef query(self, system.sockaddr *addr, int flags): + cdef int err + err = uv.uv_getnameinfo(self.loop.uvloop, + self.request, + __on_nameinfo_resolved, + addr, + flags) + if err < 0: + self.on_done() + self.callback(convert_error(err)) + + +cdef _intenum_converter(value, enum_klass): + try: + return enum_klass(value) + except ValueError: + return value + + +cdef void __on_addrinfo_resolved( + uv.uv_getaddrinfo_t *resolver, + int status, + system.addrinfo *res, +) noexcept with gil: + + if resolver.data is NULL: + aio_logger.error( + 'AddrInfoRequest callback called with NULL resolver.data') + return + + cdef: + AddrInfoRequest request = resolver.data + Loop loop = request.loop + object callback = request.callback + AddrInfo ai + + try: + if status < 0: + callback(convert_error(status)) + else: + ai = AddrInfo() + ai.set_data(res) + callback(ai) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + loop._handle_exception(ex) + finally: + request.on_done() + + +cdef void __on_nameinfo_resolved( + uv.uv_getnameinfo_t* req, + int status, + const char* hostname, + const char* service, +) noexcept with gil: + cdef: + NameInfoRequest request = req.data + Loop loop = request.loop + object callback = request.callback + + try: + if status < 0: + callback(convert_error(status)) + else: + callback(((hostname).decode(), + (service).decode())) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + loop._handle_exception(ex) + finally: + request.on_done() diff --git a/venv/lib/python3.11/site-packages/uvloop/errors.pyx b/venv/lib/python3.11/site-packages/uvloop/errors.pyx new file mode 100644 index 0000000..d810d65 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/errors.pyx @@ -0,0 +1,113 @@ +cdef str __strerr(int errno): + return strerror(errno).decode() + + +cdef __convert_python_error(int uverr): + # XXX Won't work for Windows: + # From libuv docs: + # Implementation detail: on Unix error codes are the + # negated errno (or -errno), while on Windows they + # are defined by libuv to arbitrary negative numbers. + cdef int oserr = -uverr + + exc = OSError + + if uverr in (uv.UV_EACCES, uv.UV_EPERM): + exc = PermissionError + + elif uverr in (uv.UV_EAGAIN, uv.UV_EALREADY): + exc = BlockingIOError + + elif uverr in (uv.UV_EPIPE, uv.UV_ESHUTDOWN): + exc = BrokenPipeError + + elif uverr == uv.UV_ECONNABORTED: + exc = ConnectionAbortedError + + elif uverr == uv.UV_ECONNREFUSED: + exc = ConnectionRefusedError + + elif uverr == uv.UV_ECONNRESET: + exc = ConnectionResetError + + elif uverr == uv.UV_EEXIST: + exc = FileExistsError + + elif uverr == uv.UV_ENOENT: + exc = FileNotFoundError + + elif uverr == uv.UV_EINTR: + exc = InterruptedError + + elif uverr == uv.UV_EISDIR: + exc = IsADirectoryError + + elif uverr == uv.UV_ESRCH: + exc = ProcessLookupError + + elif uverr == uv.UV_ETIMEDOUT: + exc = TimeoutError + + return exc(oserr, __strerr(oserr)) + + +cdef int __convert_socket_error(int uverr): + cdef int sock_err = 0 + + if uverr == uv.UV_EAI_ADDRFAMILY: + sock_err = socket_EAI_ADDRFAMILY + + elif uverr == uv.UV_EAI_AGAIN: + sock_err = socket_EAI_AGAIN + + elif uverr == uv.UV_EAI_BADFLAGS: + sock_err = socket_EAI_BADFLAGS + + elif uverr == uv.UV_EAI_BADHINTS: + sock_err = socket_EAI_BADHINTS + + elif uverr == uv.UV_EAI_CANCELED: + sock_err = socket_EAI_CANCELED + + elif uverr == uv.UV_EAI_FAIL: + sock_err = socket_EAI_FAIL + + elif uverr == uv.UV_EAI_FAMILY: + sock_err = socket_EAI_FAMILY + + elif uverr == uv.UV_EAI_MEMORY: + sock_err = socket_EAI_MEMORY + + elif uverr == uv.UV_EAI_NODATA: + sock_err = socket_EAI_NODATA + + elif uverr == uv.UV_EAI_NONAME: + sock_err = socket_EAI_NONAME + + elif uverr == uv.UV_EAI_OVERFLOW: + sock_err = socket_EAI_OVERFLOW + + elif uverr == uv.UV_EAI_PROTOCOL: + sock_err = socket_EAI_PROTOCOL + + elif uverr == uv.UV_EAI_SERVICE: + sock_err = socket_EAI_SERVICE + + elif uverr == uv.UV_EAI_SOCKTYPE: + sock_err = socket_EAI_SOCKTYPE + + return sock_err + + +cdef convert_error(int uverr): + cdef int sock_err + + if uverr == uv.UV_ECANCELED: + return aio_CancelledError() + + sock_err = __convert_socket_error(uverr) + if sock_err: + msg = system.gai_strerror(sock_err).decode('utf-8') + return socket_gaierror(sock_err, msg) + + return __convert_python_error(uverr) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd new file mode 100644 index 0000000..5f0d820 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd @@ -0,0 +1,11 @@ +cdef class UVAsync(UVHandle): + cdef: + method_t callback + object ctx + + cdef _init(self, Loop loop, method_t callback, object ctx) + + cdef send(self) + + @staticmethod + cdef UVAsync new(Loop loop, method_t callback, object ctx) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx new file mode 100644 index 0000000..5c740cf --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx @@ -0,0 +1,56 @@ +@cython.no_gc_clear +cdef class UVAsync(UVHandle): + cdef _init(self, Loop loop, method_t callback, object ctx): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_async_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_async_init(self._loop.uvloop, + self._handle, + __uvasync_callback) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.callback = callback + self.ctx = ctx + + cdef send(self): + cdef int err + + self._ensure_alive() + + err = uv.uv_async_send(self._handle) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + @staticmethod + cdef UVAsync new(Loop loop, method_t callback, object ctx): + cdef UVAsync handle + handle = UVAsync.__new__(UVAsync) + handle._init(loop, callback, ctx) + return handle + + +cdef void __uvasync_callback( + uv.uv_async_t* handle, +) noexcept with gil: + if __ensure_handle_data(handle, "UVAsync callback") == 0: + return + + cdef: + UVAsync async_ = handle.data + method_t cb = async_.callback + try: + cb(async_.ctx) + except BaseException as ex: + async_._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd new file mode 100644 index 0000000..ba356a7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd @@ -0,0 +1,54 @@ +cdef class UVBaseTransport(UVSocketHandle): + + cdef: + readonly bint _closing + + bint _protocol_connected + bint _protocol_paused + object _protocol_data_received + size_t _high_water + size_t _low_water + + object _protocol + Server _server + object _waiter + + dict _extra_info + + uint32_t _conn_lost + + object __weakref__ + + # All "inline" methods are final + + cdef inline _maybe_pause_protocol(self) + cdef inline _maybe_resume_protocol(self) + + cdef inline _schedule_call_connection_made(self) + cdef inline _schedule_call_connection_lost(self, exc) + + cdef _wakeup_waiter(self) + cdef _call_connection_made(self) + cdef _call_connection_lost(self, exc) + + # Overloads of UVHandle methods: + cdef _fatal_error(self, exc, throw, reason=?) + cdef _close(self) + + cdef inline _set_server(self, Server server) + cdef inline _set_waiter(self, object waiter) + + cdef _set_protocol(self, object protocol) + cdef _clear_protocol(self) + + cdef inline _init_protocol(self) + cdef inline _add_extra_info(self, str name, object obj) + + # === overloads === + + cdef _new_socket(self) + cdef size_t _get_write_buffer_size(self) + + cdef bint _is_reading(self) + cdef _start_reading(self) + cdef _stop_reading(self) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx new file mode 100644 index 0000000..28b3079 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx @@ -0,0 +1,293 @@ +cdef class UVBaseTransport(UVSocketHandle): + + def __cinit__(self): + # Flow control + self._high_water = FLOW_CONTROL_HIGH_WATER * 1024 + self._low_water = FLOW_CONTROL_HIGH_WATER // 4 + + self._protocol = None + self._protocol_connected = 0 + self._protocol_paused = 0 + self._protocol_data_received = None + + self._server = None + self._waiter = None + self._extra_info = None + + self._conn_lost = 0 + + self._closing = 0 + + cdef size_t _get_write_buffer_size(self): + return 0 + + cdef inline _schedule_call_connection_made(self): + self._loop._call_soon_handle( + new_MethodHandle(self._loop, + "UVTransport._call_connection_made", + self._call_connection_made, + self.context, + self)) + + cdef inline _schedule_call_connection_lost(self, exc): + self._loop._call_soon_handle( + new_MethodHandle1(self._loop, + "UVTransport._call_connection_lost", + self._call_connection_lost, + self.context, + self, exc)) + + cdef _fatal_error(self, exc, throw, reason=None): + # Overload UVHandle._fatal_error + + self._force_close(exc) + + if not isinstance(exc, OSError): + + if throw or self._loop is None: + raise exc + + msg = f'Fatal error on transport {self.__class__.__name__}' + if reason is not None: + msg = f'{msg} ({reason})' + + self._loop.call_exception_handler({ + 'message': msg, + 'exception': exc, + 'transport': self, + 'protocol': self._protocol, + }) + + cdef inline _maybe_pause_protocol(self): + cdef: + size_t size = self._get_write_buffer_size() + + if size <= self._high_water: + return + + if not self._protocol_paused: + self._protocol_paused = 1 + try: + # _maybe_pause_protocol() is always triggered from user-calls, + # so we must copy the context to avoid entering context twice + run_in_context( + self.context.copy(), self._protocol.pause_writing, + ) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + self._loop.call_exception_handler({ + 'message': 'protocol.pause_writing() failed', + 'exception': exc, + 'transport': self, + 'protocol': self._protocol, + }) + + cdef inline _maybe_resume_protocol(self): + cdef: + size_t size = self._get_write_buffer_size() + + if self._protocol_paused and size <= self._low_water: + self._protocol_paused = 0 + try: + # We're copying the context to avoid entering context twice, + # even though it's not always necessary to copy - it's easier + # to copy here than passing down a copied context. + run_in_context( + self.context.copy(), self._protocol.resume_writing, + ) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + self._loop.call_exception_handler({ + 'message': 'protocol.resume_writing() failed', + 'exception': exc, + 'transport': self, + 'protocol': self._protocol, + }) + + cdef _wakeup_waiter(self): + if self._waiter is not None: + if not self._waiter.cancelled(): + if not self._is_alive(): + self._waiter.set_exception( + RuntimeError( + 'closed Transport handle and unset waiter')) + else: + self._waiter.set_result(True) + self._waiter = None + + cdef _call_connection_made(self): + if self._protocol is None: + raise RuntimeError( + 'protocol is not set, cannot call connection_made()') + + # We use `_is_alive()` and not `_closing`, because we call + # `transport._close()` in `loop.create_connection()` if an + # exception happens during `await waiter`. + if not self._is_alive(): + # A connection waiter can be cancelled between + # 'await loop.create_connection()' and + # `_schedule_call_connection_made` and + # the actual `_call_connection_made`. + self._wakeup_waiter() + return + + # Set _protocol_connected to 1 before calling "connection_made": + # if transport is aborted or closed, "connection_lost" will + # still be scheduled. + self._protocol_connected = 1 + + try: + self._protocol.connection_made(self) + except BaseException: + self._wakeup_waiter() + raise + + if not self._is_alive(): + # This might happen when "transport.abort()" is called + # from "Protocol.connection_made". + self._wakeup_waiter() + return + + self._start_reading() + self._wakeup_waiter() + + cdef _call_connection_lost(self, exc): + if self._waiter is not None: + if not self._waiter.done(): + self._waiter.set_exception(exc) + self._waiter = None + + if self._closed: + # The handle is closed -- likely, _call_connection_lost + # was already called before. + return + + try: + if self._protocol_connected: + self._protocol.connection_lost(exc) + finally: + self._clear_protocol() + + self._close() + + server = self._server + if server is not None: + (server)._detach() + self._server = None + + cdef inline _set_server(self, Server server): + self._server = server + (server)._attach() + + cdef inline _set_waiter(self, object waiter): + if waiter is not None and not isfuture(waiter): + raise TypeError( + f'invalid waiter object {waiter!r}, expected asyncio.Future') + + self._waiter = waiter + + cdef _set_protocol(self, object protocol): + self._protocol = protocol + # Store a reference to the bound method directly + try: + self._protocol_data_received = protocol.data_received + except AttributeError: + pass + + cdef _clear_protocol(self): + self._protocol = None + self._protocol_data_received = None + + cdef inline _init_protocol(self): + self._loop._track_transport(self) + if self._protocol is None: + raise RuntimeError('invalid _init_protocol call') + self._schedule_call_connection_made() + + cdef inline _add_extra_info(self, str name, object obj): + if self._extra_info is None: + self._extra_info = {} + self._extra_info[name] = obj + + cdef bint _is_reading(self): + raise NotImplementedError + + cdef _start_reading(self): + raise NotImplementedError + + cdef _stop_reading(self): + raise NotImplementedError + + # === Public API === + + property _paused: + # Used by SSLProto. Might be removed in the future. + def __get__(self): + return bool(not self._is_reading()) + + def get_protocol(self): + return self._protocol + + def set_protocol(self, protocol): + self._set_protocol(protocol) + if self._is_reading(): + self._stop_reading() + self._start_reading() + + def _force_close(self, exc): + # Used by SSLProto. Might be removed in the future. + if self._conn_lost or self._closed: + return + if not self._closing: + self._closing = 1 + self._stop_reading() + self._conn_lost += 1 + self._schedule_call_connection_lost(exc) + + def abort(self): + self._force_close(None) + + def close(self): + if self._closing or self._closed: + return + + self._closing = 1 + self._stop_reading() + + if not self._get_write_buffer_size(): + # The write buffer is empty + self._conn_lost += 1 + self._schedule_call_connection_lost(None) + + def is_closing(self): + return self._closing + + def get_write_buffer_size(self): + return self._get_write_buffer_size() + + def set_write_buffer_limits(self, high=None, low=None): + self._ensure_alive() + + self._high_water, self._low_water = add_flowcontrol_defaults( + high, low, FLOW_CONTROL_HIGH_WATER) + + self._maybe_pause_protocol() + + def get_write_buffer_limits(self): + return (self._low_water, self._high_water) + + def get_extra_info(self, name, default=None): + if self._extra_info is not None and name in self._extra_info: + return self._extra_info[name] + if name == 'socket': + return self._get_socket() + if name == 'sockname': + return self._get_socket().getsockname() + if name == 'peername': + try: + return self._get_socket().getpeername() + except socket_error: + return default + return default diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/check.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/check.pxd new file mode 100644 index 0000000..86cfd8f --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/check.pxd @@ -0,0 +1,14 @@ +cdef class UVCheck(UVHandle): + cdef: + Handle h + bint running + + # All "inline" methods are final + + cdef _init(self, Loop loop, Handle h) + + cdef inline stop(self) + cdef inline start(self) + + @staticmethod + cdef UVCheck new(Loop loop, Handle h) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/check.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/check.pyx new file mode 100644 index 0000000..1a61c4e --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/check.pyx @@ -0,0 +1,72 @@ +@cython.no_gc_clear +cdef class UVCheck(UVHandle): + cdef _init(self, Loop loop, Handle h): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_check_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_check_init(self._loop.uvloop, self._handle) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.h = h + self.running = 0 + + cdef inline stop(self): + cdef int err + + if not self._is_alive(): + self.running = 0 + return + + if self.running == 1: + err = uv.uv_check_stop(self._handle) + self.running = 0 + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef inline start(self): + cdef int err + + self._ensure_alive() + + if self.running == 0: + err = uv.uv_check_start(self._handle, + cb_check_callback) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + self.running = 1 + + @staticmethod + cdef UVCheck new(Loop loop, Handle h): + cdef UVCheck handle + handle = UVCheck.__new__(UVCheck) + handle._init(loop, h) + return handle + + +cdef void cb_check_callback( + uv.uv_check_t* handle, +) noexcept with gil: + if __ensure_handle_data(handle, "UVCheck callback") == 0: + return + + cdef: + UVCheck check = handle.data + Handle h = check.h + try: + h._run() + except BaseException as ex: + check._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd new file mode 100644 index 0000000..3a32428 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd @@ -0,0 +1,12 @@ +cdef class UVFSEvent(UVHandle): + cdef: + object callback + bint running + + cdef _init(self, Loop loop, object callback, object context) + cdef _close(self) + cdef start(self, char* path, int flags) + cdef stop(self) + + @staticmethod + cdef UVFSEvent new(Loop loop, object callback, object context) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx new file mode 100644 index 0000000..6ed6433 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx @@ -0,0 +1,116 @@ +import enum + + +class FileSystemEvent(enum.IntEnum): + RENAME = uv.UV_RENAME + CHANGE = uv.UV_CHANGE + RENAME_CHANGE = RENAME | CHANGE + + +@cython.no_gc_clear +cdef class UVFSEvent(UVHandle): + cdef _init(self, Loop loop, object callback, object context): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc( + sizeof(uv.uv_fs_event_t) + ) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_fs_event_init( + self._loop.uvloop, self._handle + ) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.running = 0 + self.callback = callback + if context is None: + context = Context_CopyCurrent() + self.context = context + + cdef start(self, char* path, int flags): + cdef int err + + self._ensure_alive() + + if self.running == 0: + err = uv.uv_fs_event_start( + self._handle, + __uvfsevent_callback, + path, + flags, + ) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + self.running = 1 + + cdef stop(self): + cdef int err + + if not self._is_alive(): + self.running = 0 + return + + if self.running == 1: + err = uv.uv_fs_event_stop(self._handle) + self.running = 0 + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef _close(self): + try: + self.stop() + finally: + UVHandle._close(self) + + def cancel(self): + self._close() + + def cancelled(self): + return self.running == 0 + + @staticmethod + cdef UVFSEvent new(Loop loop, object callback, object context): + cdef UVFSEvent handle + handle = UVFSEvent.__new__(UVFSEvent) + handle._init(loop, callback, context) + return handle + + +cdef void __uvfsevent_callback( + uv.uv_fs_event_t* handle, + const char *filename, + int events, + int status, +) noexcept with gil: + if __ensure_handle_data( + handle, "UVFSEvent callback" + ) == 0: + return + + cdef: + UVFSEvent fs_event = handle.data + Handle h + + try: + h = new_Handle( + fs_event._loop, + fs_event.callback, + (filename, FileSystemEvent(events)), + fs_event.context, + ) + h._run() + except BaseException as ex: + fs_event._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd new file mode 100644 index 0000000..5af1c14 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd @@ -0,0 +1,48 @@ +cdef class UVHandle: + cdef: + uv.uv_handle_t *_handle + Loop _loop + readonly _source_traceback + bint _closed + bint _inited + object context + + # Added to enable current UDPTransport implementation, + # which doesn't use libuv handles. + bint _has_handle + + # All "inline" methods are final + + cdef inline _start_init(self, Loop loop) + cdef inline _abort_init(self) + cdef inline _finish_init(self) + + cdef inline bint _is_alive(self) + cdef inline _ensure_alive(self) + + cdef _error(self, exc, throw) + cdef _fatal_error(self, exc, throw, reason=?) + + cdef _warn_unclosed(self) + + cdef _free(self) + cdef _close(self) + + +cdef class UVSocketHandle(UVHandle): + cdef: + # Points to a Python file-object that should be closed + # when the transport is closing. Used by pipes. This + # should probably be refactored somehow. + object _fileobj + object __cached_socket + + # All "inline" methods are final + + cdef _fileno(self) + + cdef _new_socket(self) + cdef inline _get_socket(self) + cdef inline _attach_fileobj(self, object file) + + cdef _open(self, int sockfd) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx new file mode 100644 index 0000000..6efe375 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx @@ -0,0 +1,395 @@ +cdef class UVHandle: + """A base class for all libuv handles. + + Automatically manages memory deallocation and closing. + + Important: + + 1. call "_ensure_alive()" before calling any libuv functions on + your handles. + + 2. call "__ensure_handle_data" in *all* libuv handle callbacks. + """ + + def __cinit__(self): + self._closed = 0 + self._inited = 0 + self._has_handle = 1 + self._handle = NULL + self._loop = None + self._source_traceback = None + + def __init__(self): + raise TypeError( + '{} is not supposed to be instantiated from Python'.format( + self.__class__.__name__)) + + def __dealloc__(self): + if UVLOOP_DEBUG: + if self._loop is not None: + if self._inited: + self._loop._debug_handles_current.subtract([ + self.__class__.__name__]) + else: + # No "@cython.no_gc_clear" decorator on this UVHandle + raise RuntimeError( + '{} without @no_gc_clear; loop was set to None by GC' + .format(self.__class__.__name__)) + + if self._handle is NULL: + return + + # -> When we're at this point, something is wrong <- + + if self._handle.loop is NULL: + # The handle wasn't initialized with "uv_{handle}_init" + self._closed = 1 + self._free() + raise RuntimeError( + '{} is open in __dealloc__ with loop set to NULL' + .format(self.__class__.__name__)) + + if self._closed: + # So _handle is not NULL and self._closed == 1? + raise RuntimeError( + '{}.__dealloc__: _handle is NULL, _closed == 1'.format( + self.__class__.__name__)) + + # The handle is dealloced while open. Let's try to close it. + # Situations when this is possible include unhandled exceptions, + # errors during Handle.__cinit__/__init__ etc. + if self._inited: + self._handle.data = NULL + uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors + self._handle = NULL + self._warn_unclosed() + else: + # The handle was allocated, but not initialized + self._closed = 1 + self._free() + + cdef _free(self): + if self._handle == NULL: + return + + if UVLOOP_DEBUG and self._inited: + self._loop._debug_uv_handles_freed += 1 + + PyMem_RawFree(self._handle) + self._handle = NULL + + cdef _warn_unclosed(self): + if self._source_traceback is not None: + try: + tb = ''.join(tb_format_list(self._source_traceback)) + tb = 'object created at (most recent call last):\n{}'.format( + tb.rstrip()) + except Exception as ex: + msg = ( + 'unclosed resource {!r}; could not serialize ' + 'debug traceback: {}: {}' + ).format(self, type(ex).__name__, ex) + else: + msg = 'unclosed resource {!r}; {}'.format(self, tb) + else: + msg = 'unclosed resource {!r}'.format(self) + warnings_warn(msg, ResourceWarning) + + cdef inline _abort_init(self): + if self._handle is not NULL: + self._free() + + try: + if UVLOOP_DEBUG: + name = self.__class__.__name__ + if self._inited: + raise RuntimeError( + '_abort_init: {}._inited is set'.format(name)) + if self._closed: + raise RuntimeError( + '_abort_init: {}._closed is set'.format(name)) + finally: + self._closed = 1 + + cdef inline _finish_init(self): + self._inited = 1 + if self._has_handle == 1: + self._handle.data = self + if self._loop._debug: + self._source_traceback = extract_stack() + if UVLOOP_DEBUG: + cls_name = self.__class__.__name__ + self._loop._debug_uv_handles_total += 1 + self._loop._debug_handles_total.update([cls_name]) + self._loop._debug_handles_current.update([cls_name]) + + cdef inline _start_init(self, Loop loop): + if UVLOOP_DEBUG: + if self._loop is not None: + raise RuntimeError( + '{}._start_init can only be called once'.format( + self.__class__.__name__)) + + self._loop = loop + + cdef inline bint _is_alive(self): + cdef bint res + res = self._closed != 1 and self._inited == 1 + if UVLOOP_DEBUG: + if res and self._has_handle == 1: + name = self.__class__.__name__ + if self._handle is NULL: + raise RuntimeError( + '{} is alive, but _handle is NULL'.format(name)) + if self._loop is None: + raise RuntimeError( + '{} is alive, but _loop is None'.format(name)) + if self._handle.loop is not self._loop.uvloop: + raise RuntimeError( + '{} is alive, but _handle.loop is not ' + 'initialized'.format(name)) + if self._handle.data is not self: + raise RuntimeError( + '{} is alive, but _handle.data is not ' + 'initialized'.format(name)) + return res + + cdef inline _ensure_alive(self): + if not self._is_alive(): + raise RuntimeError( + 'unable to perform operation on {!r}; ' + 'the handler is closed'.format(self)) + + cdef _fatal_error(self, exc, throw, reason=None): + # Fatal error means an error that was returned by the + # underlying libuv handle function. We usually can't + # recover from that, hence we just close the handle. + self._close() + + if throw or self._loop is None: + raise exc + else: + self._loop._handle_exception(exc) + + cdef _error(self, exc, throw): + # A non-fatal error is usually an error that was caught + # by the handler, but was originated in the client code + # (not in libuv). In this case we either want to simply + # raise or log it. + if throw or self._loop is None: + raise exc + else: + self._loop._handle_exception(exc) + + cdef _close(self): + if self._closed == 1: + return + + self._closed = 1 + + if self._handle is NULL: + return + + if UVLOOP_DEBUG: + if self._handle.data is NULL: + raise RuntimeError( + '{}._close: _handle.data is NULL'.format( + self.__class__.__name__)) + + if self._handle.data is not self: + raise RuntimeError( + '{}._close: _handle.data is not UVHandle/self'.format( + self.__class__.__name__)) + + if uv.uv_is_closing(self._handle): + raise RuntimeError( + '{}._close: uv_is_closing() is true'.format( + self.__class__.__name__)) + + # We want the handle wrapper (UVHandle) to stay alive until + # the closing callback fires. + Py_INCREF(self) + uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors + + def __repr__(self): + return '<{} closed={} {:#x}>'.format( + self.__class__.__name__, + self._closed, + id(self)) + + +cdef class UVSocketHandle(UVHandle): + + def __cinit__(self): + self._fileobj = None + self.__cached_socket = None + + cdef _fileno(self): + cdef: + int fd + int err + + self._ensure_alive() + err = uv.uv_fileno(self._handle, &fd) + if err < 0: + raise convert_error(err) + + return fd + + cdef _new_socket(self): + raise NotImplementedError + + cdef inline _get_socket(self): + if self.__cached_socket is not None: + return self.__cached_socket + + if not self._is_alive(): + return None + + self.__cached_socket = self._new_socket() + if UVLOOP_DEBUG: + # We don't "dup" for the "__cached_socket". + assert self.__cached_socket.fileno() == self._fileno() + return self.__cached_socket + + cdef inline _attach_fileobj(self, object file): + # When we create a TCP/PIPE/etc connection/server based on + # a Python file object, we need to close the file object when + # the uv handle is closed. + socket_inc_io_ref(file) + self._fileobj = file + + cdef _close(self): + if self.__cached_socket is not None: + (self.__cached_socket)._fd = -1 + + UVHandle._close(self) + + try: + # This code will only run for transports created from + # Python sockets, i.e. with `loop.create_server(sock=sock)` etc. + if self._fileobj is not None: + if isinstance(self._fileobj, socket_socket): + # Detaching the socket object is the ideal solution: + # * libuv will actually close the FD; + # * detach() call will reset FD for the Python socket + # object, which means that it won't be closed 2nd time + # when the socket object is GCed. + # + # No need to call `socket_dec_io_ref()`, as + # `socket.detach()` ignores `socket._io_refs`. + self._fileobj.detach() + else: + try: + # `socket.close()` will raise an EBADF because libuv + # has already closed the underlying FD. + self._fileobj.close() + except OSError as ex: + if ex.errno != errno_EBADF: + raise + except Exception as ex: + self._loop.call_exception_handler({ + 'exception': ex, + 'transport': self, + 'message': f'could not close attached file object ' + f'{self._fileobj!r}', + }) + finally: + self._fileobj = None + + cdef _open(self, int sockfd): + raise NotImplementedError + + +cdef inline bint __ensure_handle_data(uv.uv_handle_t* handle, + const char* handle_ctx): + + cdef Loop loop + + if UVLOOP_DEBUG: + if handle.loop is NULL: + raise RuntimeError( + 'handle.loop is NULL in __ensure_handle_data') + + if handle.loop.data is NULL: + raise RuntimeError( + 'handle.loop.data is NULL in __ensure_handle_data') + + if handle.data is NULL: + loop = handle.loop.data + loop.call_exception_handler({ + 'message': '{} called with handle.data == NULL'.format( + handle_ctx.decode('latin-1')) + }) + return 0 + + if handle.data is NULL: + # The underlying UVHandle object was GCed with an open uv_handle_t. + loop = handle.loop.data + loop.call_exception_handler({ + 'message': '{} called after destroying the UVHandle'.format( + handle_ctx.decode('latin-1')) + }) + return 0 + + return 1 + + +cdef void __uv_close_handle_cb(uv.uv_handle_t* handle) noexcept with gil: + cdef UVHandle h + + if handle.data is NULL: + # The original UVHandle is long dead. Just free the mem of + # the uv_handle_t* handler. + + if UVLOOP_DEBUG: + if handle.loop == NULL or handle.loop.data == NULL: + raise RuntimeError( + '__uv_close_handle_cb: handle.loop is invalid') + (handle.loop.data)._debug_uv_handles_freed += 1 + + PyMem_RawFree(handle) + else: + h = handle.data + try: + if UVLOOP_DEBUG: + if not h._has_handle: + raise RuntimeError( + 'has_handle=0 in __uv_close_handle_cb') + h._loop._debug_handles_closed.update([ + h.__class__.__name__]) + h._free() + finally: + Py_DECREF(h) # Was INCREFed in UVHandle._close + + +cdef void __close_all_handles(Loop loop): + uv.uv_walk(loop.uvloop, + __uv_walk_close_all_handles_cb, + loop) # void + + +cdef void __uv_walk_close_all_handles_cb( + uv.uv_handle_t* handle, + void* arg, +) noexcept with gil: + + cdef: + Loop loop = arg + UVHandle h + + if uv.uv_is_closing(handle): + # The handle is closed or is closing. + return + + if handle.data is NULL: + # This shouldn't happen. Ever. + loop.call_exception_handler({ + 'message': 'handle.data is NULL in __close_all_handles_cb' + }) + return + + h = handle.data + if not h._closed: + h._warn_unclosed() + h._close() diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd new file mode 100644 index 0000000..cf7b19f --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd @@ -0,0 +1,14 @@ +cdef class UVIdle(UVHandle): + cdef: + Handle h + bint running + + # All "inline" methods are final + + cdef _init(self, Loop loop, Handle h) + + cdef inline stop(self) + cdef inline start(self) + + @staticmethod + cdef UVIdle new(Loop loop, Handle h) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx new file mode 100644 index 0000000..91c641f --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx @@ -0,0 +1,72 @@ +@cython.no_gc_clear +cdef class UVIdle(UVHandle): + cdef _init(self, Loop loop, Handle h): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_idle_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_idle_init(self._loop.uvloop, self._handle) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.h = h + self.running = 0 + + cdef inline stop(self): + cdef int err + + if not self._is_alive(): + self.running = 0 + return + + if self.running == 1: + err = uv.uv_idle_stop(self._handle) + self.running = 0 + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef inline start(self): + cdef int err + + self._ensure_alive() + + if self.running == 0: + err = uv.uv_idle_start(self._handle, + cb_idle_callback) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + self.running = 1 + + @staticmethod + cdef UVIdle new(Loop loop, Handle h): + cdef UVIdle handle + handle = UVIdle.__new__(UVIdle) + handle._init(loop, h) + return handle + + +cdef void cb_idle_callback( + uv.uv_idle_t* handle, +) noexcept with gil: + if __ensure_handle_data(handle, "UVIdle callback") == 0: + return + + cdef: + UVIdle idle = handle.data + Handle h = idle.h + try: + h._run() + except BaseException as ex: + idle._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd new file mode 100644 index 0000000..56fc265 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd @@ -0,0 +1,33 @@ +cdef class UnixServer(UVStreamServer): + + cdef bind(self, str path) + + @staticmethod + cdef UnixServer new(Loop loop, object protocol_factory, Server server, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout) + + +cdef class UnixTransport(UVStream): + + @staticmethod + cdef UnixTransport new(Loop loop, object protocol, Server server, + object waiter, object context) + + cdef connect(self, char* addr) + + +cdef class ReadUnixTransport(UVStream): + + @staticmethod + cdef ReadUnixTransport new(Loop loop, object protocol, Server server, + object waiter) + + +cdef class WriteUnixTransport(UVStream): + + @staticmethod + cdef WriteUnixTransport new(Loop loop, object protocol, Server server, + object waiter) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx new file mode 100644 index 0000000..195576c --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx @@ -0,0 +1,226 @@ +cdef __pipe_init_uv_handle(UVStream handle, Loop loop): + cdef int err + + handle._handle = PyMem_RawMalloc(sizeof(uv.uv_pipe_t)) + if handle._handle is NULL: + handle._abort_init() + raise MemoryError() + + # Initialize pipe handle with ipc=0. + # ipc=1 means that libuv will use recvmsg/sendmsg + # instead of recv/send. + err = uv.uv_pipe_init(handle._loop.uvloop, + handle._handle, + 0) + # UV_HANDLE_READABLE allows calling uv_read_start() on this pipe + # even if it is O_WRONLY, see also #317, libuv/libuv#2058 + handle._handle.flags |= uv.UV_INTERNAL_HANDLE_READABLE + if err < 0: + handle._abort_init() + raise convert_error(err) + + handle._finish_init() + + +cdef __pipe_open(UVStream handle, int fd): + cdef int err + err = uv.uv_pipe_open(handle._handle, + fd) + if err < 0: + exc = convert_error(err) + raise exc + + +cdef __pipe_get_socket(UVSocketHandle handle): + fileno = handle._fileno() + return PseudoSocket(uv.AF_UNIX, uv.SOCK_STREAM, 0, fileno) + + +@cython.no_gc_clear +cdef class UnixServer(UVStreamServer): + + @staticmethod + cdef UnixServer new(Loop loop, object protocol_factory, Server server, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout): + + cdef UnixServer handle + handle = UnixServer.__new__(UnixServer) + handle._init(loop, protocol_factory, server, backlog, + ssl, ssl_handshake_timeout, ssl_shutdown_timeout) + __pipe_init_uv_handle(handle, loop) + return handle + + cdef _new_socket(self): + return __pipe_get_socket(self) + + cdef _open(self, int sockfd): + self._ensure_alive() + __pipe_open(self, sockfd) + self._mark_as_open() + + cdef bind(self, str path): + cdef int err + self._ensure_alive() + err = uv.uv_pipe_bind(self._handle, + path.encode()) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + self._mark_as_open() + + cdef UVStream _make_new_transport(self, object protocol, object waiter, + object context): + cdef UnixTransport tr + tr = UnixTransport.new(self._loop, protocol, self._server, waiter, + context) + return tr + + +@cython.no_gc_clear +cdef class UnixTransport(UVStream): + + @staticmethod + cdef UnixTransport new(Loop loop, object protocol, Server server, + object waiter, object context): + + cdef UnixTransport handle + handle = UnixTransport.__new__(UnixTransport) + handle._init(loop, protocol, server, waiter, context) + __pipe_init_uv_handle(handle, loop) + return handle + + cdef _new_socket(self): + return __pipe_get_socket(self) + + cdef _open(self, int sockfd): + __pipe_open(self, sockfd) + + cdef connect(self, char* addr): + cdef _PipeConnectRequest req + req = _PipeConnectRequest(self._loop, self) + req.connect(addr) + + +@cython.no_gc_clear +cdef class ReadUnixTransport(UVStream): + + @staticmethod + cdef ReadUnixTransport new(Loop loop, object protocol, Server server, + object waiter): + cdef ReadUnixTransport handle + handle = ReadUnixTransport.__new__(ReadUnixTransport) + # This is only used in connect_read_pipe() and subprocess_shell/exec() + # directly, we could simply copy the current context. + handle._init(loop, protocol, server, waiter, Context_CopyCurrent()) + __pipe_init_uv_handle(handle, loop) + return handle + + cdef _new_socket(self): + return __pipe_get_socket(self) + + cdef _open(self, int sockfd): + __pipe_open(self, sockfd) + + def get_write_buffer_limits(self): + raise NotImplementedError + + def set_write_buffer_limits(self, high=None, low=None): + raise NotImplementedError + + def get_write_buffer_size(self): + raise NotImplementedError + + def write(self, data): + raise NotImplementedError + + def writelines(self, list_of_data): + raise NotImplementedError + + def write_eof(self): + raise NotImplementedError + + def can_write_eof(self): + raise NotImplementedError + + def abort(self): + raise NotImplementedError + + +@cython.no_gc_clear +cdef class WriteUnixTransport(UVStream): + + @staticmethod + cdef WriteUnixTransport new(Loop loop, object protocol, Server server, + object waiter): + cdef WriteUnixTransport handle + handle = WriteUnixTransport.__new__(WriteUnixTransport) + + # We listen for read events on write-end of the pipe. When + # the read-end is close, the uv_stream_t.read callback will + # receive an error -- we want to silence that error, and just + # close the transport. + handle._close_on_read_error() + + # This is only used in connect_write_pipe() and subprocess_shell/exec() + # directly, we could simply copy the current context. + handle._init(loop, protocol, server, waiter, Context_CopyCurrent()) + __pipe_init_uv_handle(handle, loop) + return handle + + cdef _new_socket(self): + return __pipe_get_socket(self) + + cdef _open(self, int sockfd): + __pipe_open(self, sockfd) + + def pause_reading(self): + raise NotImplementedError + + def resume_reading(self): + raise NotImplementedError + + +cdef class _PipeConnectRequest(UVRequest): + cdef: + UnixTransport transport + uv.uv_connect_t _req_data + + def __cinit__(self, loop, transport): + self.request = &self._req_data + self.request.data = self + self.transport = transport + + cdef connect(self, char* addr): + # uv_pipe_connect returns void + uv.uv_pipe_connect(self.request, + self.transport._handle, + addr, + __pipe_connect_callback) + +cdef void __pipe_connect_callback( + uv.uv_connect_t* req, + int status, +) noexcept with gil: + cdef: + _PipeConnectRequest wrapper + UnixTransport transport + + wrapper = <_PipeConnectRequest> req.data + transport = wrapper.transport + + if status < 0: + exc = convert_error(status) + else: + exc = None + + try: + transport._on_connect(exc) + except BaseException as ex: + wrapper.transport._fatal_error(ex, False) + finally: + wrapper.on_done() diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd new file mode 100644 index 0000000..d07030b --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd @@ -0,0 +1,25 @@ +cdef class UVPoll(UVHandle): + cdef: + int fd + Handle reading_handle + Handle writing_handle + + cdef _init(self, Loop loop, int fd) + cdef _close(self) + + cdef inline _poll_start(self, int flags) + cdef inline _poll_stop(self) + + cdef int is_active(self) + + cdef is_reading(self) + cdef is_writing(self) + + cdef start_reading(self, Handle callback) + cdef start_writing(self, Handle callback) + cdef stop_reading(self) + cdef stop_writing(self) + cdef stop(self) + + @staticmethod + cdef UVPoll new(Loop loop, int fd) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx new file mode 100644 index 0000000..fca5981 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx @@ -0,0 +1,233 @@ +@cython.no_gc_clear +cdef class UVPoll(UVHandle): + cdef _init(self, Loop loop, int fd): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_poll_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_poll_init(self._loop.uvloop, + self._handle, fd) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.fd = fd + self.reading_handle = None + self.writing_handle = None + + @staticmethod + cdef UVPoll new(Loop loop, int fd): + cdef UVPoll handle + handle = UVPoll.__new__(UVPoll) + handle._init(loop, fd) + return handle + + cdef int is_active(self): + return (self.reading_handle is not None or + self.writing_handle is not None) + + cdef inline _poll_start(self, int flags): + cdef int err + + self._ensure_alive() + + err = uv.uv_poll_start( + self._handle, + flags, + __on_uvpoll_event) + + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef inline _poll_stop(self): + cdef int err + + if not self._is_alive(): + return + + err = uv.uv_poll_stop(self._handle) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef: + int backend_id + system.epoll_event dummy_event + + if system.PLATFORM_IS_LINUX: + # libuv doesn't remove the FD from epoll immediately + # after uv_poll_stop or uv_poll_close, causing hard + # to debug issue with dup-ed file descriptors causing + # CPU burn in epoll/epoll_ctl: + # https://github.com/MagicStack/uvloop/issues/61 + # + # It's safe though to manually call epoll_ctl here, + # after calling uv_poll_stop. + + backend_id = uv.uv_backend_fd(self._loop.uvloop) + if backend_id != -1: + memset(&dummy_event, 0, sizeof(dummy_event)) + system.epoll_ctl( + backend_id, + system.EPOLL_CTL_DEL, + self.fd, + &dummy_event) # ignore errors + + cdef is_reading(self): + return self._is_alive() and self.reading_handle is not None + + cdef is_writing(self): + return self._is_alive() and self.writing_handle is not None + + cdef start_reading(self, Handle callback): + cdef: + int mask = 0 + + if self.reading_handle is None: + # not reading right now, setup the handle + + mask = uv.UV_READABLE + if self.writing_handle is not None: + # are we writing right now? + mask |= uv.UV_WRITABLE + + self._poll_start(mask) + else: + self.reading_handle._cancel() + + self.reading_handle = callback + + cdef start_writing(self, Handle callback): + cdef: + int mask = 0 + + if self.writing_handle is None: + # not writing right now, setup the handle + + mask = uv.UV_WRITABLE + if self.reading_handle is not None: + # are we reading right now? + mask |= uv.UV_READABLE + + self._poll_start(mask) + else: + self.writing_handle._cancel() + + self.writing_handle = callback + + cdef stop_reading(self): + if self.reading_handle is None: + return False + + self.reading_handle._cancel() + self.reading_handle = None + + if self.writing_handle is None: + self.stop() + else: + self._poll_start(uv.UV_WRITABLE) + + return True + + cdef stop_writing(self): + if self.writing_handle is None: + return False + + self.writing_handle._cancel() + self.writing_handle = None + + if self.reading_handle is None: + self.stop() + else: + self._poll_start(uv.UV_READABLE) + + return True + + cdef stop(self): + if self.reading_handle is not None: + self.reading_handle._cancel() + self.reading_handle = None + + if self.writing_handle is not None: + self.writing_handle._cancel() + self.writing_handle = None + + self._poll_stop() + + cdef _close(self): + if self.is_active(): + self.stop() + + UVHandle._close(self) + + cdef _fatal_error(self, exc, throw, reason=None): + try: + if self.reading_handle is not None: + try: + self.reading_handle._run() + except BaseException as ex: + self._loop._handle_exception(ex) + self.reading_handle = None + + if self.writing_handle is not None: + try: + self.writing_handle._run() + except BaseException as ex: + self._loop._handle_exception(ex) + self.writing_handle = None + + finally: + self._close() + + +cdef void __on_uvpoll_event( + uv.uv_poll_t* handle, + int status, + int events, +) noexcept with gil: + + if __ensure_handle_data(handle, "UVPoll callback") == 0: + return + + cdef: + UVPoll poll = handle.data + + if status < 0: + exc = convert_error(status) + poll._fatal_error(exc, False) + return + + if ((events & (uv.UV_READABLE | uv.UV_DISCONNECT)) and + poll.reading_handle is not None): + + try: + if UVLOOP_DEBUG: + poll._loop._poll_read_events_total += 1 + poll.reading_handle._run() + except BaseException as ex: + if UVLOOP_DEBUG: + poll._loop._poll_read_cb_errors_total += 1 + poll._error(ex, False) + # continue code execution + + if ((events & (uv.UV_WRITABLE | uv.UV_DISCONNECT)) and + poll.writing_handle is not None): + + try: + if UVLOOP_DEBUG: + poll._loop._poll_write_events_total += 1 + poll.writing_handle._run() + except BaseException as ex: + if UVLOOP_DEBUG: + poll._loop._poll_write_cb_errors_total += 1 + poll._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/process.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/process.pxd new file mode 100644 index 0000000..970abcf --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/process.pxd @@ -0,0 +1,80 @@ +cdef class UVProcess(UVHandle): + cdef: + object _returncode + object _pid + + object _errpipe_read + object _errpipe_write + object _preexec_fn + bint _restore_signals + + list _fds_to_close + + # Attributes used to compose uv_process_options_t: + uv.uv_process_options_t options + uv.uv_stdio_container_t[3] iocnt + list __env + char **uv_opt_env + list __args + char **uv_opt_args + char *uv_opt_file + bytes __cwd + + cdef _close_process_handle(self) + + cdef _init(self, Loop loop, list args, dict env, cwd, + start_new_session, + _stdin, _stdout, _stderr, pass_fds, + debug_flags, preexec_fn, restore_signals) + + cdef _after_fork(self) + + cdef char** __to_cstring_array(self, list arr) + cdef _init_args(self, list args) + cdef _init_env(self, dict env) + cdef _init_files(self, _stdin, _stdout, _stderr) + cdef _init_options(self, list args, dict env, cwd, start_new_session, + _stdin, _stdout, _stderr, bint force_fork) + + cdef _close_after_spawn(self, int fd) + + cdef _on_exit(self, int64_t exit_status, int term_signal) + cdef _kill(self, int signum) + + +cdef class UVProcessTransport(UVProcess): + cdef: + list _exit_waiters + list _init_futs + bint _stdio_ready + list _pending_calls + object _protocol + bint _finished + + WriteUnixTransport _stdin + ReadUnixTransport _stdout + ReadUnixTransport _stderr + + object stdin_proto + object stdout_proto + object stderr_proto + + cdef _file_redirect_stdio(self, int fd) + cdef _file_devnull(self) + cdef _file_inpipe(self) + cdef _file_outpipe(self) + + cdef _check_proc(self) + cdef _pipe_connection_lost(self, int fd, exc) + cdef _pipe_data_received(self, int fd, data) + + cdef _call_connection_made(self, waiter) + cdef _try_finish(self) + + @staticmethod + cdef UVProcessTransport new(Loop loop, protocol, args, env, cwd, + start_new_session, + _stdin, _stdout, _stderr, pass_fds, + waiter, + debug_flags, + preexec_fn, restore_signals) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/process.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/process.pyx new file mode 100644 index 0000000..63b982a --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/process.pyx @@ -0,0 +1,792 @@ +@cython.no_gc_clear +cdef class UVProcess(UVHandle): + """Abstract class; wrapper over uv_process_t handle.""" + + def __cinit__(self): + self.uv_opt_env = NULL + self.uv_opt_args = NULL + self._returncode = None + self._pid = None + self._fds_to_close = list() + self._preexec_fn = None + self._restore_signals = True + self.context = Context_CopyCurrent() + + cdef _close_process_handle(self): + # XXX: This is a workaround for a libuv bug: + # - https://github.com/libuv/libuv/issues/1933 + # - https://github.com/libuv/libuv/pull/551 + if self._handle is NULL: + return + self._handle.data = NULL + uv.uv_close(self._handle, __uv_close_process_handle_cb) + self._handle = NULL # close callback will free() the memory + + cdef _init(self, Loop loop, list args, dict env, + cwd, start_new_session, + _stdin, _stdout, _stderr, # std* can be defined as macros in C + pass_fds, debug_flags, preexec_fn, restore_signals): + + global __forking + global __forking_loop + global __forkHandler + + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc( + sizeof(uv.uv_process_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + # Too early to call _finish_init, but still a lot of work to do. + # Let's set handle.data to NULL, so in case something goes wrong, + # callbacks have a chance to avoid casting *something* into UVHandle. + self._handle.data = NULL + + force_fork = False + if system.PLATFORM_IS_APPLE and not ( + preexec_fn is None + and not pass_fds + ): + # see _execute_child() in CPython/subprocess.py + force_fork = True + + try: + self._init_options(args, env, cwd, start_new_session, + _stdin, _stdout, _stderr, force_fork) + + restore_inheritable = set() + if pass_fds: + for fd in pass_fds: + if not os_get_inheritable(fd): + restore_inheritable.add(fd) + os_set_inheritable(fd, True) + except Exception: + self._abort_init() + raise + + if __forking or loop.active_process_handler is not None: + # Our pthread_atfork handlers won't work correctly when + # another loop is forking in another thread (even though + # GIL should help us to avoid that.) + self._abort_init() + raise RuntimeError( + 'Racing with another loop to spawn a process.') + + self._errpipe_read, self._errpipe_write = os_pipe() + fds_to_close = self._fds_to_close + self._fds_to_close = None + fds_to_close.append(self._errpipe_read) + # add the write pipe last so we can close it early + fds_to_close.append(self._errpipe_write) + try: + os_set_inheritable(self._errpipe_write, True) + + self._preexec_fn = preexec_fn + self._restore_signals = restore_signals + + loop.active_process_handler = self + __forking = 1 + __forking_loop = loop + system.setForkHandler(&__get_fork_handler) + + PyOS_BeforeFork() + + err = uv.uv_spawn(loop.uvloop, + self._handle, + &self.options) + + __forking = 0 + __forking_loop = None + system.resetForkHandler() + loop.active_process_handler = None + + PyOS_AfterFork_Parent() + + if err < 0: + self._close_process_handle() + self._abort_init() + raise convert_error(err) + + self._finish_init() + + # close the write pipe early + os_close(fds_to_close.pop()) + + if preexec_fn is not None: + errpipe_data = bytearray() + while True: + # XXX: This is a blocking code that has to be + # rewritten (using loop.connect_read_pipe() or + # otherwise.) + part = os_read(self._errpipe_read, 50000) + errpipe_data += part + if not part or len(errpipe_data) > 50000: + break + + finally: + while fds_to_close: + os_close(fds_to_close.pop()) + + for fd in restore_inheritable: + os_set_inheritable(fd, False) + + # asyncio caches the PID in BaseSubprocessTransport, + # so that the transport knows what the PID was even + # after the process is finished. + self._pid = (self._handle).pid + + # Track the process handle (create a strong ref to it) + # to guarantee that __dealloc__ doesn't happen in an + # uncontrolled fashion. We want to wait until the process + # exits and libuv calls __uvprocess_on_exit_callback, + # which will call `UVProcess._close()`, which will, in turn, + # untrack this handle. + self._loop._track_process(self) + + if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK: + time_sleep(1) + + if preexec_fn is not None and errpipe_data: + # preexec_fn has raised an exception. The child + # process must be dead now. + try: + exc_name, exc_msg = errpipe_data.split(b':', 1) + exc_name = exc_name.decode() + exc_msg = exc_msg.decode() + except Exception: + self._close() + raise subprocess_SubprocessError( + 'Bad exception data from child: {!r}'.format( + errpipe_data)) + exc_cls = getattr(__builtins__, exc_name, + subprocess_SubprocessError) + + exc = subprocess_SubprocessError( + 'Exception occurred in preexec_fn.') + exc.__cause__ = exc_cls(exc_msg) + self._close() + raise exc + + cdef _after_fork(self): + # See CPython/_posixsubprocess.c for details + cdef int err + + if self._restore_signals: + _Py_RestoreSignals() + + PyOS_AfterFork_Child() + + err = uv.uv_loop_fork(self._loop.uvloop) + if err < 0: + raise convert_error(err) + + if self._preexec_fn is not None: + try: + gc_disable() + self._preexec_fn() + except BaseException as ex: + try: + with open(self._errpipe_write, 'wb') as f: + f.write(str(ex.__class__.__name__).encode()) + f.write(b':') + f.write(str(ex.args[0]).encode()) + finally: + system._exit(255) + return + else: + os_close(self._errpipe_write) + else: + os_close(self._errpipe_write) + + cdef _close_after_spawn(self, int fd): + if self._fds_to_close is None: + raise RuntimeError( + 'UVProcess._close_after_spawn called after uv_spawn') + self._fds_to_close.append(fd) + + def __dealloc__(self): + if self.uv_opt_env is not NULL: + PyMem_RawFree(self.uv_opt_env) + self.uv_opt_env = NULL + + if self.uv_opt_args is not NULL: + PyMem_RawFree(self.uv_opt_args) + self.uv_opt_args = NULL + + cdef char** __to_cstring_array(self, list arr): + cdef: + int i + ssize_t arr_len = len(arr) + bytes el + + char **ret + + ret = PyMem_RawMalloc((arr_len + 1) * sizeof(char *)) + if ret is NULL: + raise MemoryError() + + for i in range(arr_len): + el = arr[i] + # NB: PyBytes_AsString doesn't copy the data; + # we have to be careful when the "arr" is GCed, + # and it shouldn't be ever mutated. + ret[i] = PyBytes_AsString(el) + + ret[arr_len] = NULL + return ret + + cdef _init_options(self, list args, dict env, cwd, start_new_session, + _stdin, _stdout, _stderr, bint force_fork): + + memset(&self.options, 0, sizeof(uv.uv_process_options_t)) + + self._init_env(env) + self.options.env = self.uv_opt_env + + self._init_args(args) + self.options.file = self.uv_opt_file + self.options.args = self.uv_opt_args + + if start_new_session: + self.options.flags |= uv.UV_PROCESS_DETACHED + + if force_fork: + # This is a hack to work around the change in libuv 1.44: + # > macos: use posix_spawn instead of fork + # where Python subprocess options like preexec_fn are + # crippled. CPython only uses posix_spawn under a pretty + # strict list of conditions (see subprocess.py), and falls + # back to using fork() otherwise. We'd like to simulate such + # behavior with libuv, but unfortunately libuv doesn't + # provide explicit API to choose such implementation detail. + # Based on current (libuv 1.46) behavior, setting + # UV_PROCESS_SETUID or UV_PROCESS_SETGID would reliably make + # libuv fallback to use fork, so let's just use it for now. + self.options.flags |= uv.UV_PROCESS_SETUID + self.options.uid = uv.getuid() + + if cwd is not None: + cwd = os_fspath(cwd) + + if isinstance(cwd, str): + cwd = PyUnicode_EncodeFSDefault(cwd) + if not isinstance(cwd, bytes): + raise ValueError('cwd must be a str or bytes object') + + self.__cwd = cwd + self.options.cwd = PyBytes_AsString(self.__cwd) + + self.options.exit_cb = &__uvprocess_on_exit_callback + + self._init_files(_stdin, _stdout, _stderr) + + cdef _init_args(self, list args): + cdef: + bytes path + int an = len(args) + + if an < 1: + raise ValueError('cannot spawn a process: args are empty') + + self.__args = args.copy() + for i in range(an): + arg = os_fspath(args[i]) + if isinstance(arg, str): + self.__args[i] = PyUnicode_EncodeFSDefault(arg) + elif not isinstance(arg, bytes): + raise TypeError('all args must be str or bytes') + + path = self.__args[0] + self.uv_opt_file = PyBytes_AsString(path) + self.uv_opt_args = self.__to_cstring_array(self.__args) + + cdef _init_env(self, dict env): + if env is not None: + self.__env = list() + for key in env: + val = env[key] + + if isinstance(key, str): + key = PyUnicode_EncodeFSDefault(key) + elif not isinstance(key, bytes): + raise TypeError( + 'all environment vars must be bytes or str') + + if isinstance(val, str): + val = PyUnicode_EncodeFSDefault(val) + elif not isinstance(val, bytes): + raise TypeError( + 'all environment values must be bytes or str') + + self.__env.append(key + b'=' + val) + + self.uv_opt_env = self.__to_cstring_array(self.__env) + else: + self.__env = None + + cdef _init_files(self, _stdin, _stdout, _stderr): + self.options.stdio_count = 0 + + cdef _kill(self, int signum): + cdef int err + self._ensure_alive() + err = uv.uv_process_kill(self._handle, signum) + if err < 0: + raise convert_error(err) + + cdef _on_exit(self, int64_t exit_status, int term_signal): + if term_signal: + # From Python docs: + # A negative value -N indicates that the child was + # terminated by signal N (POSIX only). + self._returncode = -term_signal + else: + self._returncode = exit_status + + self._close() + + cdef _close(self): + try: + if self._loop is not None: + self._loop._untrack_process(self) + finally: + UVHandle._close(self) + + +DEF _CALL_PIPE_DATA_RECEIVED = 0 +DEF _CALL_PIPE_CONNECTION_LOST = 1 +DEF _CALL_PROCESS_EXITED = 2 +DEF _CALL_CONNECTION_LOST = 3 + + +@cython.no_gc_clear +cdef class UVProcessTransport(UVProcess): + def __cinit__(self): + self._exit_waiters = [] + self._protocol = None + + self._init_futs = [] + self._pending_calls = [] + self._stdio_ready = 0 + + self._stdin = self._stdout = self._stderr = None + self.stdin_proto = self.stdout_proto = self.stderr_proto = None + + self._finished = 0 + + cdef _on_exit(self, int64_t exit_status, int term_signal): + UVProcess._on_exit(self, exit_status, term_signal) + + if self._stdio_ready: + self._loop.call_soon(self._protocol.process_exited, + context=self.context) + else: + self._pending_calls.append((_CALL_PROCESS_EXITED, None, None)) + + self._try_finish() + + for waiter in self._exit_waiters: + if not waiter.cancelled(): + waiter.set_result(self._returncode) + self._exit_waiters.clear() + + self._close() + + cdef _check_proc(self): + if not self._is_alive() or self._returncode is not None: + raise ProcessLookupError() + + cdef _pipe_connection_lost(self, int fd, exc): + if self._stdio_ready: + self._loop.call_soon(self._protocol.pipe_connection_lost, fd, exc, + context=self.context) + self._try_finish() + else: + self._pending_calls.append((_CALL_PIPE_CONNECTION_LOST, fd, exc)) + + cdef _pipe_data_received(self, int fd, data): + if self._stdio_ready: + self._loop.call_soon(self._protocol.pipe_data_received, fd, data, + context=self.context) + else: + self._pending_calls.append((_CALL_PIPE_DATA_RECEIVED, fd, data)) + + cdef _file_redirect_stdio(self, int fd): + fd = os_dup(fd) + os_set_inheritable(fd, True) + self._close_after_spawn(fd) + return fd + + cdef _file_devnull(self): + dn = os_open(os_devnull, os_O_RDWR) + os_set_inheritable(dn, True) + self._close_after_spawn(dn) + return dn + + cdef _file_outpipe(self): + r, w = __socketpair() + os_set_inheritable(w, True) + self._close_after_spawn(w) + return r, w + + cdef _file_inpipe(self): + r, w = __socketpair() + os_set_inheritable(r, True) + self._close_after_spawn(r) + return r, w + + cdef _init_files(self, _stdin, _stdout, _stderr): + cdef uv.uv_stdio_container_t *iocnt + + UVProcess._init_files(self, _stdin, _stdout, _stderr) + + io = [None, None, None] + + self.options.stdio_count = 3 + self.options.stdio = self.iocnt + + if _stdin is not None: + if _stdin == subprocess_PIPE: + r, w = self._file_inpipe() + io[0] = r + + self.stdin_proto = WriteSubprocessPipeProto(self, 0) + waiter = self._loop._new_future() + self._stdin = WriteUnixTransport.new( + self._loop, self.stdin_proto, None, waiter) + self._init_futs.append(waiter) + self._stdin._open(w) + self._stdin._init_protocol() + elif _stdin == subprocess_DEVNULL: + io[0] = self._file_devnull() + elif _stdout == subprocess_STDOUT: + raise ValueError( + 'subprocess.STDOUT is supported only by stderr parameter') + else: + io[0] = self._file_redirect_stdio(_stdin) + else: + io[0] = self._file_redirect_stdio(0) + + if _stdout is not None: + if _stdout == subprocess_PIPE: + # We can't use UV_CREATE_PIPE here, since 'stderr' might be + # set to 'subprocess.STDOUT', and there is no way to + # emulate that functionality with libuv high-level + # streams API. Therefore, we create pipes for stdout and + # stderr manually. + + r, w = self._file_outpipe() + io[1] = w + + self.stdout_proto = ReadSubprocessPipeProto(self, 1) + waiter = self._loop._new_future() + self._stdout = ReadUnixTransport.new( + self._loop, self.stdout_proto, None, waiter) + self._init_futs.append(waiter) + self._stdout._open(r) + self._stdout._init_protocol() + elif _stdout == subprocess_DEVNULL: + io[1] = self._file_devnull() + elif _stdout == subprocess_STDOUT: + raise ValueError( + 'subprocess.STDOUT is supported only by stderr parameter') + else: + io[1] = self._file_redirect_stdio(_stdout) + else: + io[1] = self._file_redirect_stdio(1) + + if _stderr is not None: + if _stderr == subprocess_PIPE: + r, w = self._file_outpipe() + io[2] = w + + self.stderr_proto = ReadSubprocessPipeProto(self, 2) + waiter = self._loop._new_future() + self._stderr = ReadUnixTransport.new( + self._loop, self.stderr_proto, None, waiter) + self._init_futs.append(waiter) + self._stderr._open(r) + self._stderr._init_protocol() + elif _stderr == subprocess_STDOUT: + if io[1] is None: + # shouldn't ever happen + raise RuntimeError('cannot apply subprocess.STDOUT') + + io[2] = self._file_redirect_stdio(io[1]) + elif _stderr == subprocess_DEVNULL: + io[2] = self._file_devnull() + else: + io[2] = self._file_redirect_stdio(_stderr) + else: + io[2] = self._file_redirect_stdio(2) + + assert len(io) == 3 + for idx in range(3): + iocnt = &self.iocnt[idx] + if io[idx] is not None: + iocnt.flags = uv.UV_INHERIT_FD + iocnt.data.fd = io[idx] + else: + iocnt.flags = uv.UV_IGNORE + + cdef _call_connection_made(self, waiter): + try: + # we're always called in the right context, so just call the user's + self._protocol.connection_made(self) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + if waiter is not None and not waiter.cancelled(): + waiter.set_exception(ex) + else: + raise + else: + if waiter is not None and not waiter.cancelled(): + waiter.set_result(True) + + self._stdio_ready = 1 + if self._pending_calls: + pending_calls = self._pending_calls.copy() + self._pending_calls.clear() + for (type, fd, arg) in pending_calls: + if type == _CALL_PIPE_CONNECTION_LOST: + self._pipe_connection_lost(fd, arg) + elif type == _CALL_PIPE_DATA_RECEIVED: + self._pipe_data_received(fd, arg) + elif type == _CALL_PROCESS_EXITED: + self._loop.call_soon(self._protocol.process_exited) + elif type == _CALL_CONNECTION_LOST: + self._loop.call_soon(self._protocol.connection_lost, None) + + cdef _try_finish(self): + if self._returncode is None or self._finished: + return + + if ((self.stdin_proto is None or self.stdin_proto.disconnected) and + (self.stdout_proto is None or + self.stdout_proto.disconnected) and + (self.stderr_proto is None or + self.stderr_proto.disconnected)): + + self._finished = 1 + + if self._stdio_ready: + # copy self.context for simplicity + self._loop.call_soon(self._protocol.connection_lost, None, + context=self.context) + else: + self._pending_calls.append((_CALL_CONNECTION_LOST, None, None)) + + def __stdio_inited(self, waiter, stdio_fut): + exc = stdio_fut.exception() + if exc is not None: + if waiter is None: + raise exc + else: + waiter.set_exception(exc) + else: + self._loop._call_soon_handle( + new_MethodHandle1(self._loop, + "UVProcessTransport._call_connection_made", + self._call_connection_made, + None, # means to copy the current context + self, waiter)) + + @staticmethod + cdef UVProcessTransport new(Loop loop, protocol, args, env, + cwd, start_new_session, + _stdin, _stdout, _stderr, pass_fds, + waiter, + debug_flags, + preexec_fn, + restore_signals): + + cdef UVProcessTransport handle + handle = UVProcessTransport.__new__(UVProcessTransport) + handle._protocol = protocol + handle._init(loop, args, env, cwd, start_new_session, + __process_convert_fileno(_stdin), + __process_convert_fileno(_stdout), + __process_convert_fileno(_stderr), + pass_fds, + debug_flags, + preexec_fn, + restore_signals) + + if handle._init_futs: + handle._stdio_ready = 0 + init_fut = aio_gather(*handle._init_futs) + # add_done_callback will copy the current context and run the + # callback within the context + init_fut.add_done_callback( + ft_partial(handle.__stdio_inited, waiter)) + else: + handle._stdio_ready = 1 + loop._call_soon_handle( + new_MethodHandle1(loop, + "UVProcessTransport._call_connection_made", + handle._call_connection_made, + None, # means to copy the current context + handle, waiter)) + + return handle + + def get_protocol(self): + return self._protocol + + def set_protocol(self, protocol): + self._protocol = protocol + + def get_pid(self): + return self._pid + + def get_returncode(self): + return self._returncode + + def get_pipe_transport(self, fd): + if fd == 0: + return self._stdin + elif fd == 1: + return self._stdout + elif fd == 2: + return self._stderr + + def terminate(self): + self._check_proc() + self._kill(uv.SIGTERM) + + def kill(self): + self._check_proc() + self._kill(uv.SIGKILL) + + def send_signal(self, int signal): + self._check_proc() + self._kill(signal) + + def is_closing(self): + return self._closed + + def close(self): + if self._returncode is None: + self._kill(uv.SIGKILL) + + if self._stdin is not None: + self._stdin.close() + if self._stdout is not None: + self._stdout.close() + if self._stderr is not None: + self._stderr.close() + + if self._returncode is not None: + # The process is dead, just close the UV handle. + # + # (If "self._returncode is None", the process should have been + # killed already and we're just waiting for a SIGCHLD; after + # which the transport will be GC'ed and the uvhandle will be + # closed in UVHandle.__dealloc__.) + self._close() + + def get_extra_info(self, name, default=None): + return default + + def _wait(self): + fut = self._loop._new_future() + if self._returncode is not None: + fut.set_result(self._returncode) + return fut + + self._exit_waiters.append(fut) + return fut + + +class WriteSubprocessPipeProto(aio_BaseProtocol): + + def __init__(self, proc, fd): + if UVLOOP_DEBUG: + if type(proc) is not UVProcessTransport: + raise TypeError + if not isinstance(fd, int): + raise TypeError + self.proc = proc + self.fd = fd + self.pipe = None + self.disconnected = False + + def connection_made(self, transport): + self.pipe = transport + + def __repr__(self): + return ('<%s fd=%s pipe=%r>' + % (self.__class__.__name__, self.fd, self.pipe)) + + def connection_lost(self, exc): + self.disconnected = True + (self.proc)._pipe_connection_lost(self.fd, exc) + self.proc = None + + def pause_writing(self): + (self.proc)._protocol.pause_writing() + + def resume_writing(self): + (self.proc)._protocol.resume_writing() + + +class ReadSubprocessPipeProto(WriteSubprocessPipeProto, + aio_Protocol): + + def data_received(self, data): + (self.proc)._pipe_data_received(self.fd, data) + + +cdef __process_convert_fileno(object obj): + if obj is None or isinstance(obj, int): + return obj + + fileno = obj.fileno() + if not isinstance(fileno, int): + raise TypeError( + '{!r}.fileno() returned non-integer'.format(obj)) + return fileno + + +cdef void __uvprocess_on_exit_callback( + uv.uv_process_t *handle, + int64_t exit_status, + int term_signal, +) noexcept with gil: + + if __ensure_handle_data(handle, + "UVProcess exit callback") == 0: + return + + cdef UVProcess proc = handle.data + try: + proc._on_exit(exit_status, term_signal) + except BaseException as ex: + proc._error(ex, False) + + +cdef __socketpair(): + cdef: + int fds[2] + int err + + err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds) + if err: + exc = convert_error(-err) + raise exc + + os_set_inheritable(fds[0], False) + os_set_inheritable(fds[1], False) + + return fds[0], fds[1] + + +cdef void __uv_close_process_handle_cb( + uv.uv_handle_t* handle +) noexcept with gil: + PyMem_RawFree(handle) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd new file mode 100644 index 0000000..8ca8743 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd @@ -0,0 +1,50 @@ +cdef class UVStream(UVBaseTransport): + cdef: + uv.uv_shutdown_t _shutdown_req + bint __shutting_down + bint __reading + bint __read_error_close + + bint __buffered + object _protocol_get_buffer + object _protocol_buffer_updated + + bint _eof + list _buffer + size_t _buffer_size + + Py_buffer _read_pybuf + bint _read_pybuf_acquired + + # All "inline" methods are final + + cdef inline _init(self, Loop loop, object protocol, Server server, + object waiter, object context) + + + cdef inline _shutdown(self) + cdef inline _accept(self, UVStream server) + + cdef inline _close_on_read_error(self) + + cdef inline __reading_started(self) + cdef inline __reading_stopped(self) + + # The user API write() and writelines() firstly call _buffer_write() to + # buffer up user data chunks, potentially multiple times in writelines(), + # and then call _initiate_write() to start writing either immediately or in + # the next iteration (loop._queue_write()). + cdef inline _buffer_write(self, object data) + cdef inline _initiate_write(self) + + # _exec_write() is the method that does the actual send, and _try_write() + # is a fast-path used in _exec_write() to send a single chunk. + cdef inline _exec_write(self) + cdef inline _try_write(self, object data) + + cdef _close(self) + + cdef inline _on_accept(self) + cdef inline _on_eof(self) + cdef inline _on_write(self) + cdef inline _on_connect(self, object exc) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx new file mode 100644 index 0000000..d4e02e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx @@ -0,0 +1,1015 @@ +DEF __PREALLOCED_BUFS = 4 + + +@cython.no_gc_clear +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class _StreamWriteContext: + # used to hold additional write request information for uv_write + + cdef: + uv.uv_write_t req + + list buffers + + uv.uv_buf_t uv_bufs_sml[__PREALLOCED_BUFS] + Py_buffer py_bufs_sml[__PREALLOCED_BUFS] + bint py_bufs_sml_inuse + + uv.uv_buf_t* uv_bufs + Py_buffer* py_bufs + size_t py_bufs_len + + uv.uv_buf_t* uv_bufs_start + size_t uv_bufs_len + + UVStream stream + + bint closed + + cdef free_bufs(self): + cdef size_t i + + if self.uv_bufs is not NULL: + PyMem_RawFree(self.uv_bufs) + self.uv_bufs = NULL + if UVLOOP_DEBUG: + if self.py_bufs_sml_inuse: + raise RuntimeError( + '_StreamWriteContext.close: uv_bufs != NULL and ' + 'py_bufs_sml_inuse is True') + + if self.py_bufs is not NULL: + for i from 0 <= i < self.py_bufs_len: + PyBuffer_Release(&self.py_bufs[i]) + PyMem_RawFree(self.py_bufs) + self.py_bufs = NULL + if UVLOOP_DEBUG: + if self.py_bufs_sml_inuse: + raise RuntimeError( + '_StreamWriteContext.close: py_bufs != NULL and ' + 'py_bufs_sml_inuse is True') + + if self.py_bufs_sml_inuse: + for i from 0 <= i < self.py_bufs_len: + PyBuffer_Release(&self.py_bufs_sml[i]) + self.py_bufs_sml_inuse = 0 + + self.py_bufs_len = 0 + self.buffers = None + + cdef close(self): + if self.closed: + return + self.closed = 1 + self.free_bufs() + Py_DECREF(self) + + cdef advance_uv_buf(self, size_t sent): + # Advance the pointer to first uv_buf and the + # pointer to first byte in that buffer. + # + # We do this after a "uv_try_write" call, which + # sometimes sends only a portion of data. + # We then call "advance_uv_buf" on the write + # context, and reuse it in a "uv_write" call. + + cdef: + uv.uv_buf_t* buf + size_t idx + + for idx from 0 <= idx < self.uv_bufs_len: + buf = &self.uv_bufs_start[idx] + if buf.len > sent: + buf.len -= sent + buf.base = buf.base + sent + self.uv_bufs_start = buf + self.uv_bufs_len -= idx + return + else: + sent -= self.uv_bufs_start[idx].len + + if UVLOOP_DEBUG: + if sent < 0: + raise RuntimeError('fatal: sent < 0 in advance_uv_buf') + + raise RuntimeError('fatal: Could not advance _StreamWriteContext') + + @staticmethod + cdef _StreamWriteContext new(UVStream stream, list buffers): + cdef: + _StreamWriteContext ctx + int uv_bufs_idx = 0 + size_t py_bufs_len = 0 + int i + + Py_buffer* p_pybufs + uv.uv_buf_t* p_uvbufs + + ctx = _StreamWriteContext.__new__(_StreamWriteContext) + ctx.stream = None + ctx.closed = 1 + ctx.py_bufs_len = 0 + ctx.py_bufs_sml_inuse = 0 + ctx.uv_bufs = NULL + ctx.py_bufs = NULL + ctx.buffers = buffers + ctx.stream = stream + + if len(buffers) <= __PREALLOCED_BUFS: + # We've got a small number of buffers to write, don't + # need to use malloc. + ctx.py_bufs_sml_inuse = 1 + p_pybufs = &ctx.py_bufs_sml + p_uvbufs = &ctx.uv_bufs_sml + + else: + for buf in buffers: + if UVLOOP_DEBUG: + if not isinstance(buf, (bytes, bytearray, memoryview)): + raise RuntimeError( + 'invalid data in writebuf: an instance of ' + 'bytes, bytearray or memoryview was expected, ' + 'got {}'.format(type(buf))) + + if not PyBytes_CheckExact(buf): + py_bufs_len += 1 + + if py_bufs_len > 0: + ctx.py_bufs = PyMem_RawMalloc( + py_bufs_len * sizeof(Py_buffer)) + if ctx.py_bufs is NULL: + raise MemoryError() + + ctx.uv_bufs = PyMem_RawMalloc( + len(buffers) * sizeof(uv.uv_buf_t)) + if ctx.uv_bufs is NULL: + raise MemoryError() + + p_pybufs = ctx.py_bufs + p_uvbufs = ctx.uv_bufs + + py_bufs_len = 0 + for buf in buffers: + if PyBytes_CheckExact(buf): + # We can only use this hack for bytes since it's + # immutable. For everything else it is only safe to + # use buffer protocol. + p_uvbufs[uv_bufs_idx].base = PyBytes_AS_STRING(buf) + p_uvbufs[uv_bufs_idx].len = Py_SIZE(buf) + + else: + try: + PyObject_GetBuffer( + buf, &p_pybufs[py_bufs_len], PyBUF_SIMPLE) + except Exception: + # This shouldn't ever happen, as `UVStream._buffer_write` + # casts non-bytes objects to `memoryviews`. + ctx.py_bufs_len = py_bufs_len + ctx.free_bufs() + raise + + p_uvbufs[uv_bufs_idx].base = p_pybufs[py_bufs_len].buf + p_uvbufs[uv_bufs_idx].len = p_pybufs[py_bufs_len].len + + py_bufs_len += 1 + + uv_bufs_idx += 1 + + ctx.uv_bufs_start = p_uvbufs + ctx.uv_bufs_len = uv_bufs_idx + + ctx.py_bufs_len = py_bufs_len + ctx.req.data = ctx + + if UVLOOP_DEBUG: + stream._loop._debug_stream_write_ctx_total += 1 + stream._loop._debug_stream_write_ctx_cnt += 1 + + # Do incref after everything else is done. + # Under no circumstances we want `ctx` to be GCed while + # libuv is still working with `ctx.uv_bufs`. + Py_INCREF(ctx) + ctx.closed = 0 + return ctx + + def __dealloc__(self): + if not self.closed: + # Because we do an INCREF in _StreamWriteContext.new, + # __dealloc__ shouldn't ever happen with `self.closed == 1` + raise RuntimeError( + 'open _StreamWriteContext is being deallocated') + + if UVLOOP_DEBUG: + if self.stream is not None: + self.stream._loop._debug_stream_write_ctx_cnt -= 1 + self.stream = None + + +@cython.no_gc_clear +cdef class UVStream(UVBaseTransport): + + def __cinit__(self): + self.__shutting_down = 0 + self.__reading = 0 + self.__read_error_close = 0 + self.__buffered = 0 + self._eof = 0 + self._buffer = [] + self._buffer_size = 0 + + self._protocol_get_buffer = None + self._protocol_buffer_updated = None + + self._read_pybuf_acquired = False + + cdef _set_protocol(self, object protocol): + if protocol is None: + raise TypeError('protocol is required') + + UVBaseTransport._set_protocol(self, protocol) + + if (hasattr(protocol, 'get_buffer') and + not isinstance(protocol, aio_Protocol)): + try: + self._protocol_get_buffer = protocol.get_buffer + self._protocol_buffer_updated = protocol.buffer_updated + self.__buffered = 1 + except AttributeError: + pass + else: + self.__buffered = 0 + + cdef _clear_protocol(self): + UVBaseTransport._clear_protocol(self) + self._protocol_get_buffer = None + self._protocol_buffer_updated = None + self.__buffered = 0 + + cdef inline _shutdown(self): + cdef int err + + if self.__shutting_down: + return + self.__shutting_down = 1 + + self._ensure_alive() + + self._shutdown_req.data = self + err = uv.uv_shutdown(&self._shutdown_req, + self._handle, + __uv_stream_on_shutdown) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef inline _accept(self, UVStream server): + cdef int err + self._ensure_alive() + + err = uv.uv_accept(server._handle, + self._handle) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + self._on_accept() + + cdef inline _close_on_read_error(self): + self.__read_error_close = 1 + + cdef bint _is_reading(self): + return self.__reading + + cdef _start_reading(self): + cdef int err + + if self._closing: + return + + self._ensure_alive() + + if self.__reading: + return + + if self.__buffered: + err = uv.uv_read_start(self._handle, + __uv_stream_buffered_alloc, + __uv_stream_buffered_on_read) + else: + err = uv.uv_read_start(self._handle, + __loop_alloc_buffer, + __uv_stream_on_read) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + else: + # UVStream must live until the read callback is called + self.__reading_started() + + cdef inline __reading_started(self): + if self.__reading: + return + self.__reading = 1 + Py_INCREF(self) + + cdef inline __reading_stopped(self): + if not self.__reading: + return + self.__reading = 0 + Py_DECREF(self) + + cdef _stop_reading(self): + cdef int err + + if not self.__reading: + return + + self._ensure_alive() + + # From libuv docs: + # This function is idempotent and may be safely + # called on a stopped stream. + err = uv.uv_read_stop(self._handle) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + else: + self.__reading_stopped() + + cdef inline _try_write(self, object data): + cdef: + ssize_t written + bint used_buf = 0 + Py_buffer py_buf + void* buf + size_t blen + int saved_errno + int fd + + if (self._handle).write_queue_size != 0: + raise RuntimeError( + 'UVStream._try_write called with data in uv buffers') + + if PyBytes_CheckExact(data): + # We can only use this hack for bytes since it's + # immutable. For everything else it is only safe to + # use buffer protocol. + buf = PyBytes_AS_STRING(data) + blen = Py_SIZE(data) + else: + PyObject_GetBuffer(data, &py_buf, PyBUF_SIMPLE) + used_buf = 1 + buf = py_buf.buf + blen = py_buf.len + + if blen == 0: + # Empty data, do nothing. + return 0 + + fd = self._fileno() + # Use `unistd.h/write` directly, it's faster than + # uv_try_write -- less layers of code. The error + # checking logic is copied from libuv. + written = system.write(fd, buf, blen) + while written == -1 and ( + errno.errno == errno.EINTR or + (system.PLATFORM_IS_APPLE and + errno.errno == errno.EPROTOTYPE)): + # From libuv code (unix/stream.c): + # Due to a possible kernel bug at least in OS X 10.10 "Yosemite", + # EPROTOTYPE can be returned while trying to write to a socket + # that is shutting down. If we retry the write, we should get + # the expected EPIPE instead. + written = system.write(fd, buf, blen) + saved_errno = errno.errno + + if used_buf: + PyBuffer_Release(&py_buf) + + if written < 0: + if saved_errno == errno.EAGAIN or \ + saved_errno == system.EWOULDBLOCK: + return -1 + else: + exc = convert_error(-saved_errno) + self._fatal_error(exc, True) + return + + if UVLOOP_DEBUG: + self._loop._debug_stream_write_tries += 1 + + if written == blen: + return 0 + + return written + + cdef inline _buffer_write(self, object data): + cdef int dlen + + if not PyBytes_CheckExact(data): + data = memoryview(data).cast('b') + + dlen = len(data) + if not dlen: + return + + self._buffer_size += dlen + self._buffer.append(data) + + cdef inline _initiate_write(self): + if (not self._protocol_paused and + (self._handle).write_queue_size == 0 and + self._buffer_size > self._high_water): + # Fast-path. If: + # - the protocol isn't yet paused, + # - there is no data in libuv buffers for this stream, + # - the protocol will be paused if we continue to buffer data + # + # Then: + # - Try to write all buffered data right now. + all_sent = self._exec_write() + if UVLOOP_DEBUG: + if self._buffer_size != 0 or self._buffer != []: + raise RuntimeError( + '_buffer_size is not 0 after a successful _exec_write') + + # There is no need to call `_queue_write` anymore, + # as `uv_write` should be called already. + + if not all_sent: + # If not all of the data was sent successfully, + # we might need to pause the protocol. + self._maybe_pause_protocol() + + elif self._buffer_size > 0: + self._maybe_pause_protocol() + self._loop._queue_write(self) + + cdef inline _exec_write(self): + cdef: + int err + int buf_len + _StreamWriteContext ctx = None + + if self._closed: + # If the handle is closed, just return, it's too + # late to do anything. + return + + buf_len = len(self._buffer) + if not buf_len: + return + + if (self._handle).write_queue_size == 0: + # libuv internal write buffers for this stream are empty. + if buf_len == 1: + # If we only have one piece of data to send, let's + # use our fast implementation of try_write. + data = self._buffer[0] + sent = self._try_write(data) + + if sent is None: + # A `self._fatal_error` was called. + # It might not raise an exception under some + # conditions. + self._buffer_size = 0 + self._buffer.clear() + if not self._closing: + # This should never happen. + raise RuntimeError( + 'stream is open after UVStream._try_write ' + 'returned None') + return + + if sent == 0: + # All data was successfully written. + self._buffer_size = 0 + self._buffer.clear() + # on_write will call "maybe_resume_protocol". + self._on_write() + return True + + if sent > 0: + if UVLOOP_DEBUG: + if sent == len(data): + raise RuntimeError( + '_try_write sent all data and returned ' + 'non-zero') + + if PyBytes_CheckExact(data): + # Cast bytes to memoryview to avoid copying + # data that wasn't sent. + data = memoryview(data) + data = data[sent:] + + self._buffer_size -= sent + self._buffer[0] = data + + # At this point it's either data was sent partially, + # or an EAGAIN has happened. + + else: + ctx = _StreamWriteContext.new(self, self._buffer) + + err = uv.uv_try_write(self._handle, + ctx.uv_bufs_start, + ctx.uv_bufs_len) + + if err > 0: + # Some data was successfully sent. + + if err == self._buffer_size: + # Everything was sent. + ctx.close() + self._buffer.clear() + self._buffer_size = 0 + # on_write will call "maybe_resume_protocol". + self._on_write() + return True + + try: + # Advance pointers to uv_bufs in `ctx`, + # we will reuse it soon for a uv_write + # call. + ctx.advance_uv_buf(err) + except Exception as ex: # This should never happen. + # Let's try to close the `ctx` anyways. + ctx.close() + self._fatal_error(ex, True) + self._buffer.clear() + self._buffer_size = 0 + return + + elif err != uv.UV_EAGAIN: + ctx.close() + exc = convert_error(err) + self._fatal_error(exc, True) + self._buffer.clear() + self._buffer_size = 0 + return + + # fall through + + if ctx is None: + ctx = _StreamWriteContext.new(self, self._buffer) + + err = uv.uv_write(&ctx.req, + self._handle, + ctx.uv_bufs_start, + ctx.uv_bufs_len, + __uv_stream_on_write) + + self._buffer_size = 0 + # Can't use `_buffer.clear()` here: `ctx` holds a reference to + # the `_buffer`. + self._buffer = [] + + if err < 0: + # close write context + ctx.close() + + exc = convert_error(err) + self._fatal_error(exc, True) + return + + self._maybe_resume_protocol() + + cdef size_t _get_write_buffer_size(self): + if self._handle is NULL: + return 0 + return ((self._handle).write_queue_size + + self._buffer_size) + + cdef _close(self): + try: + if self._read_pybuf_acquired: + # Should never happen. libuv always calls uv_alloc/uv_read + # in pairs. + self._loop.call_exception_handler({ + 'transport': self, + 'message': 'XXX: an allocated buffer in transport._close()' + }) + self._read_pybuf_acquired = 0 + PyBuffer_Release(&self._read_pybuf) + + self._stop_reading() + finally: + UVSocketHandle._close(self) + + cdef inline _on_accept(self): + # Ultimately called by __uv_stream_on_listen. + self._init_protocol() + + cdef inline _on_eof(self): + # Any exception raised here will be caught in + # __uv_stream_on_read. + + try: + meth = self._protocol.eof_received + except AttributeError: + keep_open = False + else: + keep_open = run_in_context(self.context, meth) + + if keep_open: + # We're keeping the connection open so the + # protocol can write more, but we still can't + # receive more, so remove the reader callback. + self._stop_reading() + else: + self.close() + + cdef inline _on_write(self): + self._maybe_resume_protocol() + if not self._get_write_buffer_size(): + if self._closing: + self._schedule_call_connection_lost(None) + elif self._eof: + self._shutdown() + + cdef inline _init(self, Loop loop, object protocol, Server server, + object waiter, object context): + self.context = context + self._set_protocol(protocol) + self._start_init(loop) + + if server is not None: + self._set_server(server) + + if waiter is not None: + self._set_waiter(waiter) + + cdef inline _on_connect(self, object exc): + # Called from __tcp_connect_callback (tcp.pyx) and + # __pipe_connect_callback (pipe.pyx). + if exc is None: + self._init_protocol() + else: + if self._waiter is None: + self._fatal_error(exc, False, "connect failed") + elif self._waiter.cancelled(): + # Connect call was cancelled; just close the transport + # silently. + self._close() + elif self._waiter.done(): + self._fatal_error(exc, False, "connect failed") + else: + self._waiter.set_exception(exc) + self._close() + + # === Public API === + + def __repr__(self): + return '<{} closed={} reading={} {:#x}>'.format( + self.__class__.__name__, + self._closed, + self.__reading, + id(self)) + + def write(self, object buf): + self._ensure_alive() + + if self._eof: + raise RuntimeError('Cannot call write() after write_eof()') + if not buf: + return + if self._conn_lost: + self._conn_lost += 1 + return + self._buffer_write(buf) + self._initiate_write() + + def writelines(self, bufs): + self._ensure_alive() + + if self._eof: + raise RuntimeError('Cannot call writelines() after write_eof()') + if self._conn_lost: + self._conn_lost += 1 + return + for buf in bufs: + self._buffer_write(buf) + self._initiate_write() + + def write_eof(self): + self._ensure_alive() + + if self._eof: + return + + self._eof = 1 + if not self._get_write_buffer_size(): + self._shutdown() + + def can_write_eof(self): + return True + + def is_reading(self): + return self._is_reading() + + def pause_reading(self): + if self._closing or not self._is_reading(): + return + self._stop_reading() + + def resume_reading(self): + if self._is_reading() or self._closing: + return + self._start_reading() + + +cdef void __uv_stream_on_shutdown(uv.uv_shutdown_t* req, + int status) noexcept with gil: + + # callback for uv_shutdown + + if req.data is NULL: + aio_logger.error( + 'UVStream.shutdown callback called with NULL req.data, status=%r', + status) + return + + cdef UVStream stream = req.data + + if status < 0 and status != uv.UV_ECANCELED: + # From libuv source code: + # The ECANCELED error code is a lie, the shutdown(2) syscall is a + # fait accompli at this point. Maybe we should revisit this in + # v0.11. A possible reason for leaving it unchanged is that it + # informs the callee that the handle has been destroyed. + + if UVLOOP_DEBUG: + stream._loop._debug_stream_shutdown_errors_total += 1 + + exc = convert_error(status) + stream._fatal_error( + exc, False, "error status in uv_stream_t.shutdown callback") + return + + +cdef inline bint __uv_stream_on_read_common( + UVStream sc, + Loop loop, + ssize_t nread, +): + if sc._closed: + # The stream was closed, there is no reason to + # do any work now. + sc.__reading_stopped() # Just in case. + return True + + if nread == uv.UV_EOF: + # From libuv docs: + # The callee is responsible for stopping closing the stream + # when an error happens by calling uv_read_stop() or uv_close(). + # Trying to read from the stream again is undefined. + try: + if UVLOOP_DEBUG: + loop._debug_stream_read_eof_total += 1 + + sc._stop_reading() + sc._on_eof() + except BaseException as ex: + if UVLOOP_DEBUG: + loop._debug_stream_read_eof_cb_errors_total += 1 + + sc._fatal_error(ex, False) + finally: + return True + + if nread == 0: + # From libuv docs: + # nread might be 0, which does not indicate an error or EOF. + # This is equivalent to EAGAIN or EWOULDBLOCK under read(2). + return True + + if nread < 0: + # From libuv docs: + # The callee is responsible for stopping closing the stream + # when an error happens by calling uv_read_stop() or uv_close(). + # Trying to read from the stream again is undefined. + # + # Therefore, we're closing the stream. Since "UVHandle._close()" + # doesn't raise exceptions unless uvloop is built with DEBUG=1, + # we don't need try...finally here. + + if UVLOOP_DEBUG: + loop._debug_stream_read_errors_total += 1 + + if sc.__read_error_close: + # Used for getting notified when a pipe is closed. + # See WriteUnixTransport for the explanation. + sc._on_eof() + return True + + exc = convert_error(nread) + sc._fatal_error( + exc, False, "error status in uv_stream_t.read callback") + return True + + return False + + +cdef inline void __uv_stream_on_read_impl( + uv.uv_stream_t* stream, + ssize_t nread, + const uv.uv_buf_t* buf, +): + cdef: + UVStream sc = stream.data + Loop loop = sc._loop + + # It's OK to free the buffer early, since nothing will + # be able to touch it until this method is done. + __loop_free_buffer(loop) + + if __uv_stream_on_read_common(sc, loop, nread): + return + + try: + if UVLOOP_DEBUG: + loop._debug_stream_read_cb_total += 1 + + run_in_context1( + sc.context, + sc._protocol_data_received, + loop._recv_buffer[:nread], + ) + except BaseException as exc: + if UVLOOP_DEBUG: + loop._debug_stream_read_cb_errors_total += 1 + + sc._fatal_error(exc, False) + + +cdef inline void __uv_stream_on_write_impl( + uv.uv_write_t* req, + int status, +): + cdef: + _StreamWriteContext ctx = <_StreamWriteContext> req.data + UVStream stream = ctx.stream + + ctx.close() + + if stream._closed: + # The stream was closed, there is nothing to do. + # Even if there is an error, like EPIPE, there + # is no reason to report it. + return + + if status < 0: + if UVLOOP_DEBUG: + stream._loop._debug_stream_write_errors_total += 1 + + exc = convert_error(status) + stream._fatal_error( + exc, False, "error status in uv_stream_t.write callback") + return + + try: + stream._on_write() + except BaseException as exc: + if UVLOOP_DEBUG: + stream._loop._debug_stream_write_cb_errors_total += 1 + + stream._fatal_error(exc, False) + + +cdef void __uv_stream_on_read( + uv.uv_stream_t* stream, + ssize_t nread, + const uv.uv_buf_t* buf, +) noexcept with gil: + + if __ensure_handle_data(stream, + "UVStream read callback") == 0: + return + + # Don't need try-finally, __uv_stream_on_read_impl is void + __uv_stream_on_read_impl(stream, nread, buf) + + +cdef void __uv_stream_on_write( + uv.uv_write_t* req, + int status, +) noexcept with gil: + + if UVLOOP_DEBUG: + if req.data is NULL: + aio_logger.error( + 'UVStream.write callback called with NULL req.data, status=%r', + status) + return + + # Don't need try-finally, __uv_stream_on_write_impl is void + __uv_stream_on_write_impl(req, status) + + +cdef void __uv_stream_buffered_alloc( + uv.uv_handle_t* stream, + size_t suggested_size, + uv.uv_buf_t* uvbuf, +) noexcept with gil: + + if __ensure_handle_data(stream, + "UVStream alloc buffer callback") == 0: + return + + cdef: + UVStream sc = stream.data + Loop loop = sc._loop + Py_buffer* pybuf = &sc._read_pybuf + int got_buf = 0 + + if sc._read_pybuf_acquired: + uvbuf.len = 0 + uvbuf.base = NULL + return + + sc._read_pybuf_acquired = 0 + try: + buf = run_in_context1( + sc.context, + sc._protocol_get_buffer, + suggested_size, + ) + PyObject_GetBuffer(buf, pybuf, PyBUF_WRITABLE) + got_buf = 1 + except BaseException as exc: + # Can't call 'sc._fatal_error' or 'sc._close', libuv will SF. + # We'll do it later in __uv_stream_buffered_on_read when we + # receive UV_ENOBUFS. + uvbuf.len = 0 + uvbuf.base = NULL + return + + if not pybuf.len: + uvbuf.len = 0 + uvbuf.base = NULL + if got_buf: + PyBuffer_Release(pybuf) + return + + sc._read_pybuf_acquired = 1 + uvbuf.base = pybuf.buf + uvbuf.len = pybuf.len + + +cdef void __uv_stream_buffered_on_read( + uv.uv_stream_t* stream, + ssize_t nread, + const uv.uv_buf_t* buf, +) noexcept with gil: + + if __ensure_handle_data(stream, + "UVStream buffered read callback") == 0: + return + + cdef: + UVStream sc = stream.data + Loop loop = sc._loop + Py_buffer* pybuf = &sc._read_pybuf + + if nread == uv.UV_ENOBUFS: + sc._fatal_error( + RuntimeError( + 'unhandled error (or an empty buffer) in get_buffer()'), + False) + return + + try: + if nread > 0 and not sc._read_pybuf_acquired: + # From libuv docs: + # nread is > 0 if there is data available or < 0 on error. When + # we’ve reached EOF, nread will be set to UV_EOF. When + # nread < 0, the buf parameter might not point to a valid + # buffer; in that case buf.len and buf.base are both set to 0. + raise RuntimeError( + f'no python buffer is allocated in on_read; nread={nread}') + + if nread == 0: + # From libuv docs: + # nread might be 0, which does not indicate an error or EOF. + # This is equivalent to EAGAIN or EWOULDBLOCK under read(2). + return + + if __uv_stream_on_read_common(sc, loop, nread): + return + + if UVLOOP_DEBUG: + loop._debug_stream_read_cb_total += 1 + + run_in_context1(sc.context, sc._protocol_buffer_updated, nread) + except BaseException as exc: + if UVLOOP_DEBUG: + loop._debug_stream_read_cb_errors_total += 1 + + sc._fatal_error(exc, False) + finally: + sc._read_pybuf_acquired = 0 + PyBuffer_Release(pybuf) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd new file mode 100644 index 0000000..a004efd --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd @@ -0,0 +1,26 @@ +cdef class UVStreamServer(UVSocketHandle): + cdef: + int backlog + object ssl + object ssl_handshake_timeout + object ssl_shutdown_timeout + object protocol_factory + bint opened + Server _server + + # All "inline" methods are final + + cdef inline _init(self, Loop loop, object protocol_factory, + Server server, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout) + + cdef inline _mark_as_open(self) + + cdef inline listen(self) + cdef inline _on_listen(self) + + cdef UVStream _make_new_transport(self, object protocol, object waiter, + object context) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx new file mode 100644 index 0000000..9993317 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx @@ -0,0 +1,150 @@ +@cython.no_gc_clear +cdef class UVStreamServer(UVSocketHandle): + + def __cinit__(self): + self.opened = 0 + self._server = None + self.ssl = None + self.ssl_handshake_timeout = None + self.ssl_shutdown_timeout = None + self.protocol_factory = None + + cdef inline _init(self, Loop loop, object protocol_factory, + Server server, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout): + + if not isinstance(backlog, int): + # Don't allow floats + raise TypeError('integer argument expected, got {}'.format( + type(backlog).__name__)) + + if ssl is not None: + if not isinstance(ssl, ssl_SSLContext): + raise TypeError( + 'ssl is expected to be None or an instance of ' + 'ssl.SSLContext, got {!r}'.format(ssl)) + else: + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + self.backlog = backlog + self.ssl = ssl + self.ssl_handshake_timeout = ssl_handshake_timeout + self.ssl_shutdown_timeout = ssl_shutdown_timeout + + self._start_init(loop) + self.protocol_factory = protocol_factory + self._server = server + + cdef inline listen(self): + cdef int err + self._ensure_alive() + + if self.protocol_factory is None: + raise RuntimeError('unable to listen(); no protocol_factory') + + if self.opened != 1: + raise RuntimeError('unopened TCPServer') + + self.context = Context_CopyCurrent() + + err = uv.uv_listen( self._handle, + self.backlog, + __uv_streamserver_on_listen) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef inline _on_listen(self): + cdef UVStream client + + protocol = run_in_context(self.context, self.protocol_factory) + + if self.ssl is None: + client = self._make_new_transport(protocol, None, self.context) + + else: + waiter = self._loop._new_future() + + ssl_protocol = SSLProtocol( + self._loop, protocol, self.ssl, + waiter, + server_side=True, + server_hostname=None, + ssl_handshake_timeout=self.ssl_handshake_timeout, + ssl_shutdown_timeout=self.ssl_shutdown_timeout) + + client = self._make_new_transport(ssl_protocol, None, self.context) + + waiter.add_done_callback( + ft_partial(self.__on_ssl_connected, client)) + + client._accept(self) + + cdef _fatal_error(self, exc, throw, reason=None): + # Overload UVHandle._fatal_error + + self._close() + + if not isinstance(exc, OSError): + + if throw or self._loop is None: + raise exc + + msg = f'Fatal error on server {self.__class__.__name__}' + if reason is not None: + msg = f'{msg} ({reason})' + + self._loop.call_exception_handler({ + 'message': msg, + 'exception': exc, + }) + + cdef inline _mark_as_open(self): + self.opened = 1 + + cdef UVStream _make_new_transport(self, object protocol, object waiter, + object context): + raise NotImplementedError + + def __on_ssl_connected(self, transport, fut): + exc = fut.exception() + if exc is not None: + transport._force_close(exc) + + +cdef void __uv_streamserver_on_listen( + uv.uv_stream_t* handle, + int status, +) noexcept with gil: + + # callback for uv_listen + + if __ensure_handle_data(handle, + "UVStream listen callback") == 0: + return + + cdef: + UVStreamServer stream = handle.data + + if status < 0: + if UVLOOP_DEBUG: + stream._loop._debug_stream_listen_errors_total += 1 + + exc = convert_error(status) + stream._fatal_error( + exc, False, "error status in uv_stream_t.listen callback") + return + + try: + stream._on_listen() + except BaseException as exc: + stream._error(exc, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd new file mode 100644 index 0000000..8d388ef --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd @@ -0,0 +1,26 @@ +cdef class TCPServer(UVStreamServer): + cdef bind(self, system.sockaddr* addr, unsigned int flags=*) + + @staticmethod + cdef TCPServer new(Loop loop, object protocol_factory, Server server, + unsigned int flags, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout) + + +cdef class TCPTransport(UVStream): + cdef: + bint __peername_set + bint __sockname_set + system.sockaddr_storage __peername + system.sockaddr_storage __sockname + + cdef bind(self, system.sockaddr* addr, unsigned int flags=*) + cdef connect(self, system.sockaddr* addr) + cdef _set_nodelay(self) + + @staticmethod + cdef TCPTransport new(Loop loop, object protocol, Server server, + object waiter, object context) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx new file mode 100644 index 0000000..d5fe827 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx @@ -0,0 +1,228 @@ +cdef __tcp_init_uv_handle(UVStream handle, Loop loop, unsigned int flags): + cdef int err + + handle._handle = PyMem_RawMalloc(sizeof(uv.uv_tcp_t)) + if handle._handle is NULL: + handle._abort_init() + raise MemoryError() + + err = uv.uv_tcp_init_ex(handle._loop.uvloop, + handle._handle, + flags) + if err < 0: + handle._abort_init() + raise convert_error(err) + + handle._finish_init() + + +cdef __tcp_bind(UVStream handle, system.sockaddr* addr, unsigned int flags): + cdef int err + err = uv.uv_tcp_bind(handle._handle, + addr, flags) + if err < 0: + exc = convert_error(err) + raise exc + + +cdef __tcp_open(UVStream handle, int sockfd): + cdef int err + err = uv.uv_tcp_open(handle._handle, + sockfd) + if err < 0: + exc = convert_error(err) + raise exc + + +cdef __tcp_get_socket(UVSocketHandle handle): + cdef: + int buf_len = sizeof(system.sockaddr_storage) + int fileno + int err + system.sockaddr_storage buf + + fileno = handle._fileno() + + err = uv.uv_tcp_getsockname(handle._handle, + &buf, + &buf_len) + if err < 0: + raise convert_error(err) + + return PseudoSocket(buf.ss_family, uv.SOCK_STREAM, 0, fileno) + + +@cython.no_gc_clear +cdef class TCPServer(UVStreamServer): + + @staticmethod + cdef TCPServer new(Loop loop, object protocol_factory, Server server, + unsigned int flags, + object backlog, + object ssl, + object ssl_handshake_timeout, + object ssl_shutdown_timeout): + + cdef TCPServer handle + handle = TCPServer.__new__(TCPServer) + handle._init(loop, protocol_factory, server, backlog, + ssl, ssl_handshake_timeout, ssl_shutdown_timeout) + __tcp_init_uv_handle(handle, loop, flags) + return handle + + cdef _new_socket(self): + return __tcp_get_socket(self) + + cdef _open(self, int sockfd): + self._ensure_alive() + try: + __tcp_open(self, sockfd) + except Exception as exc: + self._fatal_error(exc, True) + else: + self._mark_as_open() + + cdef bind(self, system.sockaddr* addr, unsigned int flags=0): + self._ensure_alive() + try: + __tcp_bind(self, addr, flags) + except Exception as exc: + self._fatal_error(exc, True) + else: + self._mark_as_open() + + cdef UVStream _make_new_transport(self, object protocol, object waiter, + object context): + cdef TCPTransport tr + tr = TCPTransport.new(self._loop, protocol, self._server, waiter, + context) + return tr + + +@cython.no_gc_clear +cdef class TCPTransport(UVStream): + + @staticmethod + cdef TCPTransport new(Loop loop, object protocol, Server server, + object waiter, object context): + + cdef TCPTransport handle + handle = TCPTransport.__new__(TCPTransport) + handle._init(loop, protocol, server, waiter, context) + __tcp_init_uv_handle(handle, loop, uv.AF_UNSPEC) + handle.__peername_set = 0 + handle.__sockname_set = 0 + handle._set_nodelay() + return handle + + cdef _set_nodelay(self): + cdef int err + self._ensure_alive() + err = uv.uv_tcp_nodelay(self._handle, 1) + if err < 0: + raise convert_error(err) + + cdef _call_connection_made(self): + # asyncio saves peername & sockname when transports are instantiated, + # so that they're accessible even after the transport is closed. + # We are doing the same thing here, except that we create Python + # objects lazily, on request in get_extra_info() + + cdef: + int err + int buf_len + + buf_len = sizeof(system.sockaddr_storage) + err = uv.uv_tcp_getsockname(self._handle, + &self.__sockname, + &buf_len) + if err >= 0: + # Ignore errors, this is an optional thing. + # If something serious is going on, the transport + # will crash later (in roughly the same way how + # an asyncio transport would.) + self.__sockname_set = 1 + + buf_len = sizeof(system.sockaddr_storage) + err = uv.uv_tcp_getpeername(self._handle, + &self.__peername, + &buf_len) + if err >= 0: + # Same as few lines above -- we don't really care + # about error case here. + self.__peername_set = 1 + + UVBaseTransport._call_connection_made(self) + + def get_extra_info(self, name, default=None): + if name == 'sockname': + if self.__sockname_set: + return __convert_sockaddr_to_pyaddr( + &self.__sockname) + elif name == 'peername': + if self.__peername_set: + return __convert_sockaddr_to_pyaddr( + &self.__peername) + return super().get_extra_info(name, default) + + cdef _new_socket(self): + return __tcp_get_socket(self) + + cdef bind(self, system.sockaddr* addr, unsigned int flags=0): + self._ensure_alive() + __tcp_bind(self, addr, flags) + + cdef _open(self, int sockfd): + self._ensure_alive() + __tcp_open(self, sockfd) + + cdef connect(self, system.sockaddr* addr): + cdef _TCPConnectRequest req + req = _TCPConnectRequest(self._loop, self) + req.connect(addr) + + +cdef class _TCPConnectRequest(UVRequest): + cdef: + TCPTransport transport + uv.uv_connect_t _req_data + + def __cinit__(self, loop, transport): + self.request = &self._req_data + self.request.data = self + self.transport = transport + + cdef connect(self, system.sockaddr* addr): + cdef int err + err = uv.uv_tcp_connect(self.request, + self.transport._handle, + addr, + __tcp_connect_callback) + if err < 0: + exc = convert_error(err) + self.on_done() + raise exc + + +cdef void __tcp_connect_callback( + uv.uv_connect_t* req, + int status, +) noexcept with gil: + cdef: + _TCPConnectRequest wrapper + TCPTransport transport + + wrapper = <_TCPConnectRequest> req.data + transport = wrapper.transport + + if status < 0: + exc = convert_error(status) + else: + exc = None + + try: + transport._on_connect(exc) + except BaseException as ex: + wrapper.transport._fatal_error(ex, False) + finally: + wrapper.on_done() diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/timer.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/timer.pxd new file mode 100644 index 0000000..fda23b6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/timer.pxd @@ -0,0 +1,18 @@ +cdef class UVTimer(UVHandle): + cdef: + method_t callback + object ctx + bint running + uint64_t timeout + uint64_t start_t + + cdef _init(self, Loop loop, method_t callback, object ctx, + uint64_t timeout) + + cdef stop(self) + cdef start(self) + cdef get_when(self) + + @staticmethod + cdef UVTimer new(Loop loop, method_t callback, object ctx, + uint64_t timeout) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/timer.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/timer.pyx new file mode 100644 index 0000000..86d46ef --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/timer.pyx @@ -0,0 +1,89 @@ +@cython.no_gc_clear +cdef class UVTimer(UVHandle): + cdef _init(self, Loop loop, method_t callback, object ctx, + uint64_t timeout): + + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_timer_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_timer_init(self._loop.uvloop, self._handle) + if err < 0: + self._abort_init() + raise convert_error(err) + + self._finish_init() + + self.callback = callback + self.ctx = ctx + self.running = 0 + self.timeout = timeout + self.start_t = 0 + + cdef stop(self): + cdef int err + + if not self._is_alive(): + self.running = 0 + return + + if self.running == 1: + err = uv.uv_timer_stop(self._handle) + self.running = 0 + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + + cdef start(self): + cdef int err + + self._ensure_alive() + + if self.running == 0: + # Update libuv internal time. + uv.uv_update_time(self._loop.uvloop) # void + self.start_t = uv.uv_now(self._loop.uvloop) + + err = uv.uv_timer_start(self._handle, + __uvtimer_callback, + self.timeout, 0) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + self.running = 1 + + cdef get_when(self): + return self.start_t + self.timeout + + @staticmethod + cdef UVTimer new(Loop loop, method_t callback, object ctx, + uint64_t timeout): + + cdef UVTimer handle + handle = UVTimer.__new__(UVTimer) + handle._init(loop, callback, ctx, timeout) + return handle + + +cdef void __uvtimer_callback( + uv.uv_timer_t* handle, +) noexcept with gil: + if __ensure_handle_data(handle, "UVTimer callback") == 0: + return + + cdef: + UVTimer timer = handle.data + method_t cb = timer.callback + + timer.running = 0 + try: + cb(timer.ctx) + except BaseException as ex: + timer._error(ex, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/udp.pxd b/venv/lib/python3.11/site-packages/uvloop/handles/udp.pxd new file mode 100644 index 0000000..daa9a1b --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/udp.pxd @@ -0,0 +1,22 @@ +cdef class UDPTransport(UVBaseTransport): + cdef: + bint __receiving + int _family + object _address + + cdef _init(self, Loop loop, unsigned int family) + cdef _set_address(self, system.addrinfo *addr) + + cdef _connect(self, system.sockaddr* addr, size_t addr_len) + + cdef _bind(self, system.sockaddr* addr) + cdef open(self, int family, int sockfd) + cdef _set_broadcast(self, bint on) + + cdef inline __receiving_started(self) + cdef inline __receiving_stopped(self) + + cdef _send(self, object data, object addr) + + cdef _on_receive(self, bytes data, object exc, object addr) + cdef _on_sent(self, object exc, object context=*) diff --git a/venv/lib/python3.11/site-packages/uvloop/handles/udp.pyx b/venv/lib/python3.11/site-packages/uvloop/handles/udp.pyx new file mode 100644 index 0000000..bbe60d5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/handles/udp.pyx @@ -0,0 +1,409 @@ +@cython.no_gc_clear +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class _UDPSendContext: + # used to hold additional write request information for uv_write + + cdef: + uv.uv_udp_send_t req + + uv.uv_buf_t uv_buf + Py_buffer py_buf + + UDPTransport udp + + bint closed + + cdef close(self): + if self.closed: + return + + self.closed = 1 + PyBuffer_Release(&self.py_buf) # void + self.req.data = NULL + self.uv_buf.base = NULL + Py_DECREF(self) + self.udp = None + + @staticmethod + cdef _UDPSendContext new(UDPTransport udp, object data): + cdef _UDPSendContext ctx + ctx = _UDPSendContext.__new__(_UDPSendContext) + ctx.udp = None + ctx.closed = 1 + + ctx.req.data = ctx + Py_INCREF(ctx) + + PyObject_GetBuffer(data, &ctx.py_buf, PyBUF_SIMPLE) + ctx.uv_buf.base = ctx.py_buf.buf + ctx.uv_buf.len = ctx.py_buf.len + ctx.udp = udp + + ctx.closed = 0 + return ctx + + def __dealloc__(self): + if UVLOOP_DEBUG: + if not self.closed: + raise RuntimeError( + 'open _UDPSendContext is being deallocated') + self.udp = None + + +@cython.no_gc_clear +cdef class UDPTransport(UVBaseTransport): + def __cinit__(self): + self._family = uv.AF_UNSPEC + self.__receiving = 0 + self._address = None + self.context = Context_CopyCurrent() + + cdef _init(self, Loop loop, unsigned int family): + cdef int err + + self._start_init(loop) + + self._handle = PyMem_RawMalloc(sizeof(uv.uv_udp_t)) + if self._handle is NULL: + self._abort_init() + raise MemoryError() + + err = uv.uv_udp_init_ex(loop.uvloop, + self._handle, + family) + if err < 0: + self._abort_init() + raise convert_error(err) + + if family in (uv.AF_INET, uv.AF_INET6): + self._family = family + + self._finish_init() + + cdef _set_address(self, system.addrinfo *addr): + self._address = __convert_sockaddr_to_pyaddr(addr.ai_addr) + + cdef _connect(self, system.sockaddr* addr, size_t addr_len): + cdef int err + err = uv.uv_udp_connect(self._handle, addr) + if err < 0: + exc = convert_error(err) + raise exc + + cdef open(self, int family, int sockfd): + if family in (uv.AF_INET, uv.AF_INET6, uv.AF_UNIX): + self._family = family + else: + raise ValueError( + 'cannot open a UDP handle, invalid family {}'.format(family)) + + cdef int err + err = uv.uv_udp_open(self._handle, + sockfd) + + if err < 0: + exc = convert_error(err) + raise exc + + cdef _bind(self, system.sockaddr* addr): + cdef: + int err + int flags = 0 + + self._ensure_alive() + + err = uv.uv_udp_bind(self._handle, addr, flags) + if err < 0: + exc = convert_error(err) + raise exc + + cdef _set_broadcast(self, bint on): + cdef int err + + self._ensure_alive() + + err = uv.uv_udp_set_broadcast(self._handle, on) + if err < 0: + exc = convert_error(err) + raise exc + + cdef size_t _get_write_buffer_size(self): + if self._handle is NULL: + return 0 + return (self._handle).send_queue_size + + cdef bint _is_reading(self): + return self.__receiving + + cdef _start_reading(self): + cdef int err + + if self.__receiving: + return + + self._ensure_alive() + + err = uv.uv_udp_recv_start(self._handle, + __loop_alloc_buffer, + __uv_udp_on_receive) + + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + else: + # UDPTransport must live until the read callback is called + self.__receiving_started() + + cdef _stop_reading(self): + cdef int err + + if not self.__receiving: + return + + self._ensure_alive() + + err = uv.uv_udp_recv_stop(self._handle) + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + return + else: + self.__receiving_stopped() + + cdef inline __receiving_started(self): + if self.__receiving: + return + self.__receiving = 1 + Py_INCREF(self) + + cdef inline __receiving_stopped(self): + if not self.__receiving: + return + self.__receiving = 0 + Py_DECREF(self) + + cdef _new_socket(self): + if self._family not in (uv.AF_INET, uv.AF_INET6, uv.AF_UNIX): + raise RuntimeError( + 'UDPTransport.family is undefined; ' + 'cannot create python socket') + + fileno = self._fileno() + return PseudoSocket(self._family, uv.SOCK_DGRAM, 0, fileno) + + cdef _send(self, object data, object addr): + cdef: + _UDPSendContext ctx + system.sockaddr_storage saddr_st + system.sockaddr *saddr + Py_buffer try_pybuf + uv.uv_buf_t try_uvbuf + + self._ensure_alive() + + if self._family not in (uv.AF_INET, uv.AF_INET6, uv.AF_UNIX): + raise RuntimeError('UDPTransport.family is undefined; cannot send') + + if addr is None: + saddr = NULL + else: + try: + __convert_pyaddr_to_sockaddr(self._family, addr, + &saddr_st) + except (ValueError, TypeError): + raise + except Exception: + raise ValueError( + f'{addr!r}: socket family mismatch or ' + f'a DNS lookup is required') + saddr = (&saddr_st) + + if self._get_write_buffer_size() == 0: + PyObject_GetBuffer(data, &try_pybuf, PyBUF_SIMPLE) + try_uvbuf.base = try_pybuf.buf + try_uvbuf.len = try_pybuf.len + err = uv.uv_udp_try_send(self._handle, + &try_uvbuf, + 1, + saddr) + PyBuffer_Release(&try_pybuf) + else: + err = uv.UV_EAGAIN + + if err == uv.UV_EAGAIN: + ctx = _UDPSendContext.new(self, data) + err = uv.uv_udp_send(&ctx.req, + self._handle, + &ctx.uv_buf, + 1, + saddr, + __uv_udp_on_send) + + if err < 0: + ctx.close() + + exc = convert_error(err) + self._fatal_error(exc, True) + else: + self._maybe_pause_protocol() + + else: + if err < 0: + exc = convert_error(err) + self._fatal_error(exc, True) + else: + self._on_sent(None, self.context.copy()) + + cdef _on_receive(self, bytes data, object exc, object addr): + if exc is None: + run_in_context2( + self.context, self._protocol.datagram_received, data, addr, + ) + else: + run_in_context1(self.context, self._protocol.error_received, exc) + + cdef _on_sent(self, object exc, object context=None): + if exc is not None: + if isinstance(exc, OSError): + if context is None: + context = self.context + run_in_context1(context, self._protocol.error_received, exc) + else: + self._fatal_error( + exc, False, 'Fatal write error on datagram transport') + + self._maybe_resume_protocol() + if not self._get_write_buffer_size(): + if self._closing: + self._schedule_call_connection_lost(None) + + # === Public API === + + def sendto(self, data, addr=None): + if not data: + # Replicating asyncio logic here. + return + + if self._address: + if addr not in (None, self._address): + # Replicating asyncio logic here. + raise ValueError( + 'Invalid address: must be None or %s' % (self._address,)) + + # Instead of setting addr to self._address below like what asyncio + # does, we depend on previous uv_udp_connect() to set the address + addr = None + + if self._conn_lost: + # Replicating asyncio logic here. + if self._conn_lost >= LOG_THRESHOLD_FOR_CONNLOST_WRITES: + aio_logger.warning('socket.send() raised exception.') + self._conn_lost += 1 + return + + self._send(data, addr) + + +cdef void __uv_udp_on_receive( + uv.uv_udp_t* handle, + ssize_t nread, + const uv.uv_buf_t* buf, + const system.sockaddr* addr, + unsigned flags +) noexcept with gil: + + if __ensure_handle_data(handle, + "UDPTransport receive callback") == 0: + return + + cdef: + UDPTransport udp = handle.data + Loop loop = udp._loop + bytes data + object pyaddr + + # It's OK to free the buffer early, since nothing will + # be able to touch it until this method is done. + __loop_free_buffer(loop) + + if udp._closed: + # The handle was closed, there is no reason to + # do any work now. + udp.__receiving_stopped() # Just in case. + return + + if addr is NULL and nread == 0: + # From libuv docs: + # addr: struct sockaddr* containing the address + # of the sender. Can be NULL. Valid for the duration + # of the callback only. + # [...] + # The receive callback will be called with + # nread == 0 and addr == NULL when there is + # nothing to read, and with nread == 0 and + # addr != NULL when an empty UDP packet is + # received. + return + + if addr is NULL: + pyaddr = None + elif addr.sa_family == uv.AF_UNSPEC: + # https://github.com/MagicStack/uvloop/issues/304 + if system.PLATFORM_IS_LINUX: + pyaddr = None + else: + pyaddr = '' + else: + try: + pyaddr = __convert_sockaddr_to_pyaddr(addr) + except BaseException as exc: + udp._error(exc, False) + return + + if nread < 0: + exc = convert_error(nread) + udp._on_receive(None, exc, pyaddr) + return + + if nread == 0: + data = b'' + else: + data = loop._recv_buffer[:nread] + + try: + udp._on_receive(data, None, pyaddr) + except BaseException as exc: + udp._error(exc, False) + + +cdef void __uv_udp_on_send( + uv.uv_udp_send_t* req, + int status, +) noexcept with gil: + + if req.data is NULL: + # Shouldn't happen as: + # - _UDPSendContext does an extra INCREF in its 'init()' + # - _UDPSendContext holds a ref to the relevant UDPTransport + aio_logger.error( + 'UVStream.write callback called with NULL req.data, status=%r', + status) + return + + cdef: + _UDPSendContext ctx = <_UDPSendContext> req.data + UDPTransport udp = ctx.udp + + ctx.close() + + if status < 0: + exc = convert_error(status) + print(exc) + else: + exc = None + + try: + udp._on_sent(exc) + except BaseException as exc: + udp._error(exc, False) diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/__init__.py b/venv/lib/python3.11/site-packages/uvloop/includes/__init__.py new file mode 100644 index 0000000..2ccf9ca --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/__init__.py @@ -0,0 +1,23 @@ +# flake8: noqa + +# These have to be synced with the stdlib.pxi +import asyncio +import collections +import concurrent.futures +import errno +import functools +import gc +import inspect +import itertools +import os +import signal +import socket +import subprocess +import ssl +import stat +import sys +import threading +import traceback +import time +import warnings +import weakref diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/uvloop/includes/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..a4464aa Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/includes/__pycache__/__init__.cpython-311.pyc differ diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/consts.pxi b/venv/lib/python3.11/site-packages/uvloop/includes/consts.pxi new file mode 100644 index 0000000..f765053 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/consts.pxi @@ -0,0 +1,25 @@ +DEF UV_STREAM_RECV_BUF_SIZE = 256000 # 250kb + +DEF FLOW_CONTROL_HIGH_WATER = 64 # KiB +DEF FLOW_CONTROL_HIGH_WATER_SSL_READ = 256 # KiB +DEF FLOW_CONTROL_HIGH_WATER_SSL_WRITE = 512 # KiB + +DEF DEFAULT_FREELIST_SIZE = 250 +DEF DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE = 2048 + +DEF DEBUG_STACK_DEPTH = 10 + + +DEF __PROCESS_DEBUG_SLEEP_AFTER_FORK = 1 + + +DEF LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5 + + +# Number of seconds to wait for SSL handshake to complete +# The default timeout matches that of Nginx. +DEF SSL_HANDSHAKE_TIMEOUT = 60.0 +# Number of seconds to wait for SSL shutdown to complete +# The default timeout mimics lingering_time +DEF SSL_SHUTDOWN_TIMEOUT = 30.0 +DEF SSL_READ_MAX_SIZE = 256 * 1024 diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/debug.pxd b/venv/lib/python3.11/site-packages/uvloop/includes/debug.pxd new file mode 100644 index 0000000..a825def --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/debug.pxd @@ -0,0 +1,3 @@ +cdef extern from "includes/debug.h": + + cdef int UVLOOP_DEBUG diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/flowcontrol.pxd b/venv/lib/python3.11/site-packages/uvloop/includes/flowcontrol.pxd new file mode 100644 index 0000000..f22f1a7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/flowcontrol.pxd @@ -0,0 +1,23 @@ +# flake8: noqa + + +cdef inline add_flowcontrol_defaults(high, low, int kb): + cdef int h, l + if high is None: + if low is None: + h = kb * 1024 + else: + l = low + h = 4 * l + else: + h = high + if low is None: + l = h // 4 + else: + l = low + + if not h >= l >= 0: + raise ValueError('high (%r) must be >= low (%r) must be >= 0' % + (h, l)) + + return h, l diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/python.pxd b/venv/lib/python3.11/site-packages/uvloop/includes/python.pxd new file mode 100644 index 0000000..454d5c7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/python.pxd @@ -0,0 +1,31 @@ +cdef extern from "Python.h": + int PY_VERSION_HEX + + unicode PyUnicode_FromString(const char *) + + void* PyMem_RawMalloc(size_t n) nogil + void* PyMem_RawRealloc(void *p, size_t n) nogil + void* PyMem_RawCalloc(size_t nelem, size_t elsize) nogil + void PyMem_RawFree(void *p) nogil + + object PyUnicode_EncodeFSDefault(object) + void PyErr_SetInterrupt() nogil + + void _Py_RestoreSignals() + + object PyMemoryView_FromMemory(char *mem, ssize_t size, int flags) + object PyMemoryView_FromObject(object obj) + int PyMemoryView_Check(object obj) + + cdef enum: + PyBUF_WRITE + + +cdef extern from "includes/compat.h": + object Context_CopyCurrent() + int Context_Enter(object) except -1 + int Context_Exit(object) except -1 + + void PyOS_BeforeFork() + void PyOS_AfterFork_Parent() + void PyOS_AfterFork_Child() diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/stdlib.pxi b/venv/lib/python3.11/site-packages/uvloop/includes/stdlib.pxi new file mode 100644 index 0000000..e7957fe --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/stdlib.pxi @@ -0,0 +1,175 @@ +# flake8: noqa + + +import asyncio, asyncio.log, asyncio.base_events, \ + asyncio.sslproto, asyncio.coroutines, \ + asyncio.futures, asyncio.transports +import collections.abc +import concurrent.futures +import errno +import functools +import gc +import inspect +import itertools +import os +import signal +import socket +import subprocess +import ssl +import stat +import sys +import threading +import traceback +import time +import warnings +import weakref + + +cdef aio_get_event_loop = asyncio.get_event_loop +cdef aio_CancelledError = asyncio.CancelledError +cdef aio_InvalidStateError = asyncio.InvalidStateError +cdef aio_TimeoutError = asyncio.TimeoutError +cdef aio_Future = asyncio.Future +cdef aio_Task = asyncio.Task +cdef aio_ensure_future = asyncio.ensure_future +cdef aio_gather = asyncio.gather +cdef aio_wait = asyncio.wait +cdef aio_wrap_future = asyncio.wrap_future +cdef aio_logger = asyncio.log.logger +cdef aio_iscoroutine = asyncio.iscoroutine +cdef aio_iscoroutinefunction = asyncio.iscoroutinefunction +cdef aio_BaseProtocol = asyncio.BaseProtocol +cdef aio_Protocol = asyncio.Protocol +cdef aio_isfuture = getattr(asyncio, 'isfuture', None) +cdef aio_get_running_loop = getattr(asyncio, '_get_running_loop', None) +cdef aio_set_running_loop = getattr(asyncio, '_set_running_loop', None) +cdef aio_debug_wrapper = getattr(asyncio.coroutines, 'debug_wrapper', None) +cdef aio_AbstractChildWatcher = asyncio.AbstractChildWatcher +cdef aio_Transport = asyncio.Transport +cdef aio_FlowControlMixin = asyncio.transports._FlowControlMixin + +cdef col_deque = collections.deque +cdef col_Iterable = collections.abc.Iterable +cdef col_Counter = collections.Counter +cdef col_OrderedDict = collections.OrderedDict + +cdef cc_ThreadPoolExecutor = concurrent.futures.ThreadPoolExecutor +cdef cc_Future = concurrent.futures.Future + +cdef errno_EBADF = errno.EBADF +cdef errno_EINVAL = errno.EINVAL + +cdef ft_partial = functools.partial + +cdef gc_disable = gc.disable + +cdef iter_chain = itertools.chain +cdef inspect_isgenerator = inspect.isgenerator + +cdef int has_IPV6_V6ONLY = hasattr(socket, 'IPV6_V6ONLY') +cdef int IPV6_V6ONLY = getattr(socket, 'IPV6_V6ONLY', -1) +cdef int has_SO_REUSEPORT = hasattr(socket, 'SO_REUSEPORT') +cdef int SO_REUSEPORT = getattr(socket, 'SO_REUSEPORT', 0) +cdef int SO_BROADCAST = getattr(socket, 'SO_BROADCAST') +cdef int SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', -1) +cdef int socket_AI_CANONNAME = getattr(socket, 'AI_CANONNAME') + +cdef socket_gaierror = socket.gaierror +cdef socket_error = socket.error +cdef socket_timeout = socket.timeout +cdef socket_socket = socket.socket +cdef socket_socketpair = socket.socketpair +cdef socket_getservbyname = socket.getservbyname +cdef socket_AddressFamily = socket.AddressFamily +cdef socket_SocketKind = socket.SocketKind + +cdef int socket_EAI_ADDRFAMILY = getattr(socket, 'EAI_ADDRFAMILY', -1) +cdef int socket_EAI_AGAIN = getattr(socket, 'EAI_AGAIN', -1) +cdef int socket_EAI_BADFLAGS = getattr(socket, 'EAI_BADFLAGS', -1) +cdef int socket_EAI_BADHINTS = getattr(socket, 'EAI_BADHINTS', -1) +cdef int socket_EAI_CANCELED = getattr(socket, 'EAI_CANCELED', -1) +cdef int socket_EAI_FAIL = getattr(socket, 'EAI_FAIL', -1) +cdef int socket_EAI_FAMILY = getattr(socket, 'EAI_FAMILY', -1) +cdef int socket_EAI_MEMORY = getattr(socket, 'EAI_MEMORY', -1) +cdef int socket_EAI_NODATA = getattr(socket, 'EAI_NODATA', -1) +cdef int socket_EAI_NONAME = getattr(socket, 'EAI_NONAME', -1) +cdef int socket_EAI_OVERFLOW = getattr(socket, 'EAI_OVERFLOW', -1) +cdef int socket_EAI_PROTOCOL = getattr(socket, 'EAI_PROTOCOL', -1) +cdef int socket_EAI_SERVICE = getattr(socket, 'EAI_SERVICE', -1) +cdef int socket_EAI_SOCKTYPE = getattr(socket, 'EAI_SOCKTYPE', -1) + + +cdef str os_name = os.name +cdef os_environ = os.environ +cdef os_dup = os.dup +cdef os_set_inheritable = os.set_inheritable +cdef os_get_inheritable = os.get_inheritable +cdef os_close = os.close +cdef os_open = os.open +cdef os_devnull = os.devnull +cdef os_O_RDWR = os.O_RDWR +cdef os_pipe = os.pipe +cdef os_read = os.read +cdef os_remove = os.remove +cdef os_stat = os.stat +cdef os_fspath = os.fspath + +cdef stat_S_ISSOCK = stat.S_ISSOCK + +cdef sys_ignore_environment = sys.flags.ignore_environment +cdef sys_dev_mode = sys.flags.dev_mode +cdef sys_exc_info = sys.exc_info +cdef sys_set_coroutine_wrapper = getattr(sys, 'set_coroutine_wrapper', None) +cdef sys_get_coroutine_wrapper = getattr(sys, 'get_coroutine_wrapper', None) +cdef sys_getframe = sys._getframe +cdef sys_version_info = sys.version_info +cdef sys_getfilesystemencoding = sys.getfilesystemencoding +cdef str sys_platform = sys.platform + +cdef ssl_SSLContext = ssl.SSLContext +cdef ssl_MemoryBIO = ssl.MemoryBIO +cdef ssl_create_default_context = ssl.create_default_context +cdef ssl_SSLError = ssl.SSLError +cdef ssl_SSLAgainErrors = (ssl.SSLWantReadError, ssl.SSLSyscallError) +cdef ssl_SSLZeroReturnError = ssl.SSLZeroReturnError +cdef ssl_CertificateError = ssl.CertificateError +cdef int ssl_SSL_ERROR_WANT_READ = ssl.SSL_ERROR_WANT_READ +cdef int ssl_SSL_ERROR_WANT_WRITE = ssl.SSL_ERROR_WANT_WRITE +cdef int ssl_SSL_ERROR_SYSCALL = ssl.SSL_ERROR_SYSCALL + +cdef threading_Thread = threading.Thread +cdef threading_main_thread = threading.main_thread + +cdef int subprocess_PIPE = subprocess.PIPE +cdef int subprocess_STDOUT = subprocess.STDOUT +cdef int subprocess_DEVNULL = subprocess.DEVNULL +cdef subprocess_SubprocessError = subprocess.SubprocessError + +cdef int signal_NSIG = signal.NSIG +cdef signal_signal = signal.signal +cdef signal_siginterrupt = signal.siginterrupt +cdef signal_set_wakeup_fd = signal.set_wakeup_fd +cdef signal_default_int_handler = signal.default_int_handler +cdef signal_SIG_DFL = signal.SIG_DFL + +cdef time_sleep = time.sleep +cdef time_monotonic = time.monotonic + +cdef tb_StackSummary = traceback.StackSummary +cdef tb_walk_stack = traceback.walk_stack +cdef tb_format_list = traceback.format_list + +cdef warnings_warn = warnings.warn + +cdef weakref_WeakValueDictionary = weakref.WeakValueDictionary +cdef weakref_WeakSet = weakref.WeakSet + +cdef py_inf = float('inf') + + +# Cython doesn't clean-up imported objects properly in Py3 mode, +# so we delete refs to all modules manually (except sys) +del asyncio, concurrent, collections, errno +del functools, inspect, itertools, socket, os, threading +del signal, subprocess, ssl +del time, traceback, warnings, weakref diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/system.pxd b/venv/lib/python3.11/site-packages/uvloop/includes/system.pxd new file mode 100644 index 0000000..367fedd --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/system.pxd @@ -0,0 +1,96 @@ +from libc.stdint cimport int8_t, uint64_t + +cdef extern from "arpa/inet.h" nogil: + + int ntohl(int) + int htonl(int) + int ntohs(int) + + +cdef extern from "sys/socket.h" nogil: + + struct sockaddr: + unsigned short sa_family + char sa_data[14] + + struct addrinfo: + int ai_flags + int ai_family + int ai_socktype + int ai_protocol + size_t ai_addrlen + sockaddr* ai_addr + char* ai_canonname + addrinfo* ai_next + + struct sockaddr_in: + unsigned short sin_family + unsigned short sin_port + # ... + + struct sockaddr_in6: + unsigned short sin6_family + unsigned short sin6_port + unsigned long sin6_flowinfo + # ... + unsigned long sin6_scope_id + + struct sockaddr_storage: + unsigned short ss_family + # ... + + const char *gai_strerror(int errcode) + + int socketpair(int domain, int type, int protocol, int socket_vector[2]) + + int setsockopt(int socket, int level, int option_name, + const void *option_value, int option_len) + + +cdef extern from "sys/un.h" nogil: + + struct sockaddr_un: + unsigned short sun_family + char* sun_path + # ... + + +cdef extern from "unistd.h" nogil: + + ssize_t write(int fd, const void *buf, size_t count) + void _exit(int status) + + +cdef extern from "pthread.h": + + int pthread_atfork( + void (*prepare)(), + void (*parent)(), + void (*child)()) + + +cdef extern from "includes/compat.h" nogil: + + cdef int EWOULDBLOCK + + cdef int PLATFORM_IS_APPLE + cdef int PLATFORM_IS_LINUX + + struct epoll_event: + # We don't use the fields + pass + + int EPOLL_CTL_DEL + int epoll_ctl(int epfd, int op, int fd, epoll_event *event) + object MakeUnixSockPyAddr(sockaddr_un *addr) + + +cdef extern from "includes/fork_handler.h": + + uint64_t MAIN_THREAD_ID + int8_t MAIN_THREAD_ID_SET + ctypedef void (*OnForkHandler)() + void handleAtFork() + void setForkHandler(OnForkHandler handler) + void resetForkHandler() + void setMainThreadID(uint64_t id) diff --git a/venv/lib/python3.11/site-packages/uvloop/includes/uv.pxd b/venv/lib/python3.11/site-packages/uvloop/includes/uv.pxd new file mode 100644 index 0000000..8765130 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/includes/uv.pxd @@ -0,0 +1,506 @@ +from libc.stdint cimport uint16_t, uint32_t, uint64_t, int64_t +from posix.types cimport gid_t, uid_t +from posix.unistd cimport getuid + +from . cimport system + +# This is an internal enum UV_HANDLE_READABLE from uv-common.h, used only by +# handles/pipe.pyx to temporarily workaround a libuv issue libuv/libuv#2058, +# before there is a proper fix in libuv. In short, libuv disallowed feeding a +# write-only pipe to uv_read_start(), which was needed by uvloop to detect a +# broken pipe without having to send anything on the write-only end. We're +# setting UV_HANDLE_READABLE on pipe_t to workaround this limitation +# temporarily, please see also #317. +cdef enum: + UV_INTERNAL_HANDLE_READABLE = 0x00004000 + +cdef extern from "uv.h" nogil: + cdef int UV_TCP_IPV6ONLY + + cdef int UV_EACCES + cdef int UV_EAGAIN + cdef int UV_EALREADY + cdef int UV_EBUSY + cdef int UV_ECONNABORTED + cdef int UV_ECONNREFUSED + cdef int UV_ECONNRESET + cdef int UV_ECANCELED + cdef int UV_EEXIST + cdef int UV_EINTR + cdef int UV_EINVAL + cdef int UV_EISDIR + cdef int UV_ENOENT + cdef int UV_EOF + cdef int UV_EPERM + cdef int UV_EPIPE + cdef int UV_ESHUTDOWN + cdef int UV_ESRCH + cdef int UV_ETIMEDOUT + cdef int UV_EBADF + cdef int UV_ENOBUFS + + cdef int UV_EAI_ADDRFAMILY + cdef int UV_EAI_AGAIN + cdef int UV_EAI_BADFLAGS + cdef int UV_EAI_BADHINTS + cdef int UV_EAI_CANCELED + cdef int UV_EAI_FAIL + cdef int UV_EAI_FAMILY + cdef int UV_EAI_MEMORY + cdef int UV_EAI_NODATA + cdef int UV_EAI_NONAME + cdef int UV_EAI_OVERFLOW + cdef int UV_EAI_PROTOCOL + cdef int UV_EAI_SERVICE + cdef int UV_EAI_SOCKTYPE + + cdef int SOL_SOCKET + cdef int SO_ERROR + cdef int SO_REUSEADDR + cdef int SO_REUSEPORT + cdef int AF_INET + cdef int AF_INET6 + cdef int AF_UNIX + cdef int AF_UNSPEC + cdef int AI_PASSIVE + cdef int AI_NUMERICHOST + cdef int INET6_ADDRSTRLEN + cdef int IPPROTO_IPV6 + cdef int SOCK_STREAM + cdef int SOCK_DGRAM + cdef int IPPROTO_TCP + cdef int IPPROTO_UDP + + cdef int SIGINT + cdef int SIGHUP + cdef int SIGCHLD + cdef int SIGKILL + cdef int SIGTERM + + ctypedef int uv_os_sock_t + ctypedef int uv_file + ctypedef int uv_os_fd_t + + ctypedef struct uv_buf_t: + char* base + size_t len + + ctypedef struct uv_loop_t: + void* data + # ... + + ctypedef struct uv_handle_t: + void* data + uv_loop_t* loop + unsigned int flags + # ... + + ctypedef struct uv_idle_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_check_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_signal_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_async_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_timer_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_stream_t: + void* data + size_t write_queue_size + uv_loop_t* loop + # ... + + ctypedef struct uv_tcp_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_pipe_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_udp_t: + void* data + uv_loop_t* loop + size_t send_queue_size + size_t send_queue_count + # ... + + ctypedef struct uv_udp_send_t: + void* data + uv_udp_t* handle + + ctypedef struct uv_poll_t: + void* data + uv_loop_t* loop + # ... + + ctypedef struct uv_req_t: + # Only cancellation of uv_fs_t, uv_getaddrinfo_t, + # uv_getnameinfo_t and uv_work_t requests is + # currently supported. + void* data + uv_req_type type + # ... + + ctypedef struct uv_connect_t: + void* data + + ctypedef struct uv_getaddrinfo_t: + void* data + # ... + + ctypedef struct uv_getnameinfo_t: + void* data + # ... + + ctypedef struct uv_write_t: + void* data + # ... + + ctypedef struct uv_shutdown_t: + void* data + # ... + + ctypedef struct uv_process_t: + void* data + int pid + # ... + + ctypedef struct uv_fs_event_t: + void* data + # ... + + ctypedef enum uv_req_type: + UV_UNKNOWN_REQ = 0, + UV_REQ, + UV_CONNECT, + UV_WRITE, + UV_SHUTDOWN, + UV_UDP_SEND, + UV_FS, + UV_WORK, + UV_GETADDRINFO, + UV_GETNAMEINFO, + UV_REQ_TYPE_PRIVATE, + UV_REQ_TYPE_MAX + + ctypedef enum uv_run_mode: + UV_RUN_DEFAULT = 0, + UV_RUN_ONCE, + UV_RUN_NOWAIT + + ctypedef enum uv_poll_event: + UV_READABLE = 1, + UV_WRITABLE = 2, + UV_DISCONNECT = 4 + + ctypedef enum uv_udp_flags: + UV_UDP_IPV6ONLY = 1, + UV_UDP_PARTIAL = 2 + + ctypedef enum uv_membership: + UV_LEAVE_GROUP = 0, + UV_JOIN_GROUP + + cpdef enum uv_fs_event: + UV_RENAME = 1, + UV_CHANGE = 2 + + const char* uv_strerror(int err) + const char* uv_err_name(int err) + + ctypedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg) with gil + + ctypedef void (*uv_close_cb)(uv_handle_t* handle) with gil + ctypedef void (*uv_idle_cb)(uv_idle_t* handle) with gil + ctypedef void (*uv_check_cb)(uv_check_t* handle) with gil + ctypedef void (*uv_signal_cb)(uv_signal_t* handle, int signum) with gil + ctypedef void (*uv_async_cb)(uv_async_t* handle) with gil + ctypedef void (*uv_timer_cb)(uv_timer_t* handle) with gil + ctypedef void (*uv_connection_cb)(uv_stream_t* server, int status) with gil + ctypedef void (*uv_alloc_cb)(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) with gil + ctypedef void (*uv_read_cb)(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) with gil + ctypedef void (*uv_write_cb)(uv_write_t* req, int status) with gil + ctypedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, + int status, + system.addrinfo* res) with gil + ctypedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, + int status, + const char* hostname, + const char* service) with gil + ctypedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status) with gil + ctypedef void (*uv_poll_cb)(uv_poll_t* handle, + int status, int events) with gil + + ctypedef void (*uv_connect_cb)(uv_connect_t* req, int status) with gil + + ctypedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status) with gil + ctypedef void (*uv_udp_recv_cb)(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const system.sockaddr* addr, + unsigned flags) with gil + ctypedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, + const char *filename, + int events, + int status) with gil + + # Generic request functions + int uv_cancel(uv_req_t* req) + + # Generic handler functions + int uv_is_active(const uv_handle_t* handle) + void uv_close(uv_handle_t* handle, uv_close_cb close_cb) + int uv_is_closing(const uv_handle_t* handle) + int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) + void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) + + # Loop functions + int uv_loop_init(uv_loop_t* loop) + int uv_loop_close(uv_loop_t* loop) + int uv_loop_alive(uv_loop_t* loop) + int uv_loop_fork(uv_loop_t* loop) + int uv_backend_fd(uv_loop_t* loop) + + void uv_update_time(uv_loop_t* loop) + uint64_t uv_now(const uv_loop_t*) + + int uv_run(uv_loop_t*, uv_run_mode mode) nogil + void uv_stop(uv_loop_t*) + + # Idle handler + int uv_idle_init(uv_loop_t*, uv_idle_t* idle) + int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) + int uv_idle_stop(uv_idle_t* idle) + + # Check handler + int uv_check_init(uv_loop_t*, uv_check_t* idle) + int uv_check_start(uv_check_t* check, uv_check_cb cb) + int uv_check_stop(uv_check_t* check) + + # Signal handler + int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) + int uv_signal_start(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum) + int uv_signal_stop(uv_signal_t* handle) + + # Async handler + int uv_async_init(uv_loop_t*, + uv_async_t* async_, + uv_async_cb async_cb) + int uv_async_send(uv_async_t* async_) + + # Timer handler + int uv_timer_init(uv_loop_t*, uv_timer_t* handle) + int uv_timer_start(uv_timer_t* handle, + uv_timer_cb cb, + uint64_t timeout, + uint64_t repeat) + int uv_timer_stop(uv_timer_t* handle) + + # DNS + int uv_getaddrinfo(uv_loop_t* loop, + uv_getaddrinfo_t* req, + uv_getaddrinfo_cb getaddrinfo_cb, + const char* node, + const char* service, + const system.addrinfo* hints) + + void uv_freeaddrinfo(system.addrinfo* ai) + + int uv_getnameinfo(uv_loop_t* loop, + uv_getnameinfo_t* req, + uv_getnameinfo_cb getnameinfo_cb, + const system.sockaddr* addr, + int flags) + + int uv_ip4_name(const system.sockaddr_in* src, char* dst, size_t size) + int uv_ip6_name(const system.sockaddr_in6* src, char* dst, size_t size) + + # Streams + + int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) + int uv_accept(uv_stream_t* server, uv_stream_t* client) + int uv_read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) + int uv_read_stop(uv_stream_t*) + int uv_write(uv_write_t* req, uv_stream_t* handle, + uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) + + int uv_try_write(uv_stream_t* handle, uv_buf_t bufs[], unsigned int nbufs) + + int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) + + int uv_is_readable(const uv_stream_t* handle) + int uv_is_writable(const uv_stream_t* handle) + + # TCP + + int uv_tcp_init_ex(uv_loop_t*, uv_tcp_t* handle, unsigned int flags) + int uv_tcp_nodelay(uv_tcp_t* handle, int enable) + int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) + int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) + int uv_tcp_bind(uv_tcp_t* handle, system.sockaddr* addr, + unsigned int flags) + + int uv_tcp_getsockname(const uv_tcp_t* handle, system.sockaddr* name, + int* namelen) + int uv_tcp_getpeername(const uv_tcp_t* handle, system.sockaddr* name, + int* namelen) + + int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + const system.sockaddr* addr, uv_connect_cb cb) + + # Pipes + + int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) + int uv_pipe_open(uv_pipe_t* handle, uv_file file) + int uv_pipe_bind(uv_pipe_t* handle, const char* name) + + void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, + const char* name, uv_connect_cb cb) + + # UDP + + int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) + int uv_udp_connect(uv_udp_t* handle, const system.sockaddr* addr) + int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) + int uv_udp_bind(uv_udp_t* handle, const system.sockaddr* addr, + unsigned int flags) + int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, + const uv_buf_t bufs[], unsigned int nbufs, + const system.sockaddr* addr, uv_udp_send_cb send_cb) + int uv_udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], unsigned int nbufs, + const system.sockaddr* addr) + int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb) + int uv_udp_recv_stop(uv_udp_t* handle) + int uv_udp_set_broadcast(uv_udp_t* handle, int on) + + # Polling + + int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) + int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, + uv_os_sock_t socket) + int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) + int uv_poll_stop(uv_poll_t* poll) + + # FS Event + + int uv_fs_event_init(uv_loop_t *loop, uv_fs_event_t *handle) + int uv_fs_event_start(uv_fs_event_t *handle, uv_fs_event_cb cb, + const char *path, unsigned int flags) + int uv_fs_event_stop(uv_fs_event_t *handle) + + # Misc + + ctypedef struct uv_timeval_t: + long tv_sec + long tv_usec + + ctypedef struct uv_rusage_t: + uv_timeval_t ru_utime # user CPU time used + uv_timeval_t ru_stime # system CPU time used + uint64_t ru_maxrss # maximum resident set size + uint64_t ru_ixrss # integral shared memory size + uint64_t ru_idrss # integral unshared data size + uint64_t ru_isrss # integral unshared stack size + uint64_t ru_minflt # page reclaims (soft page faults) + uint64_t ru_majflt # page faults (hard page faults) + uint64_t ru_nswap # swaps + uint64_t ru_inblock # block input operations + uint64_t ru_oublock # block output operations + uint64_t ru_msgsnd # IPC messages sent + uint64_t ru_msgrcv # IPC messages received + uint64_t ru_nsignals # signals received + uint64_t ru_nvcsw # voluntary context switches + uint64_t ru_nivcsw # involuntary context switches + + int uv_getrusage(uv_rusage_t* rusage) + + int uv_ip4_addr(const char* ip, int port, system.sockaddr_in* addr) + int uv_ip6_addr(const char* ip, int port, system.sockaddr_in6* addr) + + # Memory Allocation + + ctypedef void* (*uv_malloc_func)(size_t size) + ctypedef void* (*uv_realloc_func)(void* ptr, size_t size) + ctypedef void* (*uv_calloc_func)(size_t count, size_t size) + ctypedef void (*uv_free_func)(void* ptr) + + int uv_replace_allocator(uv_malloc_func malloc_func, + uv_realloc_func realloc_func, + uv_calloc_func calloc_func, + uv_free_func free_func) + + # Process + + ctypedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, + int term_signal) with gil + + ctypedef enum uv_process_flags: + UV_PROCESS_SETUID = 1, + UV_PROCESS_SETGID = 2, + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = 4, + UV_PROCESS_DETACHED = 8, + UV_PROCESS_WINDOWS_HIDE = 16 + + ctypedef enum uv_stdio_flags: + UV_IGNORE = 0x00, + UV_CREATE_PIPE = 0x01, + UV_INHERIT_FD = 0x02, + UV_INHERIT_STREAM = 0x04, + UV_READABLE_PIPE = 0x10, + UV_WRITABLE_PIPE = 0x20 + + ctypedef union uv_stdio_container_data_u: + uv_stream_t* stream + int fd + + ctypedef struct uv_stdio_container_t: + uv_stdio_flags flags + uv_stdio_container_data_u data + + ctypedef struct uv_process_options_t: + uv_exit_cb exit_cb + char* file + char** args + char** env + char* cwd + unsigned int flags + int stdio_count + uv_stdio_container_t* stdio + uid_t uid + gid_t gid + + int uv_spawn(uv_loop_t* loop, uv_process_t* handle, + const uv_process_options_t* options) + + int uv_process_kill(uv_process_t* handle, int signum) + + unsigned int uv_version() diff --git a/venv/lib/python3.11/site-packages/uvloop/loop.cpython-311-x86_64-linux-gnu.so b/venv/lib/python3.11/site-packages/uvloop/loop.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000..58ae5dc Binary files /dev/null and b/venv/lib/python3.11/site-packages/uvloop/loop.cpython-311-x86_64-linux-gnu.so differ diff --git a/venv/lib/python3.11/site-packages/uvloop/loop.pxd b/venv/lib/python3.11/site-packages/uvloop/loop.pxd new file mode 100644 index 0000000..5613473 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/loop.pxd @@ -0,0 +1,229 @@ +# cython: language_level=3 + + +from .includes cimport uv +from .includes cimport system + +from libc.stdint cimport uint64_t, uint32_t, int64_t + + +include "includes/consts.pxi" + + +cdef extern from *: + ctypedef int vint "volatile int" + + +cdef class UVHandle +cdef class UVSocketHandle(UVHandle) + +cdef class UVAsync(UVHandle) +cdef class UVTimer(UVHandle) +cdef class UVIdle(UVHandle) + +cdef class UVBaseTransport(UVSocketHandle) + +ctypedef object (*method_t)(object) +ctypedef object (*method1_t)(object, object) +ctypedef object (*method2_t)(object, object, object) +ctypedef object (*method3_t)(object, object, object, object) + + +cdef class Loop: + cdef: + uv.uv_loop_t *uvloop + + bint _coroutine_debug_set + int _coroutine_origin_tracking_saved_depth + + public slow_callback_duration + + readonly bint _closed + bint _debug + bint _running + bint _stopping + + uint64_t _thread_id + + object _task_factory + object _exception_handler + object _default_executor + object _ready + set _queued_streams, _executing_streams + Py_ssize_t _ready_len + + set _servers + + object _transports + set _processes + dict _fd_to_reader_fileobj + dict _fd_to_writer_fileobj + + set _signals + dict _signal_handlers + object _ssock + object _csock + bint _listening_signals + int _old_signal_wakeup_id + + set _timers + dict _polls + + UVProcess active_process_handler + + UVAsync handler_async + UVIdle handler_idle + UVCheck handler_check__exec_writes + + object _last_error + + cdef object __weakref__ + + object _asyncgens + bint _asyncgens_shutdown_called + + bint _executor_shutdown_called + + char _recv_buffer[UV_STREAM_RECV_BUF_SIZE] + bint _recv_buffer_in_use + + # DEBUG fields + # True when compiled with DEBUG. + # Used only in unittests. + readonly bint _debug_cc + + readonly object _debug_handles_total + readonly object _debug_handles_closed + readonly object _debug_handles_current + + readonly uint64_t _debug_uv_handles_total + readonly uint64_t _debug_uv_handles_freed + + readonly uint64_t _debug_cb_handles_total + readonly uint64_t _debug_cb_handles_count + readonly uint64_t _debug_cb_timer_handles_total + readonly uint64_t _debug_cb_timer_handles_count + + readonly uint64_t _debug_stream_shutdown_errors_total + readonly uint64_t _debug_stream_listen_errors_total + + readonly uint64_t _debug_stream_read_cb_total + readonly uint64_t _debug_stream_read_cb_errors_total + readonly uint64_t _debug_stream_read_eof_total + readonly uint64_t _debug_stream_read_eof_cb_errors_total + readonly uint64_t _debug_stream_read_errors_total + + readonly uint64_t _debug_stream_write_tries + readonly uint64_t _debug_stream_write_errors_total + readonly uint64_t _debug_stream_write_ctx_total + readonly uint64_t _debug_stream_write_ctx_cnt + readonly uint64_t _debug_stream_write_cb_errors_total + + readonly uint64_t _poll_read_events_total + readonly uint64_t _poll_read_cb_errors_total + readonly uint64_t _poll_write_events_total + readonly uint64_t _poll_write_cb_errors_total + + readonly uint64_t _sock_try_write_total + + readonly uint64_t _debug_exception_handler_cnt + + cdef _init_debug_fields(self) + + cdef _on_wake(self) + cdef _on_idle(self) + + cdef __run(self, uv.uv_run_mode) + cdef _run(self, uv.uv_run_mode) + + cdef _close(self) + cdef _stop(self, exc) + cdef uint64_t _time(self) + + cdef inline _queue_write(self, UVStream stream) + cdef _exec_queued_writes(self) + + cdef inline _call_soon(self, object callback, object args, object context) + cdef inline _append_ready_handle(self, Handle handle) + cdef inline _call_soon_handle(self, Handle handle) + + cdef _call_later(self, uint64_t delay, object callback, object args, + object context) + + cdef void _handle_exception(self, object ex) + + cdef inline _is_main_thread(self) + + cdef inline _new_future(self) + cdef inline _check_signal(self, sig) + cdef inline _check_closed(self) + cdef inline _check_thread(self) + + cdef _getaddrinfo(self, object host, object port, + int family, int type, + int proto, int flags, + int unpack) + + cdef _getnameinfo(self, system.sockaddr *addr, int flags) + + cdef _track_transport(self, UVBaseTransport transport) + cdef _fileobj_to_fd(self, fileobj) + cdef _ensure_fd_no_transport(self, fd) + + cdef _track_process(self, UVProcess proc) + cdef _untrack_process(self, UVProcess proc) + + cdef _add_reader(self, fd, Handle handle) + cdef _has_reader(self, fd) + cdef _remove_reader(self, fd) + + cdef _add_writer(self, fd, Handle handle) + cdef _has_writer(self, fd) + cdef _remove_writer(self, fd) + + cdef _sock_recv(self, fut, sock, n) + cdef _sock_recv_into(self, fut, sock, buf) + cdef _sock_sendall(self, fut, sock, data) + cdef _sock_accept(self, fut, sock) + + cdef _sock_connect(self, sock, address) + cdef _sock_connect_cb(self, fut, sock, address) + + cdef _sock_set_reuseport(self, int fd) + + cdef _setup_or_resume_signals(self) + cdef _shutdown_signals(self) + cdef _pause_signals(self) + + cdef _handle_signal(self, sig) + cdef _read_from_self(self) + cdef inline _ceval_process_signals(self) + cdef _invoke_signals(self, bytes data) + + cdef _set_coroutine_debug(self, bint enabled) + + cdef _print_debug_info(self) + + +include "cbhandles.pxd" + +include "handles/handle.pxd" +include "handles/async_.pxd" +include "handles/idle.pxd" +include "handles/check.pxd" +include "handles/timer.pxd" +include "handles/poll.pxd" +include "handles/basetransport.pxd" +include "handles/stream.pxd" +include "handles/streamserver.pxd" +include "handles/tcp.pxd" +include "handles/pipe.pxd" +include "handles/process.pxd" +include "handles/fsevent.pxd" + +include "request.pxd" +include "sslproto.pxd" + +include "handles/udp.pxd" + +include "server.pxd" diff --git a/venv/lib/python3.11/site-packages/uvloop/loop.pyi b/venv/lib/python3.11/site-packages/uvloop/loop.pyi new file mode 100644 index 0000000..9c8c462 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/loop.pyi @@ -0,0 +1,297 @@ +import asyncio +import ssl +import sys +from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket +from typing import ( + IO, + Any, + Awaitable, + Callable, + Dict, + Generator, + List, + Optional, + Sequence, + Tuple, + TypeVar, + Union, + overload, +) + +_T = TypeVar('_T') +_Context = Dict[str, Any] +_ExceptionHandler = Callable[[asyncio.AbstractEventLoop, _Context], Any] +_SSLContext = Union[bool, None, ssl.SSLContext] +_ProtocolT = TypeVar("_ProtocolT", bound=asyncio.BaseProtocol) + +class Loop: + def call_soon( + self, callback: Callable[..., Any], *args: Any, context: Optional[Any] = ... + ) -> asyncio.Handle: ... + def call_soon_threadsafe( + self, callback: Callable[..., Any], *args: Any, context: Optional[Any] = ... + ) -> asyncio.Handle: ... + def call_later( + self, delay: float, callback: Callable[..., Any], *args: Any, context: Optional[Any] = ... + ) -> asyncio.TimerHandle: ... + def call_at( + self, when: float, callback: Callable[..., Any], *args: Any, context: Optional[Any] = ... + ) -> asyncio.TimerHandle: ... + def time(self) -> float: ... + def stop(self) -> None: ... + def run_forever(self) -> None: ... + def close(self) -> None: ... + def get_debug(self) -> bool: ... + def set_debug(self, enabled: bool) -> None: ... + def is_running(self) -> bool: ... + def is_closed(self) -> bool: ... + def create_future(self) -> asyncio.Future[Any]: ... + def create_task( + self, + coro: Union[Awaitable[_T], Generator[Any, None, _T]], + *, + name: Optional[str] = ..., + ) -> asyncio.Task[_T]: ... + def set_task_factory( + self, + factory: Optional[ + Callable[[asyncio.AbstractEventLoop, Generator[Any, None, _T]], asyncio.Future[_T]] + ], + ) -> None: ... + def get_task_factory( + self, + ) -> Optional[ + Callable[[asyncio.AbstractEventLoop, Generator[Any, None, _T]], asyncio.Future[_T]] + ]: ... + @overload + def run_until_complete(self, future: Generator[Any, None, _T]) -> _T: ... + @overload + def run_until_complete(self, future: Awaitable[_T]) -> _T: ... + async def getaddrinfo( + self, + host: Optional[Union[str, bytes]], + port: Optional[Union[str, bytes, int]], + *, + family: int = ..., + type: int = ..., + proto: int = ..., + flags: int = ..., + ) -> List[ + Tuple[ + AddressFamily, + SocketKind, + int, + str, + Union[Tuple[str, int], Tuple[str, int, int, int]], + ] + ]: ... + async def getnameinfo( + self, + sockaddr: Union[ + Tuple[str, int], + Tuple[str, int, int], + Tuple[str, int, int, int] + ], + flags: int = ..., + ) -> Tuple[str, str]: ... + async def start_tls( + self, + transport: asyncio.BaseTransport, + protocol: asyncio.BaseProtocol, + sslcontext: ssl.SSLContext, + *, + server_side: bool = ..., + server_hostname: Optional[str] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + ) -> asyncio.BaseTransport: ... + @overload + async def create_server( + self, + protocol_factory: asyncio.events._ProtocolFactory, + host: Optional[Union[str, Sequence[str]]] = ..., + port: int = ..., + *, + family: int = ..., + flags: int = ..., + sock: None = ..., + backlog: int = ..., + ssl: _SSLContext = ..., + reuse_address: Optional[bool] = ..., + reuse_port: Optional[bool] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + start_serving: bool = ..., + ) -> asyncio.AbstractServer: ... + @overload + async def create_server( + self, + protocol_factory: asyncio.events._ProtocolFactory, + host: None = ..., + port: None = ..., + *, + family: int = ..., + flags: int = ..., + sock: socket = ..., + backlog: int = ..., + ssl: _SSLContext = ..., + reuse_address: Optional[bool] = ..., + reuse_port: Optional[bool] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + start_serving: bool = ..., + ) -> asyncio.AbstractServer: ... + @overload + async def create_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + host: str = ..., + port: int = ..., + *, + ssl: _SSLContext = ..., + family: int = ..., + proto: int = ..., + flags: int = ..., + sock: None = ..., + local_addr: Optional[Tuple[str, int]] = ..., + server_hostname: Optional[str] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + @overload + async def create_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + host: None = ..., + port: None = ..., + *, + ssl: _SSLContext = ..., + family: int = ..., + proto: int = ..., + flags: int = ..., + sock: socket, + local_addr: None = ..., + server_hostname: Optional[str] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def create_unix_server( + self, + protocol_factory: asyncio.events._ProtocolFactory, + path: Optional[str] = ..., + *, + backlog: int = ..., + sock: Optional[socket] = ..., + ssl: _SSLContext = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + start_serving: bool = ..., + ) -> asyncio.AbstractServer: ... + async def create_unix_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + path: Optional[str] = ..., + *, + ssl: _SSLContext = ..., + sock: Optional[socket] = ..., + server_hostname: Optional[str] = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + def default_exception_handler(self, context: _Context) -> None: ... + def get_exception_handler(self) -> Optional[_ExceptionHandler]: ... + def set_exception_handler(self, handler: Optional[_ExceptionHandler]) -> None: ... + def call_exception_handler(self, context: _Context) -> None: ... + def add_reader(self, fd: Any, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_reader(self, fd: Any) -> None: ... + def add_writer(self, fd: Any, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_writer(self, fd: Any) -> None: ... + async def sock_recv(self, sock: socket, nbytes: int) -> bytes: ... + async def sock_recv_into(self, sock: socket, buf: bytearray) -> int: ... + async def sock_sendall(self, sock: socket, data: bytes) -> None: ... + async def sock_accept(self, sock: socket) -> Tuple[socket, _RetAddress]: ... + async def sock_connect(self, sock: socket, address: _Address) -> None: ... + async def sock_recvfrom(self, sock: socket, bufsize: int) -> bytes: ... + async def sock_recvfrom_into(self, sock: socket, buf: bytearray, nbytes: int = ...) -> int: ... + async def sock_sendto(self, sock: socket, data: bytes, address: _Address) -> None: ... + async def connect_accepted_socket( + self, + protocol_factory: Callable[[], _ProtocolT], + sock: socket, + *, + ssl: _SSLContext = ..., + ssl_handshake_timeout: Optional[float] = ..., + ssl_shutdown_timeout: Optional[float] = ..., + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def run_in_executor( + self, executor: Any, func: Callable[..., _T], *args: Any + ) -> _T: ... + def set_default_executor(self, executor: Any) -> None: ... + async def subprocess_shell( + self, + protocol_factory: Callable[[], _ProtocolT], + cmd: Union[bytes, str], + *, + stdin: Any = ..., + stdout: Any = ..., + stderr: Any = ..., + **kwargs: Any, + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def subprocess_exec( + self, + protocol_factory: Callable[[], _ProtocolT], + *args: Any, + stdin: Any = ..., + stdout: Any = ..., + stderr: Any = ..., + **kwargs: Any, + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def connect_read_pipe( + self, protocol_factory: Callable[[], _ProtocolT], pipe: Any + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def connect_write_pipe( + self, protocol_factory: Callable[[], _ProtocolT], pipe: Any + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + def add_signal_handler( + self, sig: int, callback: Callable[..., Any], *args: Any + ) -> None: ... + def remove_signal_handler(self, sig: int) -> bool: ... + async def create_datagram_endpoint( + self, + protocol_factory: Callable[[], _ProtocolT], + local_addr: Optional[Tuple[str, int]] = ..., + remote_addr: Optional[Tuple[str, int]] = ..., + *, + family: int = ..., + proto: int = ..., + flags: int = ..., + reuse_address: Optional[bool] = ..., + reuse_port: Optional[bool] = ..., + allow_broadcast: Optional[bool] = ..., + sock: Optional[socket] = ..., + ) -> tuple[asyncio.BaseProtocol, _ProtocolT]: ... + async def shutdown_asyncgens(self) -> None: ... + async def shutdown_default_executor( + self, + timeout: Optional[float] = ..., + ) -> None: ... + # Loop doesn't implement these, but since they are marked as abstract in typeshed, + # we have to put them in so mypy thinks the base methods are overridden + async def sendfile( + self, + transport: asyncio.BaseTransport, + file: IO[bytes], + offset: int = ..., + count: Optional[int] = ..., + *, + fallback: bool = ..., + ) -> int: ... + async def sock_sendfile( + self, + sock: socket, + file: IO[bytes], + offset: int = ..., + count: Optional[int] = ..., + *, + fallback: bool = ... + ) -> int: ... diff --git a/venv/lib/python3.11/site-packages/uvloop/loop.pyx b/venv/lib/python3.11/site-packages/uvloop/loop.pyx new file mode 100644 index 0000000..334d8d5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/loop.pyx @@ -0,0 +1,3403 @@ +# cython: language_level=3, embedsignature=True + +import asyncio +cimport cython + +from .includes.debug cimport UVLOOP_DEBUG +from .includes cimport uv +from .includes cimport system +from .includes.python cimport ( + PY_VERSION_HEX, + PyMem_RawMalloc, PyMem_RawFree, + PyMem_RawCalloc, PyMem_RawRealloc, + PyUnicode_EncodeFSDefault, + PyErr_SetInterrupt, + _Py_RestoreSignals, + Context_CopyCurrent, + Context_Enter, + Context_Exit, + PyMemoryView_FromMemory, PyBUF_WRITE, + PyMemoryView_FromObject, PyMemoryView_Check, + PyOS_AfterFork_Parent, PyOS_AfterFork_Child, + PyOS_BeforeFork, + PyUnicode_FromString +) +from .includes.flowcontrol cimport add_flowcontrol_defaults + +from libc.stdint cimport uint64_t +from libc.string cimport memset, strerror, memcpy +from libc cimport errno + +from cpython cimport PyObject +from cpython cimport PyErr_CheckSignals, PyErr_Occurred +from cpython cimport PyThread_get_thread_ident +from cpython cimport Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF +from cpython cimport ( + PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE, + Py_buffer, PyBytes_AsString, PyBytes_CheckExact, + PyBytes_AsStringAndSize, + Py_SIZE, PyBytes_AS_STRING, PyBUF_WRITABLE +) +from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer + +from . import _noop + + +include "includes/consts.pxi" +include "includes/stdlib.pxi" + +include "errors.pyx" + +cdef: + int PY39 = PY_VERSION_HEX >= 0x03090000 + int PY311 = PY_VERSION_HEX >= 0x030b0000 + uint64_t MAX_SLEEP = 3600 * 24 * 365 * 100 + + +cdef _is_sock_stream(sock_type): + if SOCK_NONBLOCK == -1: + return sock_type == uv.SOCK_STREAM + else: + # Linux's socket.type is a bitmask that can include extra info + # about socket (like SOCK_NONBLOCK bit), therefore we can't do simple + # `sock_type == socket.SOCK_STREAM`, see + # https://github.com/torvalds/linux/blob/v4.13/include/linux/net.h#L77 + # for more details. + return (sock_type & 0xF) == uv.SOCK_STREAM + + +cdef _is_sock_dgram(sock_type): + if SOCK_NONBLOCK == -1: + return sock_type == uv.SOCK_DGRAM + else: + # Read the comment in `_is_sock_stream`. + return (sock_type & 0xF) == uv.SOCK_DGRAM + + +cdef isfuture(obj): + if aio_isfuture is None: + return isinstance(obj, aio_Future) + else: + return aio_isfuture(obj) + + +cdef inline socket_inc_io_ref(sock): + if isinstance(sock, socket_socket): + sock._io_refs += 1 + + +cdef inline socket_dec_io_ref(sock): + if isinstance(sock, socket_socket): + sock._decref_socketios() + + +cdef inline run_in_context(context, method): + # This method is internally used to workaround a reference issue that in + # certain circumstances, inlined context.run() will not hold a reference to + # the given method instance, which - if deallocated - will cause segfault. + # See also: edgedb/edgedb#2222 + Py_INCREF(method) + try: + return context.run(method) + finally: + Py_DECREF(method) + + +cdef inline run_in_context1(context, method, arg): + Py_INCREF(method) + try: + return context.run(method, arg) + finally: + Py_DECREF(method) + + +cdef inline run_in_context2(context, method, arg1, arg2): + Py_INCREF(method) + try: + return context.run(method, arg1, arg2) + finally: + Py_DECREF(method) + + +# Used for deprecation and removal of `loop.create_datagram_endpoint()`'s +# *reuse_address* parameter +_unset = object() + + +@cython.no_gc_clear +cdef class Loop: + def __cinit__(self): + cdef int err + + # Install PyMem* memory allocators if they aren't installed yet. + __install_pymem() + + # Install pthread_atfork handlers + __install_atfork() + + self.uvloop = PyMem_RawMalloc(sizeof(uv.uv_loop_t)) + if self.uvloop is NULL: + raise MemoryError() + + self.slow_callback_duration = 0.1 + + self._closed = 0 + self._debug = 0 + self._thread_id = 0 + self._running = 0 + self._stopping = 0 + + self._transports = weakref_WeakValueDictionary() + self._processes = set() + + # Used to keep a reference (and hence keep the fileobj alive) + # for as long as its registered by add_reader or add_writer. + # This is how the selector module and hence asyncio behaves. + self._fd_to_reader_fileobj = {} + self._fd_to_writer_fileobj = {} + + self._timers = set() + self._polls = {} + + self._recv_buffer_in_use = 0 + + err = uv.uv_loop_init(self.uvloop) + if err < 0: + raise convert_error(err) + self.uvloop.data = self + + self._init_debug_fields() + + self.active_process_handler = None + + self._last_error = None + + self._task_factory = None + self._exception_handler = None + self._default_executor = None + + self._queued_streams = set() + self._executing_streams = set() + self._ready = col_deque() + self._ready_len = 0 + + self.handler_async = UVAsync.new( + self, self._on_wake, self) + + self.handler_idle = UVIdle.new( + self, + new_MethodHandle( + self, "loop._on_idle", self._on_idle, None, self)) + + # Needed to call `UVStream._exec_write` for writes scheduled + # during `Protocol.data_received`. + self.handler_check__exec_writes = UVCheck.new( + self, + new_MethodHandle( + self, "loop._exec_queued_writes", + self._exec_queued_writes, None, self)) + + self._signals = set() + self._ssock = self._csock = None + self._signal_handlers = {} + self._listening_signals = False + self._old_signal_wakeup_id = -1 + + self._coroutine_debug_set = False + + # A weak set of all asynchronous generators that are + # being iterated by the loop. + self._asyncgens = weakref_WeakSet() + + # Set to True when `loop.shutdown_asyncgens` is called. + self._asyncgens_shutdown_called = False + # Set to True when `loop.shutdown_default_executor` is called. + self._executor_shutdown_called = False + + self._servers = set() + + cdef inline _is_main_thread(self): + cdef uint64_t main_thread_id = system.MAIN_THREAD_ID + if system.MAIN_THREAD_ID_SET == 0: + main_thread_id = threading_main_thread().ident + system.setMainThreadID(main_thread_id) + return main_thread_id == PyThread_get_thread_ident() + + def __init__(self): + self.set_debug( + sys_dev_mode or (not sys_ignore_environment + and bool(os_environ.get('PYTHONASYNCIODEBUG')))) + + def __dealloc__(self): + if self._running == 1: + raise RuntimeError('deallocating a running event loop!') + if self._closed == 0: + aio_logger.error("deallocating an open event loop") + return + PyMem_RawFree(self.uvloop) + self.uvloop = NULL + + cdef _init_debug_fields(self): + self._debug_cc = bool(UVLOOP_DEBUG) + + if UVLOOP_DEBUG: + self._debug_handles_current = col_Counter() + self._debug_handles_closed = col_Counter() + self._debug_handles_total = col_Counter() + else: + self._debug_handles_current = None + self._debug_handles_closed = None + self._debug_handles_total = None + + self._debug_uv_handles_total = 0 + self._debug_uv_handles_freed = 0 + + self._debug_stream_read_cb_total = 0 + self._debug_stream_read_eof_total = 0 + self._debug_stream_read_errors_total = 0 + self._debug_stream_read_cb_errors_total = 0 + self._debug_stream_read_eof_cb_errors_total = 0 + + self._debug_stream_shutdown_errors_total = 0 + self._debug_stream_listen_errors_total = 0 + + self._debug_stream_write_tries = 0 + self._debug_stream_write_errors_total = 0 + self._debug_stream_write_ctx_total = 0 + self._debug_stream_write_ctx_cnt = 0 + self._debug_stream_write_cb_errors_total = 0 + + self._debug_cb_handles_total = 0 + self._debug_cb_handles_count = 0 + + self._debug_cb_timer_handles_total = 0 + self._debug_cb_timer_handles_count = 0 + + self._poll_read_events_total = 0 + self._poll_read_cb_errors_total = 0 + self._poll_write_events_total = 0 + self._poll_write_cb_errors_total = 0 + + self._sock_try_write_total = 0 + + self._debug_exception_handler_cnt = 0 + + cdef _setup_or_resume_signals(self): + if not self._is_main_thread(): + return + + if self._listening_signals: + raise RuntimeError('signals handling has been already setup') + + if self._ssock is not None: + raise RuntimeError('self-pipe exists before loop run') + + # Create a self-pipe and call set_signal_wakeup_fd() with one + # of its ends. This is needed so that libuv knows that it needs + # to wakeup on ^C (no matter if the SIGINT handler is still the + # standard Python's one or or user set their own.) + + self._ssock, self._csock = socket_socketpair() + try: + self._ssock.setblocking(False) + self._csock.setblocking(False) + + fileno = self._csock.fileno() + + self._old_signal_wakeup_id = _set_signal_wakeup_fd(fileno) + except Exception: + # Out of all statements in the try block, only the + # "_set_signal_wakeup_fd()" call can fail, but it shouldn't, + # as we ensure that the current thread is the main thread. + # Still, if something goes horribly wrong we want to clean up + # the socket pair. + self._ssock.close() + self._csock.close() + self._ssock = None + self._csock = None + raise + + self._add_reader( + self._ssock, + new_MethodHandle( + self, + "Loop._read_from_self", + self._read_from_self, + None, + self)) + + self._listening_signals = True + + cdef _pause_signals(self): + if not self._is_main_thread(): + if self._listening_signals: + raise RuntimeError( + 'cannot pause signals handling; no longer running in ' + 'the main thread') + else: + return + + if not self._listening_signals: + raise RuntimeError('signals handling has not been setup') + + self._listening_signals = False + + _set_signal_wakeup_fd(self._old_signal_wakeup_id) + + self._remove_reader(self._ssock) + self._ssock.close() + self._csock.close() + self._ssock = None + self._csock = None + + cdef _shutdown_signals(self): + if not self._is_main_thread(): + if self._signal_handlers: + aio_logger.warning( + 'cannot cleanup signal handlers: closing the event loop ' + 'in a non-main OS thread') + return + + if self._listening_signals: + raise RuntimeError( + 'cannot shutdown signals handling as it has not been paused') + + if self._ssock: + raise RuntimeError( + 'self-pipe was not cleaned up after loop was run') + + for sig in list(self._signal_handlers): + self.remove_signal_handler(sig) + + def __sighandler(self, signum, frame): + self._signals.add(signum) + + cdef inline _ceval_process_signals(self): + # Invoke CPython eval loop to let process signals. + PyErr_CheckSignals() + # Calling a pure-Python function will invoke + # _PyEval_EvalFrameDefault which will process + # pending signal callbacks. + _noop.noop() # Might raise ^C + + cdef _read_from_self(self): + cdef bytes sigdata + sigdata = b'' + while True: + try: + data = self._ssock.recv(65536) + if not data: + break + sigdata += data + except InterruptedError: + continue + except BlockingIOError: + break + if sigdata: + self._invoke_signals(sigdata) + + cdef _invoke_signals(self, bytes data): + cdef set sigs + + self._ceval_process_signals() + + sigs = self._signals.copy() + self._signals.clear() + for signum in data: + if not signum: + # ignore null bytes written by set_wakeup_fd() + continue + sigs.discard(signum) + self._handle_signal(signum) + + for signum in sigs: + # Since not all signals are registered by add_signal_handler() + # (for instance, we use the default SIGINT handler) not all + # signals will trigger loop.__sighandler() callback. Therefore + # we combine two datasources: one is self-pipe, one is data + # from __sighandler; this ensures that signals shouldn't be + # lost even if set_wakeup_fd() couldn't write to the self-pipe. + self._handle_signal(signum) + + cdef _handle_signal(self, sig): + cdef Handle handle + + try: + handle = (self._signal_handlers[sig]) + except KeyError: + handle = None + + if handle is None: + self._ceval_process_signals() + return + + if handle._cancelled: + self.remove_signal_handler(sig) # Remove it properly. + else: + self._append_ready_handle(handle) + self.handler_async.send() + + cdef _on_wake(self): + if ((self._ready_len > 0 or self._stopping) and + not self.handler_idle.running): + self.handler_idle.start() + + cdef _on_idle(self): + cdef: + int i, ntodo + object popleft = self._ready.popleft + Handle handler + + ntodo = len(self._ready) + if self._debug: + for i from 0 <= i < ntodo: + handler = popleft() + if handler._cancelled == 0: + try: + started = time_monotonic() + handler._run() + except BaseException as ex: + self._stop(ex) + return + else: + delta = time_monotonic() - started + if delta > self.slow_callback_duration: + aio_logger.warning( + 'Executing %s took %.3f seconds', + handler._format_handle(), delta) + + else: + for i from 0 <= i < ntodo: + handler = popleft() + if handler._cancelled == 0: + try: + handler._run() + except BaseException as ex: + self._stop(ex) + return + + if len(self._queued_streams): + self._exec_queued_writes() + + self._ready_len = len(self._ready) + if self._ready_len == 0 and self.handler_idle.running: + self.handler_idle.stop() + + if self._stopping: + uv.uv_stop(self.uvloop) # void + + cdef _stop(self, exc): + if exc is not None: + self._last_error = exc + if self._stopping == 1: + return + self._stopping = 1 + if not self.handler_idle.running: + self.handler_idle.start() + + cdef __run(self, uv.uv_run_mode mode): + # Although every UVHandle holds a reference to the loop, + # we want to do everything to ensure that the loop will + # never deallocate during the run -- so we do some + # manual refs management. + Py_INCREF(self) + with nogil: + err = uv.uv_run(self.uvloop, mode) + Py_DECREF(self) + + if err < 0: + raise convert_error(err) + + cdef _run(self, uv.uv_run_mode mode): + cdef int err + + if self._closed == 1: + raise RuntimeError('unable to start the loop; it was closed') + + if self._running == 1: + raise RuntimeError('this event loop is already running.') + + if (aio_get_running_loop is not None and + aio_get_running_loop() is not None): + raise RuntimeError( + 'Cannot run the event loop while another loop is running') + + # reset _last_error + self._last_error = None + + self._thread_id = PyThread_get_thread_ident() + self._running = 1 + + self.handler_check__exec_writes.start() + self.handler_idle.start() + + self._setup_or_resume_signals() + + if aio_set_running_loop is not None: + aio_set_running_loop(self) + try: + self.__run(mode) + finally: + if aio_set_running_loop is not None: + aio_set_running_loop(None) + + self.handler_check__exec_writes.stop() + self.handler_idle.stop() + + self._pause_signals() + + self._thread_id = 0 + self._running = 0 + self._stopping = 0 + + if self._last_error is not None: + # The loop was stopped with an error with 'loop._stop(error)' call + raise self._last_error + + cdef _close(self): + cdef int err + + if self._running == 1: + raise RuntimeError("Cannot close a running event loop") + + if self._closed == 1: + return + + self._closed = 1 + + for cb_handle in self._ready: + cb_handle.cancel() + self._ready.clear() + self._ready_len = 0 + + if self._polls: + for poll_handle in self._polls.values(): + (poll_handle)._close() + + self._polls.clear() + + if self._timers: + for timer_cbhandle in tuple(self._timers): + timer_cbhandle.cancel() + + # Close all remaining handles + self.handler_async._close() + self.handler_idle._close() + self.handler_check__exec_writes._close() + __close_all_handles(self) + self._shutdown_signals() + # During this run there should be no open handles, + # so it should finish right away + self.__run(uv.UV_RUN_DEFAULT) + + if self._fd_to_writer_fileobj: + for fileobj in self._fd_to_writer_fileobj.values(): + socket_dec_io_ref(fileobj) + self._fd_to_writer_fileobj.clear() + + if self._fd_to_reader_fileobj: + for fileobj in self._fd_to_reader_fileobj.values(): + socket_dec_io_ref(fileobj) + self._fd_to_reader_fileobj.clear() + + if self._timers: + raise RuntimeError( + f"new timers were queued during loop closing: {self._timers}") + + if self._polls: + raise RuntimeError( + f"new poll handles were queued during loop closing: " + f"{self._polls}") + + if self._ready: + raise RuntimeError( + f"new callbacks were queued during loop closing: " + f"{self._ready}") + + err = uv.uv_loop_close(self.uvloop) + if err < 0: + raise convert_error(err) + + self.handler_async = None + self.handler_idle = None + self.handler_check__exec_writes = None + + self._executor_shutdown_called = True + executor = self._default_executor + if executor is not None: + self._default_executor = None + executor.shutdown(wait=False) + + cdef uint64_t _time(self): + # asyncio doesn't have a time cache, neither should uvloop. + uv.uv_update_time(self.uvloop) # void + return uv.uv_now(self.uvloop) + + cdef inline _queue_write(self, UVStream stream): + self._queued_streams.add(stream) + if not self.handler_check__exec_writes.running: + self.handler_check__exec_writes.start() + + cdef _exec_queued_writes(self): + if len(self._queued_streams) == 0: + if self.handler_check__exec_writes.running: + self.handler_check__exec_writes.stop() + return + + cdef: + UVStream stream + + streams = self._queued_streams + self._queued_streams = self._executing_streams + self._executing_streams = streams + try: + for pystream in streams: + stream = pystream + stream._exec_write() + finally: + streams.clear() + + if self.handler_check__exec_writes.running: + if len(self._queued_streams) == 0: + self.handler_check__exec_writes.stop() + + cdef inline _call_soon(self, object callback, object args, object context): + cdef Handle handle + handle = new_Handle(self, callback, args, context) + self._call_soon_handle(handle) + return handle + + cdef inline _append_ready_handle(self, Handle handle): + self._check_closed() + self._ready.append(handle) + self._ready_len += 1 + + cdef inline _call_soon_handle(self, Handle handle): + self._append_ready_handle(handle) + if not self.handler_idle.running: + self.handler_idle.start() + + cdef _call_later(self, uint64_t delay, object callback, object args, + object context): + return TimerHandle(self, callback, args, delay, context) + + cdef void _handle_exception(self, object ex): + if isinstance(ex, Exception): + self.call_exception_handler({'exception': ex}) + else: + # BaseException + self._last_error = ex + # Exit ASAP + self._stop(None) + + cdef inline _check_signal(self, sig): + if not isinstance(sig, int): + raise TypeError('sig must be an int, not {!r}'.format(sig)) + + if not (1 <= sig < signal_NSIG): + raise ValueError( + 'sig {} out of range(1, {})'.format(sig, signal_NSIG)) + + cdef inline _check_closed(self): + if self._closed == 1: + raise RuntimeError('Event loop is closed') + + cdef inline _check_thread(self): + if self._thread_id == 0: + return + + cdef uint64_t thread_id + thread_id = PyThread_get_thread_ident() + + if thread_id != self._thread_id: + raise RuntimeError( + "Non-thread-safe operation invoked on an event loop other " + "than the current one") + + cdef inline _new_future(self): + return aio_Future(loop=self) + + cdef _track_transport(self, UVBaseTransport transport): + self._transports[transport._fileno()] = transport + + cdef _track_process(self, UVProcess proc): + self._processes.add(proc) + + cdef _untrack_process(self, UVProcess proc): + self._processes.discard(proc) + + cdef _fileobj_to_fd(self, fileobj): + """Return a file descriptor from a file object. + + Parameters: + fileobj -- file object or file descriptor + + Returns: + corresponding file descriptor + + Raises: + ValueError if the object is invalid + """ + # Copy of the `selectors._fileobj_to_fd()` function. + if isinstance(fileobj, int): + fd = fileobj + else: + try: + fd = int(fileobj.fileno()) + except (AttributeError, TypeError, ValueError): + raise ValueError("Invalid file object: " + "{!r}".format(fileobj)) from None + if fd < 0: + raise ValueError("Invalid file descriptor: {}".format(fd)) + return fd + + cdef _ensure_fd_no_transport(self, fd): + cdef UVBaseTransport tr + try: + tr = (self._transports[fd]) + except KeyError: + pass + else: + if tr._is_alive(): + raise RuntimeError( + 'File descriptor {!r} is used by transport {!r}'.format( + fd, tr)) + + cdef _add_reader(self, fileobj, Handle handle): + cdef: + UVPoll poll + + self._check_closed() + fd = self._fileobj_to_fd(fileobj) + self._ensure_fd_no_transport(fd) + + try: + poll = (self._polls[fd]) + except KeyError: + poll = UVPoll.new(self, fd) + self._polls[fd] = poll + + poll.start_reading(handle) + + old_fileobj = self._fd_to_reader_fileobj.pop(fd, None) + if old_fileobj is not None: + socket_dec_io_ref(old_fileobj) + + self._fd_to_reader_fileobj[fd] = fileobj + socket_inc_io_ref(fileobj) + + cdef _remove_reader(self, fileobj): + cdef: + UVPoll poll + + fd = self._fileobj_to_fd(fileobj) + self._ensure_fd_no_transport(fd) + + mapped_fileobj = self._fd_to_reader_fileobj.pop(fd, None) + if mapped_fileobj is not None: + socket_dec_io_ref(mapped_fileobj) + + if self._closed == 1: + return False + + try: + poll = (self._polls[fd]) + except KeyError: + return False + + result = poll.stop_reading() + if not poll.is_active(): + del self._polls[fd] + poll._close() + + return result + + cdef _has_reader(self, fileobj): + cdef: + UVPoll poll + + self._check_closed() + fd = self._fileobj_to_fd(fileobj) + + try: + poll = (self._polls[fd]) + except KeyError: + return False + + return poll.is_reading() + + cdef _add_writer(self, fileobj, Handle handle): + cdef: + UVPoll poll + + self._check_closed() + fd = self._fileobj_to_fd(fileobj) + self._ensure_fd_no_transport(fd) + + try: + poll = (self._polls[fd]) + except KeyError: + poll = UVPoll.new(self, fd) + self._polls[fd] = poll + + poll.start_writing(handle) + + old_fileobj = self._fd_to_writer_fileobj.pop(fd, None) + if old_fileobj is not None: + socket_dec_io_ref(old_fileobj) + + self._fd_to_writer_fileobj[fd] = fileobj + socket_inc_io_ref(fileobj) + + cdef _remove_writer(self, fileobj): + cdef: + UVPoll poll + + fd = self._fileobj_to_fd(fileobj) + self._ensure_fd_no_transport(fd) + + mapped_fileobj = self._fd_to_writer_fileobj.pop(fd, None) + if mapped_fileobj is not None: + socket_dec_io_ref(mapped_fileobj) + + if self._closed == 1: + return False + + try: + poll = (self._polls[fd]) + except KeyError: + return False + + result = poll.stop_writing() + if not poll.is_active(): + del self._polls[fd] + poll._close() + + return result + + cdef _has_writer(self, fileobj): + cdef: + UVPoll poll + + self._check_closed() + fd = self._fileobj_to_fd(fileobj) + + try: + poll = (self._polls[fd]) + except KeyError: + return False + + return poll.is_writing() + + cdef _getaddrinfo(self, object host, object port, + int family, int type, + int proto, int flags, + int unpack): + + if isinstance(port, str): + port = port.encode() + elif isinstance(port, int): + port = str(port).encode() + if port is not None and not isinstance(port, bytes): + raise TypeError('port must be a str, bytes or int') + + if isinstance(host, str): + host = host.encode('idna') + if host is not None: + if not isinstance(host, bytes): + raise TypeError('host must be a str or bytes') + + fut = self._new_future() + + def callback(result): + if AddrInfo.isinstance(result): + try: + if unpack == 0: + data = result + else: + data = (result).unpack() + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + if not fut.cancelled(): + fut.set_exception(ex) + else: + if not fut.cancelled(): + fut.set_result(data) + else: + if not fut.cancelled(): + fut.set_exception(result) + + AddrInfoRequest(self, host, port, family, type, proto, flags, callback) + return fut + + cdef _getnameinfo(self, system.sockaddr *addr, int flags): + cdef NameInfoRequest nr + fut = self._new_future() + + def callback(result): + if isinstance(result, tuple): + fut.set_result(result) + else: + fut.set_exception(result) + + nr = NameInfoRequest(self, callback) + nr.query(addr, flags) + return fut + + cdef _sock_recv(self, fut, sock, n): + if UVLOOP_DEBUG: + if fut.cancelled(): + # Shouldn't happen with _SyncSocketReaderFuture. + raise RuntimeError( + f'_sock_recv is called on a cancelled Future') + + if not self._has_reader(sock): + raise RuntimeError( + f'socket {sock!r} does not have a reader ' + f'in the _sock_recv callback') + + try: + data = sock.recv(n) + except (BlockingIOError, InterruptedError): + # No need to re-add the reader, let's just wait until + # the poll handler calls this callback again. + pass + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + fut.set_exception(exc) + self._remove_reader(sock) + else: + fut.set_result(data) + self._remove_reader(sock) + + cdef _sock_recv_into(self, fut, sock, buf): + if UVLOOP_DEBUG: + if fut.cancelled(): + # Shouldn't happen with _SyncSocketReaderFuture. + raise RuntimeError( + f'_sock_recv_into is called on a cancelled Future') + + if not self._has_reader(sock): + raise RuntimeError( + f'socket {sock!r} does not have a reader ' + f'in the _sock_recv_into callback') + + try: + data = sock.recv_into(buf) + except (BlockingIOError, InterruptedError): + # No need to re-add the reader, let's just wait until + # the poll handler calls this callback again. + pass + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + fut.set_exception(exc) + self._remove_reader(sock) + else: + fut.set_result(data) + self._remove_reader(sock) + + cdef _sock_sendall(self, fut, sock, data): + cdef: + Handle handle + int n + + if UVLOOP_DEBUG: + if fut.cancelled(): + # Shouldn't happen with _SyncSocketWriterFuture. + raise RuntimeError( + f'_sock_sendall is called on a cancelled Future') + + if not self._has_writer(sock): + raise RuntimeError( + f'socket {sock!r} does not have a writer ' + f'in the _sock_sendall callback') + + try: + n = sock.send(data) + except (BlockingIOError, InterruptedError): + # Try next time. + return + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + fut.set_exception(exc) + self._remove_writer(sock) + return + + self._remove_writer(sock) + + if n == len(data): + fut.set_result(None) + else: + if n: + if not isinstance(data, memoryview): + data = memoryview(data) + data = data[n:] + + handle = new_MethodHandle3( + self, + "Loop._sock_sendall", + self._sock_sendall, + None, + self, + fut, sock, data) + + self._add_writer(sock, handle) + + cdef _sock_accept(self, fut, sock): + try: + conn, address = sock.accept() + conn.setblocking(False) + except (BlockingIOError, InterruptedError): + # There is an active reader for _sock_accept, so + # do nothing, it will be called again. + pass + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + fut.set_exception(exc) + self._remove_reader(sock) + else: + fut.set_result((conn, address)) + self._remove_reader(sock) + + cdef _sock_connect(self, sock, address): + cdef: + Handle handle + + try: + sock.connect(address) + except (BlockingIOError, InterruptedError): + pass + else: + return + + fut = _SyncSocketWriterFuture(sock, self) + handle = new_MethodHandle3( + self, + "Loop._sock_connect", + self._sock_connect_cb, + None, + self, + fut, sock, address) + + self._add_writer(sock, handle) + return fut + + cdef _sock_connect_cb(self, fut, sock, address): + if UVLOOP_DEBUG: + if fut.cancelled(): + # Shouldn't happen with _SyncSocketWriterFuture. + raise RuntimeError( + f'_sock_connect_cb is called on a cancelled Future') + + if not self._has_writer(sock): + raise RuntimeError( + f'socket {sock!r} does not have a writer ' + f'in the _sock_connect_cb callback') + + try: + err = sock.getsockopt(uv.SOL_SOCKET, uv.SO_ERROR) + if err != 0: + # Jump to any except clause below. + raise OSError(err, 'Connect call failed %s' % (address,)) + except (BlockingIOError, InterruptedError): + # socket is still registered, the callback will be retried later + pass + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + fut.set_exception(exc) + self._remove_writer(sock) + else: + fut.set_result(None) + self._remove_writer(sock) + + cdef _sock_set_reuseport(self, int fd): + cdef: + int err + int reuseport_flag = 1 + + err = system.setsockopt( + fd, + uv.SOL_SOCKET, + SO_REUSEPORT, + &reuseport_flag, + sizeof(reuseport_flag)) + + if err < 0: + raise convert_error(-errno.errno) + + cdef _set_coroutine_debug(self, bint enabled): + enabled = bool(enabled) + if self._coroutine_debug_set == enabled: + return + + if enabled: + self._coroutine_origin_tracking_saved_depth = ( + sys.get_coroutine_origin_tracking_depth()) + sys.set_coroutine_origin_tracking_depth( + DEBUG_STACK_DEPTH) + else: + sys.set_coroutine_origin_tracking_depth( + self._coroutine_origin_tracking_saved_depth) + + self._coroutine_debug_set = enabled + + def _get_backend_id(self): + """This method is used by uvloop tests and is not part of the API.""" + return uv.uv_backend_fd(self.uvloop) + + cdef _print_debug_info(self): + cdef: + int err + uv.uv_rusage_t rusage + + err = uv.uv_getrusage(&rusage) + if err < 0: + raise convert_error(err) + + # OS + + print('---- Process info: -----') + print('Process memory: {}'.format(rusage.ru_maxrss)) + print('Number of signals: {}'.format(rusage.ru_nsignals)) + print('') + + # Loop + + print('--- Loop debug info: ---') + print('Loop time: {}'.format(self.time())) + print('Errors logged: {}'.format( + self._debug_exception_handler_cnt)) + print() + print('Callback handles: {: <8} | {}'.format( + self._debug_cb_handles_count, + self._debug_cb_handles_total)) + print('Timer handles: {: <8} | {}'.format( + self._debug_cb_timer_handles_count, + self._debug_cb_timer_handles_total)) + print() + + print(' alive | closed |') + print('UVHandles python | libuv | total') + print(' objs | handles |') + print('-------------------------------+---------+---------') + for name in sorted(self._debug_handles_total): + print(' {: <18} {: >7} | {: >7} | {: >7}'.format( + name, + self._debug_handles_current[name], + self._debug_handles_closed[name], + self._debug_handles_total[name])) + print() + + print('uv_handle_t (current: {}; freed: {}; total: {})'.format( + self._debug_uv_handles_total - self._debug_uv_handles_freed, + self._debug_uv_handles_freed, + self._debug_uv_handles_total)) + print() + + print('--- Streams debug info: ---') + print('Write errors: {}'.format( + self._debug_stream_write_errors_total)) + print('Write without poll: {}'.format( + self._debug_stream_write_tries)) + print('Write contexts: {: <8} | {}'.format( + self._debug_stream_write_ctx_cnt, + self._debug_stream_write_ctx_total)) + print('Write failed callbacks: {}'.format( + self._debug_stream_write_cb_errors_total)) + print() + print('Read errors: {}'.format( + self._debug_stream_read_errors_total)) + print('Read callbacks: {}'.format( + self._debug_stream_read_cb_total)) + print('Read failed callbacks: {}'.format( + self._debug_stream_read_cb_errors_total)) + print('Read EOFs: {}'.format( + self._debug_stream_read_eof_total)) + print('Read EOF failed callbacks: {}'.format( + self._debug_stream_read_eof_cb_errors_total)) + print() + print('Listen errors: {}'.format( + self._debug_stream_listen_errors_total)) + print('Shutdown errors {}'.format( + self._debug_stream_shutdown_errors_total)) + print() + + print('--- Polls debug info: ---') + print('Read events: {}'.format( + self._poll_read_events_total)) + print('Read callbacks failed: {}'.format( + self._poll_read_cb_errors_total)) + print('Write events: {}'.format( + self._poll_write_events_total)) + print('Write callbacks failed: {}'.format( + self._poll_write_cb_errors_total)) + print() + + print('--- Sock ops successful on 1st try: ---') + print('Socket try-writes: {}'.format( + self._sock_try_write_total)) + + print(flush=True) + + property print_debug_info: + def __get__(self): + if UVLOOP_DEBUG: + return lambda: self._print_debug_info() + else: + raise AttributeError('print_debug_info') + + # Public API + + def __repr__(self): + return '<{}.{} running={} closed={} debug={}>'.format( + self.__class__.__module__, + self.__class__.__name__, + self.is_running(), + self.is_closed(), + self.get_debug() + ) + + def call_soon(self, callback, *args, context=None): + """Arrange for a callback to be called as soon as possible. + + This operates as a FIFO queue: callbacks are called in the + order in which they are registered. Each callback will be + called exactly once. + + Any positional arguments after the callback will be passed to + the callback when it is called. + """ + if self._debug == 1: + self._check_thread() + if args: + return self._call_soon(callback, args, context) + else: + return self._call_soon(callback, None, context) + + def call_soon_threadsafe(self, callback, *args, context=None): + """Like call_soon(), but thread-safe.""" + if not args: + args = None + cdef Handle handle = new_Handle(self, callback, args, context) + self._append_ready_handle(handle) # deque append is atomic + # libuv async handler is thread-safe while the idle handler is not - + # we only set the async handler here, which will start the idle handler + # in _on_wake() from the loop and eventually call the callback. + self.handler_async.send() + return handle + + def call_later(self, delay, callback, *args, context=None): + """Arrange for a callback to be called at a given time. + + Return a Handle: an opaque object with a cancel() method that + can be used to cancel the call. + + The delay can be an int or float, expressed in seconds. It is + always relative to the current time. + + Each callback will be called exactly once. If two callbacks + are scheduled for exactly the same time, it undefined which + will be called first. + + Any positional arguments after the callback will be passed to + the callback when it is called. + """ + cdef uint64_t when + + self._check_closed() + if self._debug == 1: + self._check_thread() + + if delay < 0: + delay = 0 + elif delay == py_inf or delay > MAX_SLEEP: + # ~100 years sounds like a good approximation of + # infinity for a Python application. + delay = MAX_SLEEP + + when = round(delay * 1000) + if not args: + args = None + if when == 0: + return self._call_soon(callback, args, context) + else: + return self._call_later(when, callback, args, context) + + def call_at(self, when, callback, *args, context=None): + """Like call_later(), but uses an absolute time. + + Absolute time corresponds to the event loop's time() method. + """ + return self.call_later( + when - self.time(), callback, *args, context=context) + + def time(self): + """Return the time according to the event loop's clock. + + This is a float expressed in seconds since an epoch, but the + epoch, precision, accuracy and drift are unspecified and may + differ per event loop. + """ + return self._time() / 1000 + + def stop(self): + """Stop running the event loop. + + Every callback already scheduled will still run. This simply informs + run_forever to stop looping after a complete iteration. + """ + self._call_soon_handle( + new_MethodHandle1( + self, + "Loop._stop", + self._stop, + None, + self, + None)) + + def run_forever(self): + """Run the event loop until stop() is called.""" + self._check_closed() + mode = uv.UV_RUN_DEFAULT + if self._stopping: + # loop.stop() was called right before loop.run_forever(). + # This is how asyncio loop behaves. + mode = uv.UV_RUN_NOWAIT + self._set_coroutine_debug(self._debug) + old_agen_hooks = sys.get_asyncgen_hooks() + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, + finalizer=self._asyncgen_finalizer_hook) + try: + self._run(mode) + finally: + self._set_coroutine_debug(False) + sys.set_asyncgen_hooks(*old_agen_hooks) + + def close(self): + """Close the event loop. + + The event loop must not be running. + + This is idempotent and irreversible. + + No other methods should be called after this one. + """ + self._close() + + def get_debug(self): + return bool(self._debug) + + def set_debug(self, enabled): + self._debug = bool(enabled) + if self.is_running(): + self.call_soon_threadsafe( + self._set_coroutine_debug, self, self._debug) + + def is_running(self): + """Return whether the event loop is currently running.""" + return bool(self._running) + + def is_closed(self): + """Returns True if the event loop was closed.""" + return bool(self._closed) + + def create_future(self): + """Create a Future object attached to the loop.""" + return self._new_future() + + def create_task(self, coro, *, name=None, context=None): + """Schedule a coroutine object. + + Return a task object. + + If name is not None, task.set_name(name) will be called if the task + object has the set_name attribute, true for default Task in CPython. + + An optional keyword-only context argument allows specifying a custom + contextvars.Context for the coro to run in. The current context copy is + created when no context is provided. + """ + self._check_closed() + if PY311: + if self._task_factory is None: + task = aio_Task(coro, loop=self, context=context) + else: + task = self._task_factory(self, coro, context=context) + else: + if context is None: + if self._task_factory is None: + task = aio_Task(coro, loop=self) + else: + task = self._task_factory(self, coro) + else: + if self._task_factory is None: + task = context.run(aio_Task, coro, self) + else: + task = context.run(self._task_factory, self, coro) + + # copied from asyncio.tasks._set_task_name (bpo-34270) + if name is not None: + try: + set_name = task.set_name + except AttributeError: + pass + else: + set_name(name) + + return task + + def set_task_factory(self, factory): + """Set a task factory that will be used by loop.create_task(). + + If factory is None the default task factory will be set. + + If factory is a callable, it should have a signature matching + '(loop, coro)', where 'loop' will be a reference to the active + event loop, 'coro' will be a coroutine object. The callable + must return a Future. + """ + if factory is not None and not callable(factory): + raise TypeError('task factory must be a callable or None') + self._task_factory = factory + + def get_task_factory(self): + """Return a task factory, or None if the default one is in use.""" + return self._task_factory + + def run_until_complete(self, future): + """Run until the Future is done. + + If the argument is a coroutine, it is wrapped in a Task. + + WARNING: It would be disastrous to call run_until_complete() + with the same coroutine twice -- it would wrap it in two + different Tasks and that can't be good. + + Return the Future's result, or raise its exception. + """ + self._check_closed() + + new_task = not isfuture(future) + future = aio_ensure_future(future, loop=self) + if new_task: + # An exception is raised if the future didn't complete, so there + # is no need to log the "destroy pending task" message + future._log_destroy_pending = False + + def done_cb(fut): + if not fut.cancelled(): + exc = fut.exception() + if isinstance(exc, (SystemExit, KeyboardInterrupt)): + # Issue #336: run_forever() already finished, + # no need to stop it. + return + self.stop() + + future.add_done_callback(done_cb) + try: + self.run_forever() + except BaseException: + if new_task and future.done() and not future.cancelled(): + # The coroutine raised a BaseException. Consume the exception + # to not log a warning, the caller doesn't have access to the + # local task. + future.exception() + raise + finally: + future.remove_done_callback(done_cb) + if not future.done(): + raise RuntimeError('Event loop stopped before Future completed.') + + return future.result() + + @cython.iterable_coroutine + async def getaddrinfo(self, object host, object port, *, + int family=0, int type=0, int proto=0, int flags=0): + + addr = __static_getaddrinfo_pyaddr(host, port, family, + type, proto, flags) + if addr is not None: + return [addr] + + return await self._getaddrinfo( + host, port, family, type, proto, flags, 1) + + @cython.iterable_coroutine + async def getnameinfo(self, sockaddr, int flags=0): + cdef: + AddrInfo ai_cnt + system.addrinfo *ai + system.sockaddr_in6 *sin6 + + if not isinstance(sockaddr, tuple): + raise TypeError('getnameinfo() argument 1 must be a tuple') + + sl = len(sockaddr) + + if sl < 2 or sl > 4: + raise ValueError('sockaddr must be a tuple of 2, 3 or 4 values') + + if sl > 2: + flowinfo = sockaddr[2] + if flowinfo < 0 or flowinfo > 0xfffff: + raise OverflowError( + 'getnameinfo(): flowinfo must be 0-1048575.') + else: + flowinfo = 0 + + if sl > 3: + scope_id = sockaddr[3] + if scope_id < 0 or scope_id > 2 ** 32: + raise OverflowError( + 'getsockaddrarg: scope_id must be unsigned 32 bit integer') + else: + scope_id = 0 + + ai_cnt = await self._getaddrinfo( + sockaddr[0], sockaddr[1], + uv.AF_UNSPEC, # family + uv.SOCK_DGRAM, # type + 0, # proto + uv.AI_NUMERICHOST, # flags + 0) # unpack + + ai = ai_cnt.data + + if ai.ai_next: + raise OSError("sockaddr resolved to multiple addresses") + + if ai.ai_family == uv.AF_INET: + if sl > 2: + raise OSError("IPv4 sockaddr must be 2 tuple") + elif ai.ai_family == uv.AF_INET6: + # Modify some fields in `ai` + sin6 = ai.ai_addr + sin6.sin6_flowinfo = system.htonl(flowinfo) + sin6.sin6_scope_id = scope_id + + return await self._getnameinfo(ai.ai_addr, flags) + + @cython.iterable_coroutine + async def start_tls(self, transport, protocol, sslcontext, *, + server_side=False, + server_hostname=None, + ssl_handshake_timeout=None, + ssl_shutdown_timeout=None): + """Upgrade transport to TLS. + + Return a new transport that *protocol* should start using + immediately. + """ + if not isinstance(sslcontext, ssl_SSLContext): + raise TypeError( + f'sslcontext is expected to be an instance of ssl.SSLContext, ' + f'got {sslcontext!r}') + + if isinstance(transport, (TCPTransport, UnixTransport)): + context = (transport).context + elif isinstance(transport, _SSLProtocolTransport): + context = (<_SSLProtocolTransport>transport).context + else: + raise TypeError( + f'transport {transport!r} is not supported by start_tls()') + + waiter = self._new_future() + ssl_protocol = SSLProtocol( + self, protocol, sslcontext, waiter, + server_side, server_hostname, + ssl_handshake_timeout=ssl_handshake_timeout, + ssl_shutdown_timeout=ssl_shutdown_timeout, + call_connection_made=False) + + # Pause early so that "ssl_protocol.data_received()" doesn't + # have a chance to get called before "ssl_protocol.connection_made()". + transport.pause_reading() + + transport.set_protocol(ssl_protocol) + conmade_cb = self.call_soon(ssl_protocol.connection_made, transport, + context=context) + # transport.resume_reading() will use the right context + # (transport.context) to call e.g. data_received() + resume_cb = self.call_soon(transport.resume_reading) + app_transport = ssl_protocol._get_app_transport(context) + + try: + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + app_transport.close() + conmade_cb.cancel() + resume_cb.cancel() + raise + + return app_transport + + @cython.iterable_coroutine + async def create_server(self, protocol_factory, host=None, port=None, + *, + int family=uv.AF_UNSPEC, + int flags=uv.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): + """A coroutine which creates a TCP server bound to host and port. + + The return value is a Server object which can be used to stop + the service. + + If host is an empty string or None all interfaces are assumed + and a list of multiple sockets will be returned (most likely + one for IPv4 and another one for IPv6). The host parameter can also be + a sequence (e.g. list) of hosts to bind to. + + family can be set to either AF_INET or AF_INET6 to force the + socket to use IPv4 or IPv6. If not set it will be determined + from host (defaults to AF_UNSPEC). + + flags is a bitmask for getaddrinfo(). + + sock can optionally be specified in order to use a preexisting + socket object. + + backlog is the maximum number of queued connections passed to + listen() (defaults to 100). + + ssl can be set to an SSLContext to enable SSL over the + accepted connections. + + reuse_address tells the kernel to reuse a local socket in + TIME_WAIT state, without waiting for its natural timeout to + expire. If not specified will automatically be set to True on + UNIX. + + reuse_port tells the kernel to allow this endpoint to be bound to + the same port as other existing endpoints are bound to, so long as + they all set this flag when being created. This option is not + supported on Windows. + + ssl_handshake_timeout is the time in seconds that an SSL server + will wait for completion of the SSL handshake before aborting the + connection. Default is 60s. + + ssl_shutdown_timeout is the time in seconds that an SSL server + will wait for completion of the SSL shutdown before aborting the + connection. Default is 30s. + """ + cdef: + TCPServer tcp + system.addrinfo *addrinfo + Server server + + if sock is not None and sock.family == uv.AF_UNIX: + if host is not None or port is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + return await self.create_unix_server( + protocol_factory, sock=sock, backlog=backlog, ssl=ssl, + start_serving=start_serving) + + server = Server(self) + + if ssl is not None: + if not isinstance(ssl, ssl_SSLContext): + raise TypeError('ssl argument must be an SSLContext or None') + else: + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + if host is not None or port is not None: + if sock is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + + if reuse_address is None: + reuse_address = os_name == 'posix' and sys_platform != 'cygwin' + reuse_port = bool(reuse_port) + if reuse_port and not has_SO_REUSEPORT: + raise ValueError( + 'reuse_port not supported by socket module') + + if host == '': + hosts = [None] + elif (isinstance(host, str) or not isinstance(host, col_Iterable)): + hosts = [host] + else: + hosts = host + + fs = [self._getaddrinfo(host, port, family, + uv.SOCK_STREAM, 0, flags, + 0) for host in hosts] + + infos = await aio_gather(*fs) + + completed = False + sock = None + try: + for info in infos: + addrinfo = (info).data + while addrinfo != NULL: + if addrinfo.ai_family == uv.AF_UNSPEC: + raise RuntimeError('AF_UNSPEC in DNS results') + + try: + sock = socket_socket(addrinfo.ai_family, + addrinfo.ai_socktype, + addrinfo.ai_protocol) + except socket_error: + # Assume it's a bad family/type/protocol + # combination. + if self._debug: + aio_logger.warning( + 'create_server() failed to create ' + 'socket.socket(%r, %r, %r)', + addrinfo.ai_family, + addrinfo.ai_socktype, + addrinfo.ai_protocol, exc_info=True) + addrinfo = addrinfo.ai_next + continue + + if reuse_address: + sock.setsockopt(uv.SOL_SOCKET, uv.SO_REUSEADDR, 1) + if reuse_port: + sock.setsockopt(uv.SOL_SOCKET, uv.SO_REUSEPORT, 1) + # Disable IPv4/IPv6 dual stack support (enabled by + # default on Linux) which makes a single socket + # listen on both address families. + if (addrinfo.ai_family == uv.AF_INET6 and + has_IPV6_V6ONLY): + sock.setsockopt(uv.IPPROTO_IPV6, IPV6_V6ONLY, 1) + + pyaddr = __convert_sockaddr_to_pyaddr(addrinfo.ai_addr) + try: + sock.bind(pyaddr) + except OSError as err: + raise OSError( + err.errno, 'error while attempting ' + 'to bind on address %r: %s' + % (pyaddr, err.strerror.lower())) from None + + tcp = TCPServer.new(self, protocol_factory, server, + uv.AF_UNSPEC, backlog, + ssl, ssl_handshake_timeout, + ssl_shutdown_timeout) + + try: + tcp._open(sock.fileno()) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + tcp._close() + raise + + server._add_server(tcp) + sock.detach() + sock = None + + addrinfo = addrinfo.ai_next + + completed = True + finally: + if not completed: + if sock is not None: + sock.close() + server.close() + else: + if sock is None: + raise ValueError('Neither host/port nor sock were specified') + if not _is_sock_stream(sock.type): + raise ValueError( + 'A Stream Socket was expected, got {!r}'.format(sock)) + + # libuv will set the socket to non-blocking mode, but + # we want Python socket object to notice that. + sock.setblocking(False) + + tcp = TCPServer.new(self, protocol_factory, server, + uv.AF_UNSPEC, backlog, + ssl, ssl_handshake_timeout, + ssl_shutdown_timeout) + + try: + tcp._open(sock.fileno()) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + tcp._close() + raise + + tcp._attach_fileobj(sock) + server._add_server(tcp) + + if start_serving: + server._start_serving() + + server._ref() + return server + + @cython.iterable_coroutine + 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): + """Connect to a TCP server. + + Create a streaming transport connection to a given Internet host and + port: socket family AF_INET or socket.AF_INET6 depending on host (or + family if specified), socket type SOCK_STREAM. protocol_factory must be + a callable returning a protocol instance. + + This method is a coroutine which will try to establish the connection + in the background. When successful, the coroutine returns a + (transport, protocol) pair. + """ + cdef: + AddrInfo ai_local = None + AddrInfo ai_remote + TCPTransport tr + + system.addrinfo *rai = NULL + system.addrinfo *lai = NULL + + system.addrinfo *rai_iter = NULL + system.addrinfo *lai_iter = NULL + + system.addrinfo rai_static + system.sockaddr_storage rai_addr_static + system.addrinfo lai_static + system.sockaddr_storage lai_addr_static + + object app_protocol + object app_transport + object protocol + object ssl_waiter + + if sock is not None and sock.family == uv.AF_UNIX: + if host is not None or port is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + return await self.create_unix_connection( + protocol_factory, None, + sock=sock, ssl=ssl, server_hostname=server_hostname) + + app_protocol = protocol = protocol_factory() + ssl_waiter = None + context = Context_CopyCurrent() + if ssl: + if server_hostname is None: + if not host: + raise ValueError('You must set server_hostname ' + 'when using ssl without a host') + server_hostname = host + + ssl_waiter = self._new_future() + sslcontext = None if isinstance(ssl, bool) else ssl + protocol = SSLProtocol( + self, app_protocol, sslcontext, ssl_waiter, + False, server_hostname, + ssl_handshake_timeout=ssl_handshake_timeout, + ssl_shutdown_timeout=ssl_shutdown_timeout) + else: + if server_hostname is not None: + raise ValueError('server_hostname is only meaningful with ssl') + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + if host is not None or port is not None: + if sock is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + + fs = [] + f1 = f2 = None + + addr = __static_getaddrinfo( + host, port, family, uv.SOCK_STREAM, + proto, &rai_addr_static) + + if addr is None: + f1 = self._getaddrinfo( + host, port, family, + uv.SOCK_STREAM, proto, flags, + 0) # 0 == don't unpack + + fs.append(f1) + else: + rai_static.ai_addr = &rai_addr_static + rai_static.ai_next = NULL + rai = &rai_static + + if local_addr is not None: + if not isinstance(local_addr, (tuple, list)) or \ + len(local_addr) != 2: + raise ValueError( + 'local_addr must be a tuple of host and port') + + addr = __static_getaddrinfo( + local_addr[0], local_addr[1], + family, uv.SOCK_STREAM, + proto, &lai_addr_static) + if addr is None: + f2 = self._getaddrinfo( + local_addr[0], local_addr[1], family, + uv.SOCK_STREAM, proto, flags, + 0) # 0 == don't unpack + + fs.append(f2) + else: + lai_static.ai_addr = &lai_addr_static + lai_static.ai_next = NULL + lai = &lai_static + + if len(fs): + await aio_wait(fs) + + if rai is NULL: + ai_remote = f1.result() + if ai_remote.data is NULL: + raise OSError('getaddrinfo() returned empty list') + rai = ai_remote.data + + if lai is NULL and f2 is not None: + ai_local = f2.result() + if ai_local.data is NULL: + raise OSError( + 'getaddrinfo() returned empty list for local_addr') + lai = ai_local.data + + exceptions = [] + rai_iter = rai + while rai_iter is not NULL: + tr = None + try: + waiter = self._new_future() + tr = TCPTransport.new(self, protocol, None, waiter, + context) + + if lai is not NULL: + lai_iter = lai + while lai_iter is not NULL: + try: + tr.bind(lai_iter.ai_addr) + break + except OSError as exc: + exceptions.append(exc) + lai_iter = lai_iter.ai_next + else: + tr._close() + tr = None + + rai_iter = rai_iter.ai_next + continue + + tr.connect(rai_iter.ai_addr) + await waiter + + except OSError as exc: + if tr is not None: + tr._close() + tr = None + exceptions.append(exc) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + if tr is not None: + tr._close() + tr = None + raise + else: + break + + rai_iter = rai_iter.ai_next + + else: + # If they all have the same str(), raise one. + model = str(exceptions[0]) + if all(str(exc) == model for exc in exceptions): + raise exceptions[0] + # Raise a combined exception so the user can see all + # the various error messages. + raise OSError('Multiple exceptions: {}'.format( + ', '.join(str(exc) for exc in exceptions))) + else: + if sock is None: + raise ValueError( + 'host and port was not specified and no sock specified') + if not _is_sock_stream(sock.type): + raise ValueError( + 'A Stream Socket was expected, got {!r}'.format(sock)) + + # libuv will set the socket to non-blocking mode, but + # we want Python socket object to notice that. + sock.setblocking(False) + + waiter = self._new_future() + tr = TCPTransport.new(self, protocol, None, waiter, context) + try: + # libuv will make socket non-blocking + tr._open(sock.fileno()) + tr._init_protocol() + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + # It's OK to call `_close()` here, as opposed to + # `_force_close()` or `close()` as we want to terminate the + # transport immediately. The `waiter` can only be waken + # up in `Transport._call_connection_made()`, and calling + # `_close()` before it is fine. + tr._close() + raise + + tr._attach_fileobj(sock) + + if ssl: + app_transport = protocol._get_app_transport(context) + try: + await ssl_waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + app_transport.close() + raise + return app_transport, app_protocol + else: + return tr, protocol + + @cython.iterable_coroutine + async def create_unix_server(self, protocol_factory, path=None, + *, backlog=100, sock=None, ssl=None, + ssl_handshake_timeout=None, + ssl_shutdown_timeout=None, + start_serving=True): + """A coroutine which creates a UNIX Domain Socket server. + + The return value is a Server object, which can be used to stop + the service. + + path is a str, representing a file systsem path to bind the + server socket to. + + sock can optionally be specified in order to use a preexisting + socket object. + + backlog is the maximum number of queued connections passed to + listen() (defaults to 100). + + ssl can be set to an SSLContext to enable SSL over the + accepted connections. + + ssl_handshake_timeout is the time in seconds that an SSL server + will wait for completion of the SSL handshake before aborting the + connection. Default is 60s. + + ssl_shutdown_timeout is the time in seconds that an SSL server + will wait for completion of the SSL shutdown before aborting the + connection. Default is 30s. + """ + cdef: + UnixServer pipe + Server server = Server(self) + + if ssl is not None: + if not isinstance(ssl, ssl_SSLContext): + raise TypeError('ssl argument must be an SSLContext or None') + else: + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + if path is not None: + if sock is not None: + raise ValueError( + 'path and sock can not be specified at the same time') + orig_path = path + + path = os_fspath(path) + + if isinstance(path, str): + path = PyUnicode_EncodeFSDefault(path) + + # Check for abstract socket. + if path[0] != 0: + try: + if stat_S_ISSOCK(os_stat(path).st_mode): + os_remove(path) + except FileNotFoundError: + pass + except OSError as err: + # Directory may have permissions only to create socket. + aio_logger.error( + 'Unable to check or remove stale UNIX socket %r: %r', + orig_path, err) + + # We use Python sockets to create a UNIX server socket because + # when UNIX sockets are created by libuv, libuv removes the path + # they were bound to. This is different from asyncio, which + # doesn't cleanup the socket path. + sock = socket_socket(uv.AF_UNIX) + + try: + sock.bind(path) + except OSError as exc: + sock.close() + if exc.errno == errno.EADDRINUSE: + # Let's improve the error message by adding + # with what exact address it occurs. + msg = 'Address {!r} is already in use'.format(orig_path) + raise OSError(errno.EADDRINUSE, msg) from None + else: + raise + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + sock.close() + raise + + else: + if sock is None: + raise ValueError( + 'path was not specified, and no sock specified') + + if sock.family != uv.AF_UNIX or not _is_sock_stream(sock.type): + raise ValueError( + 'A UNIX Domain Stream Socket was expected, got {!r}' + .format(sock)) + + # libuv will set the socket to non-blocking mode, but + # we want Python socket object to notice that. + sock.setblocking(False) + + pipe = UnixServer.new( + self, protocol_factory, server, backlog, + ssl, ssl_handshake_timeout, ssl_shutdown_timeout) + + try: + pipe._open(sock.fileno()) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + pipe._close() + sock.close() + raise + + pipe._attach_fileobj(sock) + server._add_server(pipe) + + if start_serving: + server._start_serving() + + return server + + @cython.iterable_coroutine + 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): + + cdef: + UnixTransport tr + object app_protocol + object app_transport + object protocol + object ssl_waiter + + app_protocol = protocol = protocol_factory() + ssl_waiter = None + context = Context_CopyCurrent() + if ssl: + if server_hostname is None: + raise ValueError('You must set server_hostname ' + 'when using ssl without a host') + + ssl_waiter = self._new_future() + sslcontext = None if isinstance(ssl, bool) else ssl + protocol = SSLProtocol( + self, app_protocol, sslcontext, ssl_waiter, + False, server_hostname, + ssl_handshake_timeout=ssl_handshake_timeout, + ssl_shutdown_timeout=ssl_shutdown_timeout) + else: + if server_hostname is not None: + raise ValueError('server_hostname is only meaningful with ssl') + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + if path is not None: + if sock is not None: + raise ValueError( + 'path and sock can not be specified at the same time') + + path = os_fspath(path) + + if isinstance(path, str): + path = PyUnicode_EncodeFSDefault(path) + + waiter = self._new_future() + tr = UnixTransport.new(self, protocol, None, waiter, context) + tr.connect(path) + try: + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + tr._close() + raise + + else: + if sock is None: + raise ValueError('no path and sock were specified') + + if sock.family != uv.AF_UNIX or not _is_sock_stream(sock.type): + raise ValueError( + 'A UNIX Domain Stream Socket was expected, got {!r}' + .format(sock)) + + # libuv will set the socket to non-blocking mode, but + # we want Python socket object to notice that. + sock.setblocking(False) + + waiter = self._new_future() + tr = UnixTransport.new(self, protocol, None, waiter, context) + try: + tr._open(sock.fileno()) + tr._init_protocol() + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + tr._close() + raise + + tr._attach_fileobj(sock) + + if ssl: + app_transport = protocol._get_app_transport(Context_CopyCurrent()) + try: + await ssl_waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + app_transport.close() + raise + return app_transport, app_protocol + else: + return tr, protocol + + def default_exception_handler(self, context): + """Default exception handler. + + This is called when an exception occurs and no exception + handler is set, and can be called by a custom exception + handler that wants to defer to the default behavior. + + The context parameter has the same meaning as in + `call_exception_handler()`. + """ + message = context.get('message') + if not message: + message = 'Unhandled exception in event loop' + + exception = context.get('exception') + if exception is not None: + exc_info = (type(exception), exception, exception.__traceback__) + else: + exc_info = False + + log_lines = [message] + for key in sorted(context): + if key in {'message', 'exception'}: + continue + value = context[key] + if key == 'source_traceback': + tb = ''.join(tb_format_list(value)) + value = 'Object created at (most recent call last):\n' + value += tb.rstrip() + else: + try: + value = repr(value) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + value = ('Exception in __repr__ {!r}; ' + 'value type: {!r}'.format(ex, type(value))) + log_lines.append('{}: {}'.format(key, value)) + + aio_logger.error('\n'.join(log_lines), exc_info=exc_info) + + def get_exception_handler(self): + """Return an exception handler, or None if the default one is in use. + """ + return self._exception_handler + + def set_exception_handler(self, handler): + """Set handler as the new event loop exception handler. + + If handler is None, the default exception handler will + be set. + + If handler is a callable object, it should have a + signature matching '(loop, context)', where 'loop' + will be a reference to the active event loop, 'context' + will be a dict object (see `call_exception_handler()` + documentation for details about context). + """ + if handler is not None and not callable(handler): + raise TypeError('A callable object or None is expected, ' + 'got {!r}'.format(handler)) + self._exception_handler = handler + + def call_exception_handler(self, context): + """Call the current event loop's exception handler. + + The context argument is a dict containing the following keys: + + - 'message': Error message; + - 'exception' (optional): Exception object; + - 'future' (optional): Future instance; + - 'handle' (optional): Handle instance; + - 'protocol' (optional): Protocol instance; + - 'transport' (optional): Transport instance; + - 'socket' (optional): Socket instance. + + New keys maybe introduced in the future. + + Note: do not overload this method in an event loop subclass. + For custom exception handling, use the + `set_exception_handler()` method. + """ + if UVLOOP_DEBUG: + self._debug_exception_handler_cnt += 1 + + if self._exception_handler is None: + try: + self.default_exception_handler(context) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + # Second protection layer for unexpected errors + # in the default implementation, as well as for subclassed + # event loops with overloaded "default_exception_handler". + aio_logger.error('Exception in default exception handler', + exc_info=True) + else: + try: + self._exception_handler(self, context) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + # Exception in the user set custom exception handler. + try: + # Let's try default handler. + self.default_exception_handler({ + 'message': 'Unhandled error in exception handler', + 'exception': exc, + 'context': context, + }) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + # Guard 'default_exception_handler' in case it is + # overloaded. + aio_logger.error('Exception in default exception handler ' + 'while handling an unexpected error ' + 'in custom exception handler', + exc_info=True) + + def add_reader(self, fileobj, callback, *args): + """Add a reader callback.""" + if len(args) == 0: + args = None + self._add_reader(fileobj, new_Handle(self, callback, args, None)) + + def remove_reader(self, fileobj): + """Remove a reader callback.""" + self._remove_reader(fileobj) + + def add_writer(self, fileobj, callback, *args): + """Add a writer callback..""" + if len(args) == 0: + args = None + self._add_writer(fileobj, new_Handle(self, callback, args, None)) + + def remove_writer(self, fileobj): + """Remove a writer callback.""" + self._remove_writer(fileobj) + + @cython.iterable_coroutine + async def sock_recv(self, sock, n): + """Receive data from the socket. + + The return value is a bytes object representing the data received. + The maximum amount of data to be received at once is specified by + nbytes. + + This method is a coroutine. + """ + cdef: + Handle handle + + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + + fut = _SyncSocketReaderFuture(sock, self) + handle = new_MethodHandle3( + self, + "Loop._sock_recv", + self._sock_recv, + None, + self, + fut, sock, n) + + self._add_reader(sock, handle) + return await fut + + @cython.iterable_coroutine + async def sock_recv_into(self, sock, buf): + """Receive data from the socket. + + The received data is written into *buf* (a writable buffer). + The return value is the number of bytes written. + + This method is a coroutine. + """ + cdef: + Handle handle + + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + + fut = _SyncSocketReaderFuture(sock, self) + handle = new_MethodHandle3( + self, + "Loop._sock_recv_into", + self._sock_recv_into, + None, + self, + fut, sock, buf) + + self._add_reader(sock, handle) + return await fut + + @cython.iterable_coroutine + async def sock_sendall(self, sock, data): + """Send data to the socket. + + The socket must be connected to a remote socket. This method continues + to send data from data until either all data has been sent or an + error occurs. None is returned on success. On error, an exception is + raised, and there is no way to determine how much data, if any, was + successfully processed by the receiving end of the connection. + + This method is a coroutine. + """ + cdef: + Handle handle + ssize_t n + + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + + if not data: + return + + socket_inc_io_ref(sock) + try: + try: + n = sock.send(data) + except (BlockingIOError, InterruptedError): + pass + else: + if UVLOOP_DEBUG: + # This can be a partial success, i.e. only part + # of the data was sent + self._sock_try_write_total += 1 + + if n == len(data): + return + if not isinstance(data, memoryview): + data = memoryview(data) + data = data[n:] + + fut = _SyncSocketWriterFuture(sock, self) + handle = new_MethodHandle3( + self, + "Loop._sock_sendall", + self._sock_sendall, + None, + self, + fut, sock, data) + + self._add_writer(sock, handle) + return await fut + finally: + socket_dec_io_ref(sock) + + @cython.iterable_coroutine + async def sock_accept(self, sock): + """Accept a connection. + + The socket must be bound to an address and listening for connections. + The return value is a pair (conn, address) where conn is a new socket + object usable to send and receive data on the connection, and address + is the address bound to the socket on the other end of the connection. + + This method is a coroutine. + """ + cdef: + Handle handle + + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + + fut = _SyncSocketReaderFuture(sock, self) + handle = new_MethodHandle2( + self, + "Loop._sock_accept", + self._sock_accept, + None, + self, + fut, sock) + + self._add_reader(sock, handle) + return await fut + + @cython.iterable_coroutine + async def sock_connect(self, sock, address): + """Connect to a remote socket at address. + + This method is a coroutine. + """ + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + + socket_inc_io_ref(sock) + try: + if sock.family == uv.AF_UNIX: + fut = self._sock_connect(sock, address) + else: + addrs = await self.getaddrinfo( + *address[:2], family=sock.family) + + _, _, _, _, address = addrs[0] + fut = self._sock_connect(sock, address) + if fut is not None: + await fut + finally: + socket_dec_io_ref(sock) + + @cython.iterable_coroutine + async def sock_recvfrom(self, sock, bufsize): + raise NotImplementedError + + @cython.iterable_coroutine + async def sock_recvfrom_into(self, sock, buf, nbytes=0): + raise NotImplementedError + + @cython.iterable_coroutine + async def sock_sendto(self, sock, data, address): + raise NotImplementedError + + @cython.iterable_coroutine + async def connect_accepted_socket(self, protocol_factory, sock, *, + ssl=None, + ssl_handshake_timeout=None, + ssl_shutdown_timeout=None): + """Handle an accepted connection. + + This is used by servers that accept connections outside of + asyncio but that use asyncio to handle connections. + + This method is a coroutine. When completed, the coroutine + returns a (transport, protocol) pair. + """ + + cdef: + UVStream transport = None + + if ssl is not None: + if not isinstance(ssl, ssl_SSLContext): + raise TypeError('ssl argument must be an SSLContext or None') + else: + if ssl_handshake_timeout is not None: + raise ValueError( + 'ssl_handshake_timeout is only meaningful with ssl') + if ssl_shutdown_timeout is not None: + raise ValueError( + 'ssl_shutdown_timeout is only meaningful with ssl') + + if not _is_sock_stream(sock.type): + raise ValueError( + 'A Stream Socket was expected, got {!r}'.format(sock)) + + app_protocol = protocol_factory() + waiter = self._new_future() + transport_waiter = None + context = Context_CopyCurrent() + + if ssl is None: + protocol = app_protocol + transport_waiter = waiter + else: + protocol = SSLProtocol( + self, app_protocol, ssl, waiter, + server_side=True, + server_hostname=None, + ssl_handshake_timeout=ssl_handshake_timeout, + ssl_shutdown_timeout=ssl_shutdown_timeout) + transport_waiter = None + + if sock.family == uv.AF_UNIX: + transport = UnixTransport.new( + self, protocol, None, transport_waiter, context) + elif sock.family in (uv.AF_INET, uv.AF_INET6): + transport = TCPTransport.new( + self, protocol, None, transport_waiter, context) + + if transport is None: + raise ValueError( + 'invalid socket family, expected AF_UNIX, AF_INET or AF_INET6') + + transport._open(sock.fileno()) + transport._init_protocol() + transport._attach_fileobj(sock) + + if ssl: + app_transport = protocol._get_app_transport(context) + try: + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + app_transport.close() + raise + return app_transport, protocol + else: + try: + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + transport._close() + raise + return transport, protocol + + def run_in_executor(self, executor, func, *args): + if aio_iscoroutine(func) or aio_iscoroutinefunction(func): + raise TypeError("coroutines cannot be used with run_in_executor()") + + self._check_closed() + + if executor is None: + executor = self._default_executor + # Only check when the default executor is being used + self._check_default_executor() + if executor is None: + executor = cc_ThreadPoolExecutor() + self._default_executor = executor + + return aio_wrap_future(executor.submit(func, *args), loop=self) + + def set_default_executor(self, executor): + self._default_executor = executor + + @cython.iterable_coroutine + async def __subprocess_run(self, protocol_factory, args, + stdin=subprocess_PIPE, + stdout=subprocess_PIPE, + stderr=subprocess_PIPE, + universal_newlines=False, + shell=True, + bufsize=0, + preexec_fn=None, + close_fds=None, + cwd=None, + env=None, + startupinfo=None, + creationflags=0, + restore_signals=True, + start_new_session=False, + executable=None, + pass_fds=(), + # For tests only! Do not use in your code. Ever. + __uvloop_sleep_after_fork=False): + + # TODO: Implement close_fds (might not be very important in + # Python 3.5, since all FDs aren't inheritable by default.) + + cdef: + int debug_flags = 0 + + if universal_newlines: + raise ValueError("universal_newlines must be False") + if bufsize != 0: + raise ValueError("bufsize must be 0") + if startupinfo is not None: + raise ValueError('startupinfo is not supported') + if creationflags != 0: + raise ValueError('creationflags is not supported') + + if executable is not None: + args[0] = executable + + if __uvloop_sleep_after_fork: + debug_flags |= __PROCESS_DEBUG_SLEEP_AFTER_FORK + + waiter = self._new_future() + protocol = protocol_factory() + proc = UVProcessTransport.new(self, protocol, + args, env, cwd, start_new_session, + stdin, stdout, stderr, pass_fds, + waiter, + debug_flags, + preexec_fn, + restore_signals) + + try: + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + proc.close() + raise + + return proc, protocol + + @cython.iterable_coroutine + async def subprocess_shell(self, protocol_factory, cmd, *, + shell=True, + **kwargs): + + if not shell: + raise ValueError("shell must be True") + + args = [cmd] + if shell: + args = [b'/bin/sh', b'-c'] + args + + return await self.__subprocess_run(protocol_factory, args, shell=True, + **kwargs) + + @cython.iterable_coroutine + async def subprocess_exec(self, protocol_factory, program, *args, + shell=False, **kwargs): + + if shell: + raise ValueError("shell must be False") + + args = list((program,) + args) + + return await self.__subprocess_run(protocol_factory, args, shell=False, + **kwargs) + + @cython.iterable_coroutine + async def connect_read_pipe(self, proto_factory, pipe): + """Register read pipe in event loop. Set the pipe to non-blocking mode. + + protocol_factory should instantiate object with Protocol interface. + pipe is a file-like object. + Return pair (transport, protocol), where transport supports the + ReadTransport interface.""" + cdef: + ReadUnixTransport transp + + waiter = self._new_future() + proto = proto_factory() + transp = ReadUnixTransport.new(self, proto, None, waiter) + transp._add_extra_info('pipe', pipe) + try: + transp._open(pipe.fileno()) + transp._init_protocol() + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + transp._close() + raise + transp._attach_fileobj(pipe) + return transp, proto + + @cython.iterable_coroutine + async def connect_write_pipe(self, proto_factory, pipe): + """Register write pipe in event loop. + + protocol_factory should instantiate object with BaseProtocol interface. + Pipe is file-like object already switched to nonblocking. + Return pair (transport, protocol), where transport support + WriteTransport interface.""" + cdef: + WriteUnixTransport transp + + waiter = self._new_future() + proto = proto_factory() + transp = WriteUnixTransport.new(self, proto, None, waiter) + transp._add_extra_info('pipe', pipe) + try: + transp._open(pipe.fileno()) + transp._init_protocol() + await waiter + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + transp._close() + raise + transp._attach_fileobj(pipe) + return transp, proto + + def add_signal_handler(self, sig, callback, *args): + """Add a handler for a signal. UNIX only. + + Raise ValueError if the signal number is invalid or uncatchable. + Raise RuntimeError if there is a problem setting up the handler. + """ + cdef: + Handle h + + if not self._is_main_thread(): + raise ValueError( + 'add_signal_handler() can only be called from ' + 'the main thread') + + if (aio_iscoroutine(callback) + or aio_iscoroutinefunction(callback)): + raise TypeError( + "coroutines cannot be used with add_signal_handler()") + + if sig == uv.SIGCHLD: + if (hasattr(callback, '__self__') and + isinstance(callback.__self__, aio_AbstractChildWatcher)): + + warnings_warn( + "!!! asyncio is trying to install its ChildWatcher for " + "SIGCHLD signal !!!\n\nThis is probably because a uvloop " + "instance is used with asyncio.set_event_loop(). " + "The correct way to use uvloop is to install its policy: " + "`asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())`" + "\n\n", RuntimeWarning, source=self) + + # TODO: ideally we should always raise an error here, + # but that would be a backwards incompatible change, + # because we recommended using "asyncio.set_event_loop()" + # in our README. Need to start a deprecation period + # at some point to turn this warning into an error. + return + + raise RuntimeError( + 'cannot add a signal handler for SIGCHLD: it is used ' + 'by the event loop to track subprocesses') + + self._check_signal(sig) + self._check_closed() + + h = new_Handle(self, callback, args or None, None) + self._signal_handlers[sig] = h + + try: + # Register a dummy signal handler to ask Python to write the signal + # number in the wakeup file descriptor. + signal_signal(sig, self.__sighandler) + + # Set SA_RESTART to limit EINTR occurrences. + signal_siginterrupt(sig, False) + except OSError as exc: + del self._signal_handlers[sig] + if not self._signal_handlers: + try: + signal_set_wakeup_fd(-1) + except (ValueError, OSError) as nexc: + aio_logger.info('set_wakeup_fd(-1) failed: %s', nexc) + + if exc.errno == errno_EINVAL: + raise RuntimeError('sig {} cannot be caught'.format(sig)) + else: + raise + + def remove_signal_handler(self, sig): + """Remove a handler for a signal. UNIX only. + + Return True if a signal handler was removed, False if not. + """ + + if not self._is_main_thread(): + raise ValueError( + 'remove_signal_handler() can only be called from ' + 'the main thread') + + self._check_signal(sig) + + if not self._listening_signals: + return False + + try: + del self._signal_handlers[sig] + except KeyError: + return False + + if sig == uv.SIGINT: + handler = signal_default_int_handler + else: + handler = signal_SIG_DFL + + try: + signal_signal(sig, handler) + except OSError as exc: + if exc.errno == errno_EINVAL: + raise RuntimeError('sig {} cannot be caught'.format(sig)) + else: + raise + + return True + + @cython.iterable_coroutine + async def create_datagram_endpoint(self, protocol_factory, + local_addr=None, remote_addr=None, *, + family=0, proto=0, flags=0, + reuse_address=_unset, reuse_port=None, + allow_broadcast=None, sock=None): + """A coroutine which creates a datagram endpoint. + + This method will try to establish the endpoint in the background. + When successful, the coroutine returns a (transport, protocol) pair. + + protocol_factory must be a callable returning a protocol instance. + + socket family AF_INET or socket.AF_INET6 depending on host (or + family if specified), socket type SOCK_DGRAM. + + reuse_port tells the kernel to allow this endpoint to be bound to + the same port as other existing endpoints are bound to, so long as + they all set this flag when being created. This option is not + supported on Windows and some UNIX's. If the + :py:data:`~socket.SO_REUSEPORT` constant is not defined then this + capability is unsupported. + + allow_broadcast tells the kernel to allow this endpoint to send + messages to the broadcast address. + + sock can optionally be specified in order to use a preexisting + socket object. + """ + cdef: + UDPTransport udp = None + system.addrinfo * lai + system.addrinfo * rai + + if sock is not None: + if not _is_sock_dgram(sock.type): + raise ValueError( + 'A UDP Socket was expected, got {!r}'.format(sock)) + if (local_addr or remote_addr or + family or proto or flags or + reuse_port or allow_broadcast): + # show the problematic kwargs in exception msg + opts = dict(local_addr=local_addr, remote_addr=remote_addr, + family=family, proto=proto, flags=flags, + reuse_address=reuse_address, reuse_port=reuse_port, + allow_broadcast=allow_broadcast) + problems = ', '.join( + '{}={}'.format(k, v) for k, v in opts.items() if v) + raise ValueError( + 'socket modifier keyword arguments can not be used ' + 'when sock is specified. ({})'.format(problems)) + sock.setblocking(False) + udp = UDPTransport.__new__(UDPTransport) + udp._init(self, uv.AF_UNSPEC) + udp.open(sock.family, sock.fileno()) + udp._attach_fileobj(sock) + else: + if reuse_address is not _unset: + if reuse_address: + raise ValueError("Passing `reuse_address=True` is no " + "longer supported, as the usage of " + "SO_REUSEPORT in UDP poses a significant " + "security concern.") + else: + warnings_warn("The *reuse_address* parameter has been " + "deprecated as of 0.15.", DeprecationWarning, + stacklevel=2) + reuse_port = bool(reuse_port) + if reuse_port and not has_SO_REUSEPORT: + raise ValueError( + 'reuse_port not supported by socket module') + + lads = None + if local_addr is not None: + if (not isinstance(local_addr, (tuple, list)) or + len(local_addr) != 2): + raise TypeError( + 'local_addr must be a tuple of (host, port)') + lads = await self._getaddrinfo( + local_addr[0], local_addr[1], + family, uv.SOCK_DGRAM, proto, flags, + 0) + + rads = None + if remote_addr is not None: + if (not isinstance(remote_addr, (tuple, list)) or + len(remote_addr) != 2): + raise TypeError( + 'remote_addr must be a tuple of (host, port)') + rads = await self._getaddrinfo( + remote_addr[0], remote_addr[1], + family, uv.SOCK_DGRAM, proto, flags, + 0) + + excs = [] + if lads is None: + if rads is not None: + udp = UDPTransport.__new__(UDPTransport) + rai = (rads).data + udp._init(self, rai.ai_family) + udp._connect(rai.ai_addr, rai.ai_addrlen) + udp._set_address(rai) + else: + if family not in (uv.AF_INET, uv.AF_INET6): + raise ValueError('unexpected address family') + udp = UDPTransport.__new__(UDPTransport) + udp._init(self, family) + + if reuse_port: + self._sock_set_reuseport(udp._fileno()) + + else: + lai = (lads).data + while lai is not NULL: + try: + udp = UDPTransport.__new__(UDPTransport) + udp._init(self, lai.ai_family) + if reuse_port: + self._sock_set_reuseport(udp._fileno()) + udp._bind(lai.ai_addr) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + lai = lai.ai_next + excs.append(ex) + continue + else: + break + else: + ctx = None + if len(excs): + ctx = excs[0] + raise OSError('could not bind to local_addr {}'.format( + local_addr)) from ctx + + if rads is not None: + rai = (rads).data + while rai is not NULL: + if rai.ai_family != lai.ai_family: + rai = rai.ai_next + continue + if rai.ai_protocol != lai.ai_protocol: + rai = rai.ai_next + continue + udp._connect(rai.ai_addr, rai.ai_addrlen) + udp._set_address(rai) + break + else: + raise OSError( + 'could not bind to remote_addr {}'.format( + remote_addr)) + + if allow_broadcast: + udp._set_broadcast(1) + + protocol = protocol_factory() + waiter = self._new_future() + assert udp is not None + udp._set_protocol(protocol) + udp._set_waiter(waiter) + udp._init_protocol() + + await waiter + return udp, protocol + + def _monitor_fs(self, path: str, callback) -> asyncio.Handle: + cdef: + UVFSEvent fs_handle + char* c_str_path + + self._check_closed() + fs_handle = UVFSEvent.new(self, callback, None) + p_bytes = path.encode('UTF-8') + c_str_path = p_bytes + flags = 0 + fs_handle.start(c_str_path, flags) + return fs_handle + + def _check_default_executor(self): + if self._executor_shutdown_called: + raise RuntimeError('Executor shutdown has been called') + + def _asyncgen_finalizer_hook(self, agen): + self._asyncgens.discard(agen) + if not self.is_closed(): + self.call_soon_threadsafe(self.create_task, agen.aclose()) + + def _asyncgen_firstiter_hook(self, agen): + if self._asyncgens_shutdown_called: + warnings_warn( + "asynchronous generator {!r} was scheduled after " + "loop.shutdown_asyncgens() call".format(agen), + ResourceWarning, source=self) + + self._asyncgens.add(agen) + + @cython.iterable_coroutine + async def shutdown_asyncgens(self): + """Shutdown all active asynchronous generators.""" + self._asyncgens_shutdown_called = True + + if not len(self._asyncgens): + return + + closing_agens = list(self._asyncgens) + self._asyncgens.clear() + + shutdown_coro = aio_gather( + *[ag.aclose() for ag in closing_agens], + return_exceptions=True) + + results = await shutdown_coro + for result, agen in zip(results, closing_agens): + if isinstance(result, Exception): + self.call_exception_handler({ + 'message': 'an error occurred during closing of ' + 'asynchronous generator {!r}'.format(agen), + 'exception': result, + 'asyncgen': agen + }) + + @cython.iterable_coroutine + async def shutdown_default_executor(self, timeout=None): + """Schedule the shutdown of the default executor. + + The timeout parameter specifies the amount of time the executor will + be given to finish joining. The default value is None, which means + that the executor will be given an unlimited amount of time. + """ + self._executor_shutdown_called = True + if self._default_executor is None: + return + future = self.create_future() + thread = threading_Thread(target=self._do_shutdown, args=(future,)) + thread.start() + try: + await future + finally: + thread.join(timeout) + + if thread.is_alive(): + warnings_warn( + "The executor did not finishing joining " + f"its threads within {timeout} seconds.", + RuntimeWarning, + stacklevel=2 + ) + self._default_executor.shutdown(wait=False) + + def _do_shutdown(self, future): + try: + self._default_executor.shutdown(wait=True) + self.call_soon_threadsafe(future.set_result, None) + except Exception as ex: + self.call_soon_threadsafe(future.set_exception, ex) + + +# Expose pointer for integration with other C-extensions +def libuv_get_loop_t_ptr(loop): + return PyCapsule_New((loop).uvloop, NULL, NULL) + + +def libuv_get_version(): + return uv.uv_version() + + +def _testhelper_unwrap_capsuled_pointer(obj): + return PyCapsule_GetPointer(obj, NULL) + + +cdef void __loop_alloc_buffer( + uv.uv_handle_t* uvhandle, + size_t suggested_size, + uv.uv_buf_t* buf +) noexcept with gil: + cdef: + Loop loop = (uvhandle.data)._loop + + if loop._recv_buffer_in_use == 1: + buf.len = 0 + exc = RuntimeError('concurrent allocations') + loop._handle_exception(exc) + return + + loop._recv_buffer_in_use = 1 + buf.base = loop._recv_buffer + buf.len = sizeof(loop._recv_buffer) + + +cdef inline void __loop_free_buffer(Loop loop): + loop._recv_buffer_in_use = 0 + + +class _SyncSocketReaderFuture(aio_Future): + + def __init__(self, sock, loop): + aio_Future.__init__(self, loop=loop) + self.__sock = sock + self.__loop = loop + + def __remove_reader(self): + if self.__sock is not None and self.__sock.fileno() != -1: + self.__loop.remove_reader(self.__sock) + self.__sock = None + + if PY39: + def cancel(self, msg=None): + self.__remove_reader() + aio_Future.cancel(self, msg=msg) + + else: + def cancel(self): + self.__remove_reader() + aio_Future.cancel(self) + + +class _SyncSocketWriterFuture(aio_Future): + + def __init__(self, sock, loop): + aio_Future.__init__(self, loop=loop) + self.__sock = sock + self.__loop = loop + + def __remove_writer(self): + if self.__sock is not None and self.__sock.fileno() != -1: + self.__loop.remove_writer(self.__sock) + self.__sock = None + + if PY39: + def cancel(self, msg=None): + self.__remove_writer() + aio_Future.cancel(self, msg=msg) + + else: + def cancel(self): + self.__remove_writer() + aio_Future.cancel(self) + + +include "cbhandles.pyx" +include "pseudosock.pyx" +include "lru.pyx" + +include "handles/handle.pyx" +include "handles/async_.pyx" +include "handles/idle.pyx" +include "handles/check.pyx" +include "handles/timer.pyx" +include "handles/poll.pyx" +include "handles/basetransport.pyx" +include "handles/stream.pyx" +include "handles/streamserver.pyx" +include "handles/tcp.pyx" +include "handles/pipe.pyx" +include "handles/process.pyx" +include "handles/fsevent.pyx" + +include "request.pyx" +include "dns.pyx" +include "sslproto.pyx" + +include "handles/udp.pyx" + +include "server.pyx" + + +# Used in UVProcess +cdef vint __atfork_installed = 0 +cdef vint __forking = 0 +cdef Loop __forking_loop = None + + +cdef void __get_fork_handler() noexcept nogil: + with gil: + if (__forking and __forking_loop is not None and + __forking_loop.active_process_handler is not None): + __forking_loop.active_process_handler._after_fork() + +cdef __install_atfork(): + global __atfork_installed + + if __atfork_installed: + return + __atfork_installed = 1 + + cdef int err + + err = system.pthread_atfork(NULL, NULL, &system.handleAtFork) + if err: + __atfork_installed = 0 + raise convert_error(-err) + + +# Install PyMem* memory allocators +cdef vint __mem_installed = 0 +cdef __install_pymem(): + global __mem_installed + if __mem_installed: + return + __mem_installed = 1 + + cdef int err + err = uv.uv_replace_allocator(PyMem_RawMalloc, + PyMem_RawRealloc, + PyMem_RawCalloc, + PyMem_RawFree) + if err < 0: + __mem_installed = 0 + raise convert_error(err) + + +cdef _set_signal_wakeup_fd(fd): + if fd >= 0: + return signal_set_wakeup_fd(fd, warn_on_full_buffer=False) + else: + return signal_set_wakeup_fd(fd) + + +# Helpers for tests + +@cython.iterable_coroutine +async def _test_coroutine_1(): + return 42 diff --git a/venv/lib/python3.11/site-packages/uvloop/lru.pyx b/venv/lib/python3.11/site-packages/uvloop/lru.pyx new file mode 100644 index 0000000..cc7ea1d --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/lru.pyx @@ -0,0 +1,79 @@ +cdef object _LRU_MARKER = object() + + +@cython.final +cdef class LruCache: + + cdef: + object _dict + int _maxsize + object _dict_move_to_end + object _dict_get + + # We use an OrderedDict for LRU implementation. Operations: + # + # * We use a simple `__setitem__` to push a new entry: + # `entries[key] = new_entry` + # That will push `new_entry` to the *end* of the entries dict. + # + # * When we have a cache hit, we call + # `entries.move_to_end(key, last=True)` + # to move the entry to the *end* of the entries dict. + # + # * When we need to remove entries to maintain `max_size`, we call + # `entries.popitem(last=False)` + # to remove an entry from the *beginning* of the entries dict. + # + # So new entries and hits are always promoted to the end of the + # entries dict, whereas the unused one will group in the + # beginning of it. + + def __init__(self, *, maxsize): + if maxsize <= 0: + raise ValueError( + f'maxsize is expected to be greater than 0, got {maxsize}') + + self._dict = col_OrderedDict() + self._dict_move_to_end = self._dict.move_to_end + self._dict_get = self._dict.get + self._maxsize = maxsize + + cdef get(self, key, default): + o = self._dict_get(key, _LRU_MARKER) + if o is _LRU_MARKER: + return default + self._dict_move_to_end(key) # last=True + return o + + cdef inline needs_cleanup(self): + return len(self._dict) > self._maxsize + + cdef inline cleanup_one(self): + k, _ = self._dict.popitem(last=False) + return k + + def __getitem__(self, key): + o = self._dict[key] + self._dict_move_to_end(key) # last=True + return o + + def __setitem__(self, key, o): + if key in self._dict: + self._dict[key] = o + self._dict_move_to_end(key) # last=True + else: + self._dict[key] = o + while self.needs_cleanup(): + self.cleanup_one() + + def __delitem__(self, key): + del self._dict[key] + + def __contains__(self, key): + return key in self._dict + + def __len__(self): + return len(self._dict) + + def __iter__(self): + return iter(self._dict) diff --git a/venv/lib/python3.11/site-packages/uvloop/pseudosock.pyx b/venv/lib/python3.11/site-packages/uvloop/pseudosock.pyx new file mode 100644 index 0000000..10a1ad6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/pseudosock.pyx @@ -0,0 +1,209 @@ +cdef class PseudoSocket: + cdef: + int _family + int _type + int _proto + int _fd + object _peername + object _sockname + + def __init__(self, int family, int type, int proto, int fd): + self._family = family + self._type = type + self._proto = proto + self._fd = fd + self._peername = None + self._sockname = None + + cdef _na(self, what): + raise TypeError('transport sockets do not support {}'.format(what)) + + cdef _make_sock(self): + return socket_socket(self._family, self._type, self._proto, self._fd) + + property family: + def __get__(self): + try: + return socket_AddressFamily(self._family) + except ValueError: + return self._family + + property type: + def __get__(self): + try: + return socket_SocketKind(self._type) + except ValueError: + return self._type + + property proto: + def __get__(self): + return self._proto + + def __repr__(self): + s = ("self.request.data is not self: + raise RuntimeError( + '{}.cancel: .request.data is not UVRequest'.format( + self.__class__.__name__)) + + # We only can cancel pending requests. Let's try. + err = uv.uv_cancel(self.request) + if err < 0: + if err == uv.UV_EBUSY: + # Can't close the request -- it's executing (see the first + # comment). Loop will have to wait until the callback + # fires. + pass + elif err == uv.UV_EINVAL: + # From libuv docs: + # + # Only cancellation of uv_fs_t, uv_getaddrinfo_t, + # uv_getnameinfo_t and uv_work_t requests is currently + # supported. + return + else: + ex = convert_error(err) + self.loop._handle_exception(ex) diff --git a/venv/lib/python3.11/site-packages/uvloop/server.pxd b/venv/lib/python3.11/site-packages/uvloop/server.pxd new file mode 100644 index 0000000..ef10f81 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/server.pxd @@ -0,0 +1,19 @@ +cdef class Server: + cdef: + list _servers + list _waiters + int _active_count + Loop _loop + bint _serving + object _serving_forever_fut + object __weakref__ + + cdef _add_server(self, UVStreamServer srv) + cdef _start_serving(self) + cdef _wakeup(self) + + cdef _attach(self) + cdef _detach(self) + + cdef _ref(self) + cdef _unref(self) diff --git a/venv/lib/python3.11/site-packages/uvloop/server.pyx b/venv/lib/python3.11/site-packages/uvloop/server.pyx new file mode 100644 index 0000000..845bcfd --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/server.pyx @@ -0,0 +1,136 @@ +import asyncio + + +cdef class Server: + def __cinit__(self, Loop loop): + self._loop = loop + self._servers = [] + self._waiters = [] + self._active_count = 0 + self._serving_forever_fut = None + + cdef _add_server(self, UVStreamServer srv): + self._servers.append(srv) + + cdef _start_serving(self): + if self._serving: + return + + self._serving = 1 + for server in self._servers: + (server).listen() + + cdef _wakeup(self): + cdef list waiters + + waiters = self._waiters + self._waiters = None + for waiter in waiters: + if not waiter.done(): + waiter.set_result(waiter) + + cdef _attach(self): + assert self._servers is not None + self._active_count += 1 + + cdef _detach(self): + assert self._active_count > 0 + self._active_count -= 1 + if self._active_count == 0 and self._servers is None: + self._wakeup() + + cdef _ref(self): + # Keep the server object alive while it's not explicitly closed. + self._loop._servers.add(self) + + cdef _unref(self): + self._loop._servers.discard(self) + + # Public API + + @cython.iterable_coroutine + async def __aenter__(self): + return self + + @cython.iterable_coroutine + async def __aexit__(self, *exc): + self.close() + await self.wait_closed() + + def __repr__(self): + return '<%s sockets=%r>' % (self.__class__.__name__, self.sockets) + + def get_loop(self): + return self._loop + + @cython.iterable_coroutine + async def wait_closed(self): + # Do not remove `self._servers is None` below + # because close() method only closes server sockets + # and existing client connections are left open. + if self._servers is None or self._waiters is None: + return + waiter = self._loop._new_future() + self._waiters.append(waiter) + await waiter + + def close(self): + cdef list servers + + if self._servers is None: + return + + try: + servers = self._servers + self._servers = None + self._serving = 0 + + for server in servers: + (server)._close() + + if self._active_count == 0: + self._wakeup() + finally: + self._unref() + + def is_serving(self): + return self._serving + + @cython.iterable_coroutine + async def start_serving(self): + self._start_serving() + + @cython.iterable_coroutine + async def serve_forever(self): + if self._serving_forever_fut is not None: + raise RuntimeError( + f'server {self!r} is already being awaited on serve_forever()') + if self._servers is None: + raise RuntimeError(f'server {self!r} is closed') + + self._start_serving() + self._serving_forever_fut = self._loop.create_future() + + try: + await self._serving_forever_fut + except asyncio.CancelledError: + try: + self.close() + await self.wait_closed() + finally: + raise + finally: + self._serving_forever_fut = None + + property sockets: + def __get__(self): + cdef list sockets = [] + + # Guard against `self._servers is None` + if self._servers: + for server in self._servers: + sockets.append( + (server)._get_socket() + ) + + return sockets diff --git a/venv/lib/python3.11/site-packages/uvloop/sslproto.pxd b/venv/lib/python3.11/site-packages/uvloop/sslproto.pxd new file mode 100644 index 0000000..3da10f0 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/sslproto.pxd @@ -0,0 +1,138 @@ +cdef enum SSLProtocolState: + UNWRAPPED = 0 + DO_HANDSHAKE = 1 + WRAPPED = 2 + FLUSHING = 3 + SHUTDOWN = 4 + + +cdef enum AppProtocolState: + # This tracks the state of app protocol (https://git.io/fj59P): + # + # INIT -cm-> CON_MADE [-dr*->] [-er-> EOF?] -cl-> CON_LOST + # + # * cm: connection_made() + # * dr: data_received() + # * er: eof_received() + # * cl: connection_lost() + + STATE_INIT = 0 + STATE_CON_MADE = 1 + STATE_EOF = 2 + STATE_CON_LOST = 3 + + +cdef class _SSLProtocolTransport: + cdef: + Loop _loop + SSLProtocol _ssl_protocol + bint _closed + object context + + +cdef class SSLProtocol: + cdef: + bint _server_side + str _server_hostname + object _sslcontext + + object _extra + + object _write_backlog + size_t _write_buffer_size + + object _waiter + Loop _loop + _SSLProtocolTransport _app_transport + bint _app_transport_created + + object _transport + object _ssl_handshake_timeout + object _ssl_shutdown_timeout + + object _sslobj + object _sslobj_read + object _sslobj_write + object _incoming + object _incoming_write + object _outgoing + object _outgoing_read + char* _ssl_buffer + size_t _ssl_buffer_len + object _ssl_buffer_view + SSLProtocolState _state + size_t _conn_lost + AppProtocolState _app_state + + bint _ssl_writing_paused + bint _app_reading_paused + + size_t _incoming_high_water + size_t _incoming_low_water + bint _ssl_reading_paused + + bint _app_writing_paused + size_t _outgoing_high_water + size_t _outgoing_low_water + + object _app_protocol + bint _app_protocol_is_buffer + object _app_protocol_get_buffer + object _app_protocol_buffer_updated + + object _handshake_start_time + object _handshake_timeout_handle + object _shutdown_timeout_handle + + cdef _set_app_protocol(self, app_protocol) + cdef _wakeup_waiter(self, exc=*) + cdef _get_extra_info(self, name, default=*) + cdef _set_state(self, SSLProtocolState new_state) + + # Handshake flow + + cdef _start_handshake(self) + cdef _check_handshake_timeout(self) + cdef _do_handshake(self) + cdef _on_handshake_complete(self, handshake_exc) + + # Shutdown flow + + cdef _start_shutdown(self, object context=*) + cdef _check_shutdown_timeout(self) + cdef _do_read_into_void(self, object context) + cdef _do_flush(self, object context=*) + cdef _do_shutdown(self, object context=*) + cdef _on_shutdown_complete(self, shutdown_exc) + cdef _abort(self, exc) + + # Outgoing flow + + cdef _write_appdata(self, list_of_data, object context) + cdef _do_write(self) + cdef _process_outgoing(self) + + # Incoming flow + + cdef _do_read(self) + cdef _do_read__buffered(self) + cdef _do_read__copied(self) + cdef _call_eof_received(self, object context=*) + + # Flow control for writes from APP socket + + cdef _control_app_writing(self, object context=*) + cdef size_t _get_write_buffer_size(self) + cdef _set_write_buffer_limits(self, high=*, low=*) + + # Flow control for reads to APP socket + + cdef _pause_reading(self) + cdef _resume_reading(self, object context) + + # Flow control for reads from SSL socket + + cdef _control_ssl_reading(self) + cdef _set_read_buffer_limits(self, high=*, low=*) + cdef size_t _get_read_buffer_size(self) + cdef _fatal_error(self, exc, message=*) diff --git a/venv/lib/python3.11/site-packages/uvloop/sslproto.pyx b/venv/lib/python3.11/site-packages/uvloop/sslproto.pyx new file mode 100644 index 0000000..42bb764 --- /dev/null +++ b/venv/lib/python3.11/site-packages/uvloop/sslproto.pyx @@ -0,0 +1,950 @@ +cdef _create_transport_context(server_side, server_hostname): + if server_side: + raise ValueError('Server side SSL needs a valid SSLContext') + + # Client side may pass ssl=True to use a default + # context; in that case the sslcontext passed is None. + # The default is secure for client connections. + # Python 3.4+: use up-to-date strong settings. + sslcontext = ssl_create_default_context() + if not server_hostname: + sslcontext.check_hostname = False + return sslcontext + + +cdef class _SSLProtocolTransport: + + # TODO: + # _sendfile_compatible = constants._SendfileMode.FALLBACK + + def __cinit__(self, Loop loop, ssl_protocol, context): + self._loop = loop + # SSLProtocol instance + self._ssl_protocol = ssl_protocol + self._closed = False + if context is None: + context = Context_CopyCurrent() + self.context = context + + def get_extra_info(self, name, default=None): + """Get optional transport information.""" + return self._ssl_protocol._get_extra_info(name, default) + + def set_protocol(self, protocol): + self._ssl_protocol._set_app_protocol(protocol) + + def get_protocol(self): + return self._ssl_protocol._app_protocol + + def is_closing(self): + return self._closed + + def close(self): + """Close the transport. + + Buffered data will be flushed asynchronously. No more data + will be received. After all buffered data is flushed, the + protocol's connection_lost() method will (eventually) called + with None as its argument. + """ + self._closed = True + self._ssl_protocol._start_shutdown(self.context.copy()) + + def __dealloc__(self): + if not self._closed: + self._closed = True + warnings_warn( + "unclosed transport ", ResourceWarning) + + def is_reading(self): + return not self._ssl_protocol._app_reading_paused + + def pause_reading(self): + """Pause the receiving end. + + No data will be passed to the protocol's data_received() + method until resume_reading() is called. + """ + self._ssl_protocol._pause_reading() + + def resume_reading(self): + """Resume the receiving end. + + Data received will once again be passed to the protocol's + data_received() method. + """ + self._ssl_protocol._resume_reading(self.context.copy()) + + def set_write_buffer_limits(self, high=None, low=None): + """Set the high- and low-water limits for write flow control. + + These two values control when to call the protocol's + pause_writing() and resume_writing() methods. If specified, + the low-water limit must be less than or equal to the + high-water limit. Neither value can be negative. + + The defaults are implementation-specific. If only the + high-water limit is given, the low-water limit defaults to an + implementation-specific value less than or equal to the + high-water limit. Setting high to zero forces low to zero as + well, and causes pause_writing() to be called whenever the + buffer becomes non-empty. Setting low to zero causes + resume_writing() to be called only once the buffer is empty. + Use of zero for either limit is generally sub-optimal as it + reduces opportunities for doing I/O and computation + concurrently. + """ + self._ssl_protocol._set_write_buffer_limits(high, low) + self._ssl_protocol._control_app_writing(self.context.copy()) + + def get_write_buffer_limits(self): + return (self._ssl_protocol._outgoing_low_water, + self._ssl_protocol._outgoing_high_water) + + def get_write_buffer_size(self): + """Return the current size of the write buffers.""" + return self._ssl_protocol._get_write_buffer_size() + + def set_read_buffer_limits(self, high=None, low=None): + """Set the high- and low-water limits for read flow control. + + These two values control when to call the upstream transport's + pause_reading() and resume_reading() methods. If specified, + the low-water limit must be less than or equal to the + high-water limit. Neither value can be negative. + + The defaults are implementation-specific. If only the + high-water limit is given, the low-water limit defaults to an + implementation-specific value less than or equal to the + high-water limit. Setting high to zero forces low to zero as + well, and causes pause_reading() to be called whenever the + buffer becomes non-empty. Setting low to zero causes + resume_reading() to be called only once the buffer is empty. + Use of zero for either limit is generally sub-optimal as it + reduces opportunities for doing I/O and computation + concurrently. + """ + self._ssl_protocol._set_read_buffer_limits(high, low) + self._ssl_protocol._control_ssl_reading() + + def get_read_buffer_limits(self): + return (self._ssl_protocol._incoming_low_water, + self._ssl_protocol._incoming_high_water) + + def get_read_buffer_size(self): + """Return the current size of the read buffer.""" + return self._ssl_protocol._get_read_buffer_size() + + @property + def _protocol_paused(self): + # Required for sendfile fallback pause_writing/resume_writing logic + return self._ssl_protocol._app_writing_paused + + def write(self, data): + """Write some data bytes to the transport. + + This does not block; it buffers the data and arranges for it + to be sent out asynchronously. + """ + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError(f"data: expecting a bytes-like instance, " + f"got {type(data).__name__}") + if not data: + return + self._ssl_protocol._write_appdata((data,), self.context.copy()) + + def writelines(self, list_of_data): + """Write a list (or any iterable) of data bytes to the transport. + + The default implementation concatenates the arguments and + calls write() on the result. + """ + self._ssl_protocol._write_appdata(list_of_data, self.context.copy()) + + def write_eof(self): + """Close the write end after flushing buffered data. + + This raises :exc:`NotImplementedError` right now. + """ + raise NotImplementedError + + def can_write_eof(self): + """Return True if this transport supports write_eof(), False if not.""" + return False + + def abort(self): + """Close the transport immediately. + + Buffered data will be lost. No more data will be received. + The protocol's connection_lost() method will (eventually) be + called with None as its argument. + """ + self._force_close(None) + + def _force_close(self, exc): + self._closed = True + self._ssl_protocol._abort(exc) + + def _test__append_write_backlog(self, data): + # for test only + self._ssl_protocol._write_backlog.append(data) + self._ssl_protocol._write_buffer_size += len(data) + + +cdef class SSLProtocol: + """SSL protocol. + + Implementation of SSL on top of a socket using incoming and outgoing + buffers which are ssl.MemoryBIO objects. + """ + + def __cinit__(self, *args, **kwargs): + self._ssl_buffer_len = SSL_READ_MAX_SIZE + self._ssl_buffer = PyMem_RawMalloc(self._ssl_buffer_len) + if not self._ssl_buffer: + raise MemoryError() + self._ssl_buffer_view = PyMemoryView_FromMemory( + self._ssl_buffer, self._ssl_buffer_len, PyBUF_WRITE) + + def __dealloc__(self): + self._ssl_buffer_view = None + PyMem_RawFree(self._ssl_buffer) + self._ssl_buffer = NULL + self._ssl_buffer_len = 0 + + def __init__(self, loop, app_protocol, sslcontext, waiter, + server_side=False, server_hostname=None, + call_connection_made=True, + ssl_handshake_timeout=None, + ssl_shutdown_timeout=None): + if ssl_handshake_timeout is None: + ssl_handshake_timeout = SSL_HANDSHAKE_TIMEOUT + elif ssl_handshake_timeout <= 0: + raise ValueError( + f"ssl_handshake_timeout should be a positive number, " + f"got {ssl_handshake_timeout}") + if ssl_shutdown_timeout is None: + ssl_shutdown_timeout = SSL_SHUTDOWN_TIMEOUT + elif ssl_shutdown_timeout <= 0: + raise ValueError( + f"ssl_shutdown_timeout should be a positive number, " + f"got {ssl_shutdown_timeout}") + + if not sslcontext: + sslcontext = _create_transport_context( + server_side, server_hostname) + + self._server_side = server_side + if server_hostname and not server_side: + self._server_hostname = server_hostname + else: + self._server_hostname = None + self._sslcontext = sslcontext + # SSL-specific extra info. More info are set when the handshake + # completes. + self._extra = dict(sslcontext=sslcontext) + + # App data write buffering + self._write_backlog = col_deque() + self._write_buffer_size = 0 + + self._waiter = waiter + self._loop = loop + self._set_app_protocol(app_protocol) + self._app_transport = None + self._app_transport_created = False + # transport, ex: SelectorSocketTransport + self._transport = None + self._ssl_handshake_timeout = ssl_handshake_timeout + self._ssl_shutdown_timeout = ssl_shutdown_timeout + # SSL and state machine + self._sslobj = None + self._incoming = ssl_MemoryBIO() + self._incoming_write = self._incoming.write + self._outgoing = ssl_MemoryBIO() + self._outgoing_read = self._outgoing.read + self._state = UNWRAPPED + self._conn_lost = 0 # Set when connection_lost called + if call_connection_made: + self._app_state = STATE_INIT + else: + self._app_state = STATE_CON_MADE + + # Flow Control + + self._ssl_writing_paused = False + + self._app_reading_paused = False + + self._ssl_reading_paused = False + self._incoming_high_water = 0 + self._incoming_low_water = 0 + self._set_read_buffer_limits() + + self._app_writing_paused = False + self._outgoing_high_water = 0 + self._outgoing_low_water = 0 + self._set_write_buffer_limits() + + cdef _set_app_protocol(self, app_protocol): + self._app_protocol = app_protocol + if (hasattr(app_protocol, 'get_buffer') and + not isinstance(app_protocol, aio_Protocol)): + self._app_protocol_get_buffer = app_protocol.get_buffer + self._app_protocol_buffer_updated = app_protocol.buffer_updated + self._app_protocol_is_buffer = True + else: + self._app_protocol_is_buffer = False + + cdef _wakeup_waiter(self, exc=None): + if self._waiter is None: + return + if not self._waiter.cancelled(): + if exc is not None: + self._waiter.set_exception(exc) + else: + self._waiter.set_result(None) + self._waiter = None + + def _get_app_transport(self, context=None): + if self._app_transport is None: + if self._app_transport_created: + raise RuntimeError('Creating _SSLProtocolTransport twice') + self._app_transport = _SSLProtocolTransport(self._loop, self, + context) + self._app_transport_created = True + return self._app_transport + + def connection_made(self, transport): + """Called when the low-level connection is made. + + Start the SSL handshake. + """ + self._transport = transport + self._start_handshake() + + def connection_lost(self, exc): + """Called when the low-level connection is lost or closed. + + The argument is an exception object or None (the latter + meaning a regular EOF is received or the connection was + aborted or closed). + """ + self._write_backlog.clear() + self._outgoing_read() + self._conn_lost += 1 + + # Just mark the app transport as closed so that its __dealloc__ + # doesn't complain. + if self._app_transport is not None: + self._app_transport._closed = True + + if self._state != DO_HANDSHAKE: + if self._app_state == STATE_CON_MADE or \ + self._app_state == STATE_EOF: + self._app_state = STATE_CON_LOST + self._loop.call_soon(self._app_protocol.connection_lost, exc) + self._set_state(UNWRAPPED) + self._transport = None + self._app_transport = None + self._app_protocol = None + self._wakeup_waiter(exc) + + if self._shutdown_timeout_handle: + self._shutdown_timeout_handle.cancel() + self._shutdown_timeout_handle = None + if self._handshake_timeout_handle: + self._handshake_timeout_handle.cancel() + self._handshake_timeout_handle = None + + def get_buffer(self, n): + cdef size_t want = n + if want > SSL_READ_MAX_SIZE: + want = SSL_READ_MAX_SIZE + if self._ssl_buffer_len < want: + self._ssl_buffer = PyMem_RawRealloc(self._ssl_buffer, want) + if not self._ssl_buffer: + raise MemoryError() + self._ssl_buffer_len = want + self._ssl_buffer_view = PyMemoryView_FromMemory( + self._ssl_buffer, want, PyBUF_WRITE) + return self._ssl_buffer_view + + def buffer_updated(self, nbytes): + self._incoming_write(PyMemoryView_FromMemory( + self._ssl_buffer, nbytes, PyBUF_WRITE)) + + if self._state == DO_HANDSHAKE: + self._do_handshake() + + elif self._state == WRAPPED: + self._do_read() + + elif self._state == FLUSHING: + self._do_flush() + + elif self._state == SHUTDOWN: + self._do_shutdown() + + def eof_received(self): + """Called when the other end of the low-level stream + is half-closed. + + If this returns a false value (including None), the transport + will close itself. If it returns a true value, closing the + transport is up to the protocol. + """ + try: + if self._loop.get_debug(): + aio_logger.debug("%r received EOF", self) + + if self._state == DO_HANDSHAKE: + self._on_handshake_complete(ConnectionResetError) + + elif self._state == WRAPPED or self._state == FLUSHING: + # We treat a low-level EOF as a critical situation similar to a + # broken connection - just send whatever is in the buffer and + # close. No application level eof_received() is called - + # because we don't want the user to think that this is a + # graceful shutdown triggered by SSL "close_notify". + self._set_state(SHUTDOWN) + self._on_shutdown_complete(None) + + elif self._state == SHUTDOWN: + self._on_shutdown_complete(None) + + except Exception: + self._transport.close() + raise + + cdef _get_extra_info(self, name, default=None): + if name == 'uvloop.sslproto': + return self + elif name in self._extra: + return self._extra[name] + elif self._transport is not None: + return self._transport.get_extra_info(name, default) + else: + return default + + cdef _set_state(self, SSLProtocolState new_state): + cdef bint allowed = False + + if new_state == UNWRAPPED: + allowed = True + + elif self._state == UNWRAPPED and new_state == DO_HANDSHAKE: + allowed = True + + elif self._state == DO_HANDSHAKE and new_state == WRAPPED: + allowed = True + + elif self._state == WRAPPED and new_state == FLUSHING: + allowed = True + + elif self._state == WRAPPED and new_state == SHUTDOWN: + allowed = True + + elif self._state == FLUSHING and new_state == SHUTDOWN: + allowed = True + + if allowed: + self._state = new_state + + else: + raise RuntimeError( + 'cannot switch state from {} to {}'.format( + self._state, new_state)) + + # Handshake flow + + cdef _start_handshake(self): + if self._loop.get_debug(): + aio_logger.debug("%r starts SSL handshake", self) + self._handshake_start_time = self._loop.time() + else: + self._handshake_start_time = None + + self._set_state(DO_HANDSHAKE) + + # start handshake timeout count down + self._handshake_timeout_handle = \ + self._loop.call_later(self._ssl_handshake_timeout, + lambda: self._check_handshake_timeout()) + + try: + self._sslobj = self._sslcontext.wrap_bio( + self._incoming, self._outgoing, + server_side=self._server_side, + server_hostname=self._server_hostname) + self._sslobj_read = self._sslobj.read + self._sslobj_write = self._sslobj.write + except Exception as ex: + self._on_handshake_complete(ex) + else: + self._do_handshake() + + cdef _check_handshake_timeout(self): + if self._state == DO_HANDSHAKE: + msg = ( + f"SSL handshake is taking longer than " + f"{self._ssl_handshake_timeout} seconds: " + f"aborting the connection" + ) + self._fatal_error(ConnectionAbortedError(msg)) + + cdef _do_handshake(self): + try: + self._sslobj.do_handshake() + except ssl_SSLAgainErrors as exc: + self._process_outgoing() + except ssl_SSLError as exc: + self._on_handshake_complete(exc) + else: + self._on_handshake_complete(None) + + cdef _on_handshake_complete(self, handshake_exc): + if self._handshake_timeout_handle is not None: + self._handshake_timeout_handle.cancel() + self._handshake_timeout_handle = None + + sslobj = self._sslobj + try: + if handshake_exc is None: + self._set_state(WRAPPED) + else: + raise handshake_exc + + peercert = sslobj.getpeercert() + except Exception as exc: + self._set_state(UNWRAPPED) + if isinstance(exc, ssl_CertificateError): + msg = 'SSL handshake failed on verifying the certificate' + else: + msg = 'SSL handshake failed' + self._fatal_error(exc, msg) + self._wakeup_waiter(exc) + return + + if self._loop.get_debug(): + dt = self._loop.time() - self._handshake_start_time + aio_logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3) + + # Add extra info that becomes available after handshake. + self._extra.update(peercert=peercert, + cipher=sslobj.cipher(), + compression=sslobj.compression(), + ssl_object=sslobj) + if self._app_state == STATE_INIT: + self._app_state = STATE_CON_MADE + self._app_protocol.connection_made(self._get_app_transport()) + self._wakeup_waiter() + + # We should wakeup user code before sending the first data below. In + # case of `start_tls()`, the user can only get the SSLTransport in the + # wakeup callback, because `connection_made()` is not called again. + # We should schedule the first data later than the wakeup callback so + # that the user get a chance to e.g. check ALPN with the transport + # before having to handle the first data. + self._loop._call_soon_handle( + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, + None, # current context is good + self)) + + # Shutdown flow + + cdef _start_shutdown(self, object context=None): + if self._state in (FLUSHING, SHUTDOWN, UNWRAPPED): + return + # we don't need the context for _abort or the timeout, because + # TCP transport._force_close() should be able to call + # connection_lost() in the right context + if self._app_transport is not None: + self._app_transport._closed = True + if self._state == DO_HANDSHAKE: + self._abort(None) + else: + self._set_state(FLUSHING) + self._shutdown_timeout_handle = \ + self._loop.call_later(self._ssl_shutdown_timeout, + lambda: self._check_shutdown_timeout()) + self._do_flush(context) + + cdef _check_shutdown_timeout(self): + if self._state in (FLUSHING, SHUTDOWN): + self._transport._force_close( + aio_TimeoutError('SSL shutdown timed out')) + + cdef _do_read_into_void(self, object context): + """Consume and discard incoming application data. + + If close_notify is received for the first time, call eof_received. + """ + cdef: + bint close_notify = False + try: + while True: + if not self._sslobj_read(SSL_READ_MAX_SIZE): + close_notify = True + break + except ssl_SSLAgainErrors as exc: + pass + except ssl_SSLZeroReturnError: + close_notify = True + if close_notify: + self._call_eof_received(context) + + cdef _do_flush(self, object context=None): + """Flush the write backlog, discarding new data received. + + We don't send close_notify in FLUSHING because we still want to send + the remaining data over SSL, even if we received a close_notify. Also, + no application-level resume_writing() or pause_writing() will be called + in FLUSHING, as we could fully manage the flow control internally. + """ + try: + self._do_read_into_void(context) + self._do_write() + self._process_outgoing() + self._control_ssl_reading() + except Exception as ex: + self._on_shutdown_complete(ex) + else: + if not self._get_write_buffer_size(): + self._set_state(SHUTDOWN) + self._do_shutdown(context) + + cdef _do_shutdown(self, object context=None): + """Send close_notify and wait for the same from the peer.""" + try: + # we must skip all application data (if any) before unwrap + self._do_read_into_void(context) + try: + self._sslobj.unwrap() + except ssl_SSLAgainErrors as exc: + self._process_outgoing() + else: + self._process_outgoing() + if not self._get_write_buffer_size(): + self._on_shutdown_complete(None) + except Exception as ex: + self._on_shutdown_complete(ex) + + cdef _on_shutdown_complete(self, shutdown_exc): + if self._shutdown_timeout_handle is not None: + self._shutdown_timeout_handle.cancel() + self._shutdown_timeout_handle = None + + # we don't need the context here because TCP transport.close() should + # be able to call connection_made() in the right context + if shutdown_exc: + self._fatal_error(shutdown_exc, 'Error occurred during shutdown') + else: + self._transport.close() + + cdef _abort(self, exc): + self._set_state(UNWRAPPED) + if self._transport is not None: + self._transport._force_close(exc) + + # Outgoing flow + + cdef _write_appdata(self, list_of_data, object context): + if self._state in (FLUSHING, SHUTDOWN, UNWRAPPED): + if self._conn_lost >= LOG_THRESHOLD_FOR_CONNLOST_WRITES: + aio_logger.warning('SSL connection is closed') + self._conn_lost += 1 + return + + for data in list_of_data: + self._write_backlog.append(data) + self._write_buffer_size += len(data) + + try: + if self._state == WRAPPED: + self._do_write() + self._process_outgoing() + self._control_app_writing(context) + + except Exception as ex: + self._fatal_error(ex, 'Fatal error on SSL protocol') + + cdef _do_write(self): + """Do SSL write, consumes write backlog and fills outgoing BIO.""" + cdef size_t data_len, count + try: + while self._write_backlog: + data = self._write_backlog[0] + count = self._sslobj_write(data) + data_len = len(data) + if count < data_len: + if not PyMemoryView_Check(data): + data = PyMemoryView_FromObject(data) + self._write_backlog[0] = data[count:] + self._write_buffer_size -= count + else: + del self._write_backlog[0] + self._write_buffer_size -= data_len + except ssl_SSLAgainErrors as exc: + pass + + cdef _process_outgoing(self): + """Send bytes from the outgoing BIO.""" + if not self._ssl_writing_paused: + data = self._outgoing_read() + if len(data): + self._transport.write(data) + + # Incoming flow + + cdef _do_read(self): + if self._state != WRAPPED: + return + try: + if not self._app_reading_paused: + if self._app_protocol_is_buffer: + self._do_read__buffered() + else: + self._do_read__copied() + if self._write_backlog: + self._do_write() + self._process_outgoing() + self._control_app_writing() + self._control_ssl_reading() + except Exception as ex: + self._fatal_error(ex, 'Fatal error on SSL protocol') + + cdef _do_read__buffered(self): + cdef: + Py_buffer pybuf + bint pybuf_inited = False + size_t wants, offset = 0 + int count = 1 + object buf + + buf = self._app_protocol_get_buffer(self._get_read_buffer_size()) + wants = len(buf) + + try: + count = self._sslobj_read(wants, buf) + + if count > 0: + offset = count + if offset < wants: + PyObject_GetBuffer(buf, &pybuf, PyBUF_WRITABLE) + pybuf_inited = True + while offset < wants: + buf = PyMemoryView_FromMemory( + (pybuf.buf) + offset, + wants - offset, + PyBUF_WRITE) + count = self._sslobj_read(wants - offset, buf) + if count > 0: + offset += count + else: + break + else: + self._loop._call_soon_handle( + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, + None, # current context is good + self)) + except ssl_SSLAgainErrors as exc: + pass + finally: + if pybuf_inited: + PyBuffer_Release(&pybuf) + if offset > 0: + self._app_protocol_buffer_updated(offset) + if not count: + # close_notify + self._call_eof_received() + self._start_shutdown() + + cdef _do_read__copied(self): + cdef: + list data + bytes first, chunk = b'1' + bint zero = True, one = False + + try: + while True: + chunk = self._sslobj_read(SSL_READ_MAX_SIZE) + if not chunk: + break + if zero: + zero = False + one = True + first = chunk + elif one: + one = False + data = [first, chunk] + else: + data.append(chunk) + except ssl_SSLAgainErrors as exc: + pass + if one: + self._app_protocol.data_received(first) + elif not zero: + self._app_protocol.data_received(b''.join(data)) + if not chunk: + # close_notify + self._call_eof_received() + self._start_shutdown() + + cdef _call_eof_received(self, object context=None): + if self._app_state == STATE_CON_MADE: + self._app_state = STATE_EOF + try: + if context is None: + # If the caller didn't provide a context, we assume the + # caller is already in the right context, which is usually + # inside the upstream callbacks like buffer_updated() + keep_open = self._app_protocol.eof_received() + else: + keep_open = run_in_context( + context, self._app_protocol.eof_received, + ) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as ex: + self._fatal_error(ex, 'Error calling eof_received()') + else: + if keep_open: + aio_logger.warning('returning true from eof_received() ' + 'has no effect when using ssl') + + # Flow control for writes from APP socket + + cdef _control_app_writing(self, object context=None): + cdef size_t size = self._get_write_buffer_size() + if size >= self._outgoing_high_water and not self._app_writing_paused: + self._app_writing_paused = True + try: + if context is None: + # If the caller didn't provide a context, we assume the + # caller is already in the right context, which is usually + # inside the upstream callbacks like buffer_updated() + self._app_protocol.pause_writing() + else: + run_in_context(context, self._app_protocol.pause_writing) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + self._loop.call_exception_handler({ + 'message': 'protocol.pause_writing() failed', + 'exception': exc, + 'transport': self._app_transport, + 'protocol': self, + }) + elif size <= self._outgoing_low_water and self._app_writing_paused: + self._app_writing_paused = False + try: + if context is None: + # If the caller didn't provide a context, we assume the + # caller is already in the right context, which is usually + # inside the upstream callbacks like resume_writing() + self._app_protocol.resume_writing() + else: + run_in_context(context, self._app_protocol.resume_writing) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + self._loop.call_exception_handler({ + 'message': 'protocol.resume_writing() failed', + 'exception': exc, + 'transport': self._app_transport, + 'protocol': self, + }) + + cdef size_t _get_write_buffer_size(self): + return self._outgoing.pending + self._write_buffer_size + + cdef _set_write_buffer_limits(self, high=None, low=None): + high, low = add_flowcontrol_defaults( + high, low, FLOW_CONTROL_HIGH_WATER_SSL_WRITE) + self._outgoing_high_water = high + self._outgoing_low_water = low + + # Flow control for reads to APP socket + + cdef _pause_reading(self): + self._app_reading_paused = True + + cdef _resume_reading(self, object context): + if self._app_reading_paused: + self._app_reading_paused = False + if self._state == WRAPPED: + self._loop._call_soon_handle( + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, + context, + self)) + + # Flow control for reads from SSL socket + + cdef _control_ssl_reading(self): + cdef size_t size = self._get_read_buffer_size() + if size >= self._incoming_high_water and not self._ssl_reading_paused: + self._ssl_reading_paused = True + self._transport.pause_reading() + elif size <= self._incoming_low_water and self._ssl_reading_paused: + self._ssl_reading_paused = False + self._transport.resume_reading() + + cdef _set_read_buffer_limits(self, high=None, low=None): + high, low = add_flowcontrol_defaults( + high, low, FLOW_CONTROL_HIGH_WATER_SSL_READ) + self._incoming_high_water = high + self._incoming_low_water = low + + cdef size_t _get_read_buffer_size(self): + return self._incoming.pending + + # Flow control for writes to SSL socket + + def pause_writing(self): + """Called when the low-level transport's buffer goes over + the high-water mark. + """ + assert not self._ssl_writing_paused + self._ssl_writing_paused = True + + def resume_writing(self): + """Called when the low-level transport's buffer drains below + the low-water mark. + """ + assert self._ssl_writing_paused + self._ssl_writing_paused = False + + if self._state == WRAPPED: + self._process_outgoing() + self._control_app_writing() + + elif self._state == FLUSHING: + self._do_flush() + + elif self._state == SHUTDOWN: + self._do_shutdown() + + cdef _fatal_error(self, exc, message='Fatal error on transport'): + if self._app_transport: + self._app_transport._force_close(exc) + elif self._transport: + self._transport._force_close(exc) + + if isinstance(exc, OSError): + if self._loop.get_debug(): + aio_logger.debug("%r: %s", self, message, exc_info=True) + elif not isinstance(exc, aio_CancelledError): + self._loop.call_exception_handler({ + 'message': message, + 'exception': exc, + 'transport': self._transport, + 'protocol': self, + }) -- cgit v1.2.3