diff --git a/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/METADATA b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f52e7cd82ebddad7c66174058b828db25fa87aa4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/METADATA @@ -0,0 +1,261 @@ +Metadata-Version: 2.2 +Name: nvidia-ml-py +Version: 12.570.86 +Summary: Python Bindings for the NVIDIA Management Library +Home-page: https://forums.developer.nvidia.com +Author: NVIDIA Corporation +Author-email: nvml-bindings@nvidia.com +License: BSD +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Hardware +Classifier: Topic :: System :: Systems Administration +Description-Content-Type: text/markdown +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license +Dynamic: summary + +pyNVML +====== + +Python bindings to the NVIDIA Management Library +------------------------------------------------ + +Provides a Python interface to GPU management and monitoring functions. + +This is a wrapper around the NVML library. +For information about the NVML library, see the NVML developer page +http://developer.nvidia.com/nvidia-management-library-nvml + +Download the latest package from: +http://pypi.python.org/pypi/nvidia-ml-py/ + +Note this file can be run with 'python -m doctest -v README.txt' +although the results are system dependent + +The nvml header file contains function documentation that is relevant +to this wrapper. The header file is distributed with. +https://developer.nvidia.com/gpu-deployment-kit + +The main difference is this library handles allocating structs and +passing pointers to the functions, before returning the desired value. +Non-success return codes are raised as exceptions as described in the +section below. + +REQUIRES +-------- +Python 2.5, or an earlier version with the ctypes module. + +INSTALLATION +------------ + +Pip Installation with python3: +- `python3 -m pip install nvidia-ml-py` + +Manual Installation: +``` +$ tar -xzf nvidia-ml-py-$major-$minor-$patch.tar.gz` +$ cd nvidia-ml-py-$major-$minor-$patch +$ sudo python setup.py install +``` + +USAGE +----- +``` +>>> from pynvml import * +>>> nvmlInit() +>>> print(f"Driver Version: {nvmlSystemGetDriverVersion()}") +Driver Version: 11.515.48 +>>> deviceCount = nvmlDeviceGetCount() +>>> for i in range(deviceCount): +... handle = nvmlDeviceGetHandleByIndex(i) +... print(f"Device {i} : {nvmlDeviceGetName(handle)}") +... +Device 0 : Tesla K40c + +>>> nvmlShutdown() +``` + +FUNCTIONS +--------- +Python methods wrap NVML functions, implemented in a C shared library. +Each function's use is the same with the following exceptions: + +- Instead of returning error codes, failing error codes are raised as Python exceptions. + +``` +>>> try: +... nvmlDeviceGetCount() +... except NVMLError as error: +... print(error) +... +Uninitialized +``` + +- C function output parameters are returned from the corresponding Python function left to right. +``` +nvmlReturn_t nvmlDeviceGetEccMode(nvmlDevice_t device, + nvmlEnableState_t *current, + nvmlEnableState_t *pending); + +>>> nvmlInit() +>>> handle = nvmlDeviceGetHandleByIndex(0) +>>> (current, pending) = nvmlDeviceGetEccMode(handle) +``` +- C structs are converted into Python classes. + +``` +// C Function and typedef struct +nvmlReturn_t DECLDIR nvmlDeviceGetMemoryInfo(nvmlDevice_t device, + nvmlMemory_t *memory); +typedef struct nvmlMemory_st { + unsigned long long total; + unsigned long long free; + unsigned long long used; +} nvmlMemory_t; + + +# Python call to function and accessing members of ctype struct +>>> info = nvmlDeviceGetMemoryInfo(handle) +>>> print(f"Total memory: {info.total}") +Total memory: 5636292608 +>>> print(f"Free memory:, {info.free}") +Free memory: 5578420224 +>>> print(f"Used memory: {info.used}") +Used memory: 57872384 +``` + +- Python handles string buffer creation. + +``` +// C Function that needs character array and length +nvmlReturn_t nvmlSystemGetDriverVersion(char* version, + unsigned int length); + +# Python function handles memory +>>> version = nvmlSystemGetDriverVersion() +>>> print(version) +... 11.520.75 +>>> nvmlShutdown() +``` + +For usage information see the NVML documentation. + +VARIABLES +--------- +All meaningful NVML constants and enums are exposed in Python. + +The NVML_VALUE_NOT_AVAILABLE constant is not used. Instead None is mapped to the field. + +EXCEPTIONS +---------- +Since the C library uses return codes and python prefers exception handling, the +library converts all return codes to various exceptions. The exceptions are generated +automatically via a function at run time instead of being defined manually. + +The list of exceptions can be found in NVMLError base class. + +The example seen above in the FUNCTIONS section: + +``` +>>> try: +... nvmlDeviceGetCount() +... except NVMLError as error: +... print(error) +... +Uninitialized +``` + +Can be more accurately caught like this: + +``` +>>> try: +... nvmlDeviceGetCount() +... except NVMLError_Uninitialized as error: +... print(error) +... +Uninitialized +``` + +The conversion from name to exception is like this for all exceptions: +* `NVML_ERROR_UNINITIALIZED` => `NVMLError_Uninitialized` +* `NVML_ERROR_LIBRARY_NOT_FOUND` => `NVMLError_LibraryNotFound` +* `NVML_ERROR_ALREADY_INITIALIZED` => `NVMLError_AlreadyInitialized` + +RELEASE NOTES +------------- +Version 2.285.0 +- Added new functions for NVML 2.285. See NVML documentation for more information. +- Ported to support Python 3.0 and Python 2.0 syntax. +- Added nvidia_smi.py tool as a sample app. + +Version 3.295.0 +- Added new functions for NVML 3.295. See NVML documentation for more information. +- Updated nvidia_smi.py tool +- Includes additional error handling + +Version 4.304.0 +- Added new functions for NVML 4.304. See NVML documentation for more information. +- Updated nvidia_smi.py tool + +Version 4.304.3 +- Fixing nvmlUnitGetDeviceCount bug + +Version 5.319.0 +- Added new functions for NVML 5.319. See NVML documentation for more information. + +Version 6.340.0 +- Added new functions for NVML 6.340. See NVML documentation for more information. + +Version 7.346.0 +- Added new functions for NVML 7.346. See NVML documentation for more information. + +Version 7.352.0 +- Added new functions for NVML 7.352. See NVML documentation for more information. + +Version 10.418 +- Added new functions for NVML 10.418. See NVML documentation for more information. +- Fixed issues with using the bindings with Python 3.x +- Replaced sample app nvidia_smi.py with example.py + +Version 11.515.48 +- Python3 support added +- Updated API to add function new to NVML, bringing pynvml up to date with NVML +- Added auto-version to handle byte and string conversion automatically for both structs and functions +- Minor bug fixes +- Added README.txt correctly in long_description for pypi.org + +Version 11.520 +- Updated Long Description to be actual markdown +- Added new functions for NVML 11.520 + +Version 11.525 +- Added new functions for NVML 11.525 + +COPYRIGHT +--------- +Copyright (c) 2011-2023, NVIDIA Corporation. All rights reserved. + +LICENSE +------- +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of the NVIDIA Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/RECORD b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a3ef3608643dae12265175595cbeb1a3718e58a2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/RECORD @@ -0,0 +1,9 @@ +__pycache__/example.cpython-311.pyc,, +__pycache__/pynvml.cpython-311.pyc,, +example.py,sha256=mDXwPVyEDuiKeMApEh53r_M36xuncmzMpFOGA3Q-_Kw,7968 +nvidia_ml_py-12.570.86.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +nvidia_ml_py-12.570.86.dist-info/METADATA,sha256=vY-jfk5MJsbWGy2jmbgdwfPKG4G0FHbspv-av_h5bEE,8718 +nvidia_ml_py-12.570.86.dist-info/RECORD,, +nvidia_ml_py-12.570.86.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91 +nvidia_ml_py-12.570.86.dist-info/top_level.txt,sha256=wLINSA1WKnhsGgKsb_nuj51ZCQrXaN5qhioTL56g98A,15 +pynvml.py,sha256=PCW5qJPhGshkIhIUOOQyUXsxkCVaPeTi30R7LsJb4YE,234473 diff --git a/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..505164bc02d63fe6b0b3299f849a77c5f1beeb41 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..32cd7dc2238f6f95fdc3a286f712d402cdda37aa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/top_level.txt @@ -0,0 +1,2 @@ +example +pynvml diff --git a/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/METADATA b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..91c59c9a280a03cc6bae6bb174233bc1d7068b06 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/METADATA @@ -0,0 +1,327 @@ +Metadata-Version: 2.3 +Name: platformdirs +Version: 4.3.6 +Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`. +Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases +Project-URL: Documentation, https://platformdirs.readthedocs.io +Project-URL: Homepage, https://github.com/tox-dev/platformdirs +Project-URL: Source, https://github.com/tox-dev/platformdirs +Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues +Maintainer-email: Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt +License-Expression: MIT +License-File: LICENSE +Keywords: appdirs,application,cache,directory,log,user +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +Provides-Extra: docs +Requires-Dist: furo>=2024.8.6; extra == 'docs' +Requires-Dist: proselint>=0.14; extra == 'docs' +Requires-Dist: sphinx-autodoc-typehints>=2.4; extra == 'docs' +Requires-Dist: sphinx>=8.0.2; extra == 'docs' +Provides-Extra: test +Requires-Dist: appdirs==1.4.4; extra == 'test' +Requires-Dist: covdefaults>=2.3; extra == 'test' +Requires-Dist: pytest-cov>=5; extra == 'test' +Requires-Dist: pytest-mock>=3.14; extra == 'test' +Requires-Dist: pytest>=8.3.2; extra == 'test' +Provides-Extra: type +Requires-Dist: mypy>=1.11.2; extra == 'type' +Description-Content-Type: text/x-rst + +The problem +=========== + +.. image:: https://badge.fury.io/py/platformdirs.svg + :target: https://badge.fury.io/py/platformdirs +.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg + :target: https://pypi.python.org/pypi/platformdirs/ +.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg + :target: https://github.com/platformdirs/platformdirs/actions +.. image:: https://static.pepy.tech/badge/platformdirs/month + :target: https://pepy.tech/project/platformdirs + +When writing desktop application, finding the right location to store user data +and configuration varies per platform. Even for single-platform apps, there +may by plenty of nuances in figuring out the right location. + +For example, if running on macOS, you should use:: + + ~/Library/Application Support/ + +If on Windows (at least English Win) that should be:: + + C:\Documents and Settings\\Application Data\Local Settings\\ + +or possibly:: + + C:\Documents and Settings\\Application Data\\ + +for `roaming profiles `_ but that is another story. + +On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: + + ~/.local/share/ + +.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +``platformdirs`` to the rescue +============================== + +This kind of thing is what the ``platformdirs`` package is for. +``platformdirs`` will help you choose an appropriate: + +- user data dir (``user_data_dir``) +- user config dir (``user_config_dir``) +- user cache dir (``user_cache_dir``) +- site data dir (``site_data_dir``) +- site config dir (``site_config_dir``) +- user log dir (``user_log_dir``) +- user documents dir (``user_documents_dir``) +- user downloads dir (``user_downloads_dir``) +- user pictures dir (``user_pictures_dir``) +- user videos dir (``user_videos_dir``) +- user music dir (``user_music_dir``) +- user desktop dir (``user_desktop_dir``) +- user runtime dir (``user_runtime_dir``) + +And also: + +- Is slightly opinionated on the directory names used. Look for "OPINION" in + documentation and code for when an opinion is being applied. + +Example output +============== + +On macOS: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> site_data_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/Users/trentm/Library/Caches/SuperApp' + >>> user_log_dir(appname, appauthor) + '/Users/trentm/Library/Logs/SuperApp' + >>> user_documents_dir() + '/Users/trentm/Documents' + >>> user_downloads_dir() + '/Users/trentm/Downloads' + >>> user_pictures_dir() + '/Users/trentm/Pictures' + >>> user_videos_dir() + '/Users/trentm/Movies' + >>> user_music_dir() + '/Users/trentm/Music' + >>> user_desktop_dir() + '/Users/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +On Windows: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_data_dir(appname, appauthor, roaming=True) + 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' + >>> user_cache_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' + >>> user_log_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' + >>> user_documents_dir() + 'C:\\Users\\trentm\\Documents' + >>> user_downloads_dir() + 'C:\\Users\\trentm\\Downloads' + >>> user_pictures_dir() + 'C:\\Users\\trentm\\Pictures' + >>> user_videos_dir() + 'C:\\Users\\trentm\\Videos' + >>> user_music_dir() + 'C:\\Users\\trentm\\Music' + >>> user_desktop_dir() + 'C:\\Users\\trentm\\Desktop' + >>> user_runtime_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' + +On Linux: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/home/trentm/.local/share/SuperApp' + >>> site_data_dir(appname, appauthor) + '/usr/local/share/SuperApp' + >>> site_data_dir(appname, appauthor, multipath=True) + '/usr/local/share/SuperApp:/usr/share/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp' + >>> user_log_dir(appname, appauthor) + '/home/trentm/.local/state/SuperApp/log' + >>> user_config_dir(appname) + '/home/trentm/.config/SuperApp' + >>> user_documents_dir() + '/home/trentm/Documents' + >>> user_downloads_dir() + '/home/trentm/Downloads' + >>> user_pictures_dir() + '/home/trentm/Pictures' + >>> user_videos_dir() + '/home/trentm/Videos' + >>> user_music_dir() + '/home/trentm/Music' + >>> user_desktop_dir() + '/home/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/run/user/{os.getuid()}/SuperApp' + >>> site_config_dir(appname) + '/etc/xdg/SuperApp' + >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" + >>> site_config_dir(appname, multipath=True) + '/etc/SuperApp:/usr/local/etc/SuperApp' + +On Android:: + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/data/data/com.myApp/files/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp' + >>> user_log_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/log' + >>> user_config_dir(appname) + '/data/data/com.myApp/shared_prefs/SuperApp' + >>> user_documents_dir() + '/storage/emulated/0/Documents' + >>> user_downloads_dir() + '/storage/emulated/0/Downloads' + >>> user_pictures_dir() + '/storage/emulated/0/Pictures' + >>> user_videos_dir() + '/storage/emulated/0/DCIM/Camera' + >>> user_music_dir() + '/storage/emulated/0/Music' + >>> user_desktop_dir() + '/storage/emulated/0/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/tmp' + +Note: Some android apps like Termux and Pydroid are used as shells. These +apps are used by the end user to emulate Linux environment. Presence of +``SHELL`` environment variable is used by Platformdirs to differentiate +between general android apps and android apps used as shells. Shell android +apps also support ``XDG_*`` environment variables. + + +``PlatformDirs`` for convenience +================================ + +.. code-block:: pycon + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +Per-version isolation +===================== + +If you have multiple versions of your app in use that you want to be +able to run side-by-side, then you may want version-isolation for these +dirs:: + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp/1.0' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp/1.0' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' + +Be wary of using this for configuration files though; you'll need to handle +migrating configuration files manually. + +Why this Fork? +============== + +This repository is a friendly fork of the wonderful work started by +`ActiveState `_ who created +``appdirs``, this package's ancestor. + +Maintaining an open source project is no easy task, particularly +from within an organization, and the Python community is indebted +to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for +creating an incredibly useful simple module, as evidenced by the wide +number of users it has attracted over the years. + +Nonetheless, given the number of long-standing open issues +and pull requests, and no clear path towards `ensuring +that maintenance of the package would continue or grow +`_, this fork was +created. + +Contributions are most welcome. diff --git a/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/RECORD b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a040b1890eb587e09b3196d8cf5c6b9007409c22 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/RECORD @@ -0,0 +1,22 @@ +platformdirs-4.3.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +platformdirs-4.3.6.dist-info/METADATA,sha256=085GgRFo5U1nc9NR8e6unEWKxUjDMsgSHDyaCETsCQ4,11868 +platformdirs-4.3.6.dist-info/RECORD,, +platformdirs-4.3.6.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87 +platformdirs-4.3.6.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089 +platformdirs/__init__.py,sha256=mVCfMmBM4q24lq6336V3VJncdxaOegI4qQSmQCjkR5E,22284 +platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493 +platformdirs/__pycache__/__init__.cpython-311.pyc,, +platformdirs/__pycache__/__main__.cpython-311.pyc,, +platformdirs/__pycache__/android.cpython-311.pyc,, +platformdirs/__pycache__/api.cpython-311.pyc,, +platformdirs/__pycache__/macos.cpython-311.pyc,, +platformdirs/__pycache__/unix.cpython-311.pyc,, +platformdirs/__pycache__/version.cpython-311.pyc,, +platformdirs/__pycache__/windows.cpython-311.pyc,, +platformdirs/android.py,sha256=kV5oL3V3DZ6WZKu9yFiQupv18yp_jlSV2ChH1TmPcds,9007 +platformdirs/api.py,sha256=2dfUDNbEXeDhDKarqtR5NY7oUikUZ4RZhs3ozstmhBQ,9246 +platformdirs/macos.py,sha256=UlbyFZ8Rzu3xndCqQEHrfsYTeHwYdFap1Ioz-yxveT4,6154 +platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +platformdirs/unix.py,sha256=uRPJWRyQEtv7yOSvU94rUmsblo5XKDLA1SzFg55kbK0,10393 +platformdirs/version.py,sha256=oH4KgTfK4AklbTYVcV_yynvJ9JLI3pyvDVay0hRsLCs,411 +platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125 diff --git a/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/licenses/LICENSE b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f35fed9191b1142ddaada8a96de4a9461c5d796c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-202x The platformdirs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/METADATA b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..b8f89610892156145ecc6894e7b005e7c2a630a1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/METADATA @@ -0,0 +1,212 @@ +Metadata-Version: 2.1 +Name: pytest +Version: 8.3.4 +Summary: pytest: simple powerful testing with Python +Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin, Others (See AUTHORS) +License: MIT +Project-URL: Changelog, https://docs.pytest.org/en/stable/changelog.html +Project-URL: Homepage, https://docs.pytest.org/en/latest/ +Project-URL: Source, https://github.com/pytest-dev/pytest +Project-URL: Tracker, https://github.com/pytest-dev/pytest/issues +Project-URL: Twitter, https://twitter.com/pytestdotorg +Keywords: test,unittest +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Testing +Classifier: Topic :: Utilities +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: colorama; sys_platform == "win32" +Requires-Dist: exceptiongroup>=1.0.0rc8; python_version < "3.11" +Requires-Dist: iniconfig +Requires-Dist: packaging +Requires-Dist: pluggy<2,>=1.5 +Requires-Dist: tomli>=1; python_version < "3.11" +Provides-Extra: dev +Requires-Dist: argcomplete; extra == "dev" +Requires-Dist: attrs>=19.2; extra == "dev" +Requires-Dist: hypothesis>=3.56; extra == "dev" +Requires-Dist: mock; extra == "dev" +Requires-Dist: pygments>=2.7.2; extra == "dev" +Requires-Dist: requests; extra == "dev" +Requires-Dist: setuptools; extra == "dev" +Requires-Dist: xmlschema; extra == "dev" + +.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg + :target: https://docs.pytest.org/en/stable/ + :align: center + :height: 200 + :alt: pytest + + +------ + +.. image:: https://img.shields.io/pypi/v/pytest.svg + :target: https://pypi.org/project/pytest/ + +.. image:: https://img.shields.io/conda/vn/conda-forge/pytest.svg + :target: https://anaconda.org/conda-forge/pytest + +.. image:: https://img.shields.io/pypi/pyversions/pytest.svg + :target: https://pypi.org/project/pytest/ + +.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg + :target: https://codecov.io/gh/pytest-dev/pytest + :alt: Code coverage Status + +.. image:: https://github.com/pytest-dev/pytest/actions/workflows/test.yml/badge.svg + :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest + +.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg + :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main + :alt: pre-commit.ci status + +.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg + :target: https://www.codetriage.com/pytest-dev/pytest + +.. image:: https://readthedocs.org/projects/pytest/badge/?version=latest + :target: https://pytest.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/Discord-pytest--dev-blue + :target: https://discord.com/invite/pytest-dev + :alt: Discord + +.. image:: https://img.shields.io/badge/Libera%20chat-%23pytest-orange + :target: https://web.libera.chat/#pytest + :alt: Libera chat + + +The ``pytest`` framework makes it easy to write small tests, yet +scales to support complex functional testing for applications and libraries. + +An example of a simple test: + +.. code-block:: python + + # content of test_sample.py + def inc(x): + return x + 1 + + + def test_answer(): + assert inc(3) == 5 + + +To execute it:: + + $ pytest + ============================= test session starts ============================= + collected 1 items + + test_sample.py F + + ================================== FAILURES =================================== + _________________________________ test_answer _________________________________ + + def test_answer(): + > assert inc(3) == 5 + E assert 4 == 5 + E + where 4 = inc(3) + + test_sample.py:5: AssertionError + ========================== 1 failed in 0.04 seconds =========================== + + +Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started `_ for more examples. + + +Features +-------- + +- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) + +- `Auto-discovery + `_ + of test modules and functions + +- `Modular fixtures `_ for + managing small or parametrized long-lived test resources + +- Can run `unittest `_ (or trial) + test suites out of the box + +- Python 3.8+ or PyPy3 + +- Rich plugin architecture, with over 1300+ `external plugins `_ and thriving community + + +Documentation +------------- + +For full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/stable/. + + +Bugs/Requests +------------- + +Please use the `GitHub issue tracker `_ to submit bugs or request features. + + +Changelog +--------- + +Consult the `Changelog `__ page for fixes and enhancements of each version. + + +Support pytest +-------------- + +`Open Collective`_ is an online funding platform for open and transparent communities. +It provides tools to raise money and share your finances in full transparency. + +It is the platform of choice for individuals and companies that want to make one-time or +monthly donations directly to the project. + +See more details in the `pytest collective`_. + +.. _Open Collective: https://opencollective.com +.. _pytest collective: https://opencollective.com/pytest + + +pytest for enterprise +--------------------- + +Available as part of the Tidelift Subscription. + +The maintainers of pytest and thousands of other packages are working with Tidelift to deliver commercial support and +maintenance for the open source dependencies you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. + +`Learn more. `_ + +Security +^^^^^^^^ + +pytest has never been associated with a security vulnerability, but in any case, to report a +security vulnerability please use the `Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. + + +License +------- + +Copyright Holger Krekel and others, 2004. + +Distributed under the terms of the `MIT`_ license, pytest is free and open source software. + +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/RECORD b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..b4302557f519088b503fd1e443761b6a259ad52b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/RECORD @@ -0,0 +1,154 @@ +../../../bin/py.test,sha256=vvHOr9KK5AGKvqYrdl0oQCYePyyaJI8PmXGxzS68qLc,239 +../../../bin/pytest,sha256=vvHOr9KK5AGKvqYrdl0oQCYePyyaJI8PmXGxzS68qLc,239 +__pycache__/py.cpython-311.pyc,, +_pytest/__init__.py,sha256=4IdRJhnW5XG2KlaJkOxn5_TC9WeQ5tXDSF7tbb4vEso,391 +_pytest/__pycache__/__init__.cpython-311.pyc,, +_pytest/__pycache__/_argcomplete.cpython-311.pyc,, +_pytest/__pycache__/_version.cpython-311.pyc,, +_pytest/__pycache__/cacheprovider.cpython-311.pyc,, +_pytest/__pycache__/capture.cpython-311.pyc,, +_pytest/__pycache__/compat.cpython-311.pyc,, +_pytest/__pycache__/debugging.cpython-311.pyc,, +_pytest/__pycache__/deprecated.cpython-311.pyc,, +_pytest/__pycache__/doctest.cpython-311.pyc,, +_pytest/__pycache__/faulthandler.cpython-311.pyc,, +_pytest/__pycache__/fixtures.cpython-311.pyc,, +_pytest/__pycache__/freeze_support.cpython-311.pyc,, +_pytest/__pycache__/helpconfig.cpython-311.pyc,, +_pytest/__pycache__/hookspec.cpython-311.pyc,, +_pytest/__pycache__/junitxml.cpython-311.pyc,, +_pytest/__pycache__/legacypath.cpython-311.pyc,, +_pytest/__pycache__/logging.cpython-311.pyc,, +_pytest/__pycache__/main.cpython-311.pyc,, +_pytest/__pycache__/monkeypatch.cpython-311.pyc,, +_pytest/__pycache__/nodes.cpython-311.pyc,, +_pytest/__pycache__/outcomes.cpython-311.pyc,, +_pytest/__pycache__/pastebin.cpython-311.pyc,, +_pytest/__pycache__/pathlib.cpython-311.pyc,, +_pytest/__pycache__/pytester.cpython-311.pyc,, +_pytest/__pycache__/pytester_assertions.cpython-311.pyc,, +_pytest/__pycache__/python.cpython-311.pyc,, +_pytest/__pycache__/python_api.cpython-311.pyc,, +_pytest/__pycache__/python_path.cpython-311.pyc,, +_pytest/__pycache__/recwarn.cpython-311.pyc,, +_pytest/__pycache__/reports.cpython-311.pyc,, +_pytest/__pycache__/runner.cpython-311.pyc,, +_pytest/__pycache__/scope.cpython-311.pyc,, +_pytest/__pycache__/setuponly.cpython-311.pyc,, +_pytest/__pycache__/setupplan.cpython-311.pyc,, +_pytest/__pycache__/skipping.cpython-311.pyc,, +_pytest/__pycache__/stash.cpython-311.pyc,, +_pytest/__pycache__/stepwise.cpython-311.pyc,, +_pytest/__pycache__/terminal.cpython-311.pyc,, +_pytest/__pycache__/threadexception.cpython-311.pyc,, +_pytest/__pycache__/timing.cpython-311.pyc,, +_pytest/__pycache__/tmpdir.cpython-311.pyc,, +_pytest/__pycache__/unittest.cpython-311.pyc,, +_pytest/__pycache__/unraisableexception.cpython-311.pyc,, +_pytest/__pycache__/warning_types.cpython-311.pyc,, +_pytest/__pycache__/warnings.cpython-311.pyc,, +_pytest/_argcomplete.py,sha256=gh0pna66p4LVb2D8ST4568WGxvdInGT43m6slYhqNqU,3776 +_pytest/_code/__init__.py,sha256=5h7R-LFINKh7p8QR1HgdjvSGo1ysVJz28MQ9h7ipHK4,521 +_pytest/_code/__pycache__/__init__.cpython-311.pyc,, +_pytest/_code/__pycache__/code.cpython-311.pyc,, +_pytest/_code/__pycache__/source.cpython-311.pyc,, +_pytest/_code/code.py,sha256=umPdqLxq8UgWKAItTEvF6ZOq5dF65mzCJHFaZHzTNGY,50133 +_pytest/_code/source.py,sha256=2w9OZFOrRpiVaD_UdUS1T2XC7c2Is2GZn0iQy-lZfwk,7278 +_pytest/_io/__init__.py,sha256=pkLF29VEFr6Dlr3eOtJL8sf47RLFt1Jf4X1DZBPlYmc,190 +_pytest/_io/__pycache__/__init__.cpython-311.pyc,, +_pytest/_io/__pycache__/pprint.cpython-311.pyc,, +_pytest/_io/__pycache__/saferepr.cpython-311.pyc,, +_pytest/_io/__pycache__/terminalwriter.cpython-311.pyc,, +_pytest/_io/__pycache__/wcwidth.cpython-311.pyc,, +_pytest/_io/pprint.py,sha256=BCe8K7Zc0drYC5_JKZBBMVrhK84ARlmPpk9vSWPYhaE,19633 +_pytest/_io/saferepr.py,sha256=Hhx5F-75iz03hdk-WO86Bmy9RBuRHsuJj-YUzozfrgo,4082 +_pytest/_io/terminalwriter.py,sha256=dQ07zJ1-vlpFqWBBu_c0cHxT0yXcGSu7o7LxDCEyB3s,9319 +_pytest/_io/wcwidth.py,sha256=cUEJ74UhweICwbKvU2q6noZcNgD0QlBEB9CfakGYaqA,1289 +_pytest/_py/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +_pytest/_py/__pycache__/__init__.cpython-311.pyc,, +_pytest/_py/__pycache__/error.cpython-311.pyc,, +_pytest/_py/__pycache__/path.cpython-311.pyc,, +_pytest/_py/error.py,sha256=S1BRlfXSD248OFNzAuZ5O9w9W6fr2NUn0X8wYFGMNk0,3015 +_pytest/_py/path.py,sha256=Xl4UspvrwwKYNlLZDGbjhUrnD6fuBmFxxchgltmwGek,49211 +_pytest/_version.py,sha256=lMdMvJTmZKMLyCj9eyDEhOf6ttbh5vLbkANi6yBfmkc,411 +_pytest/assertion/__init__.py,sha256=lK1YNNAk1VqCK-Y5C5hMJMqJQyxQT11HuDH3w85a3Zc,6791 +_pytest/assertion/__pycache__/__init__.cpython-311.pyc,, +_pytest/assertion/__pycache__/rewrite.cpython-311.pyc,, +_pytest/assertion/__pycache__/truncate.cpython-311.pyc,, +_pytest/assertion/__pycache__/util.cpython-311.pyc,, +_pytest/assertion/rewrite.py,sha256=OltHwIVPXCPe0Ejgta_4hWjY8Xo3BIGbQEyLREfXZCA,48272 +_pytest/assertion/truncate.py,sha256=GYl5iqDXUuKQHgd_mthWl3ZjxBbDVQliBhks1Ty00kE,4459 +_pytest/assertion/util.py,sha256=6Vg5dZDuIXak9OLupvKyavLDroATgpU6ilkclc0OlJY,20265 +_pytest/cacheprovider.py,sha256=BFQVkna56tlO-v9qaNJVHIcF30hIVGrP9St_vMp4w98,22373 +_pytest/capture.py,sha256=iiu_k5_0ASbINS5_o7ZxMShGaWZFQD-y7jtU-NiJtMs,34680 +_pytest/compat.py,sha256=sPcVQwPd45GaqsgIZEbCTR04GKhkVmIfft6QnKj3hmo,11467 +_pytest/config/__init__.py,sha256=Ch5YizaRrCfMykEuZdHF0RaIyvtWvoSXL4v6E1Cu-FY,70645 +_pytest/config/__pycache__/__init__.cpython-311.pyc,, +_pytest/config/__pycache__/argparsing.cpython-311.pyc,, +_pytest/config/__pycache__/compat.cpython-311.pyc,, +_pytest/config/__pycache__/exceptions.cpython-311.pyc,, +_pytest/config/__pycache__/findpaths.cpython-311.pyc,, +_pytest/config/argparsing.py,sha256=dNjEvFh2C34XMoiE_R7liJv5cryXUz2WR2VsxdnQdjo,20562 +_pytest/config/compat.py,sha256=-m8G4-LLezCd4KZO6JQufEz7cRDqUSOjIwCtiKWpJvY,2938 +_pytest/config/exceptions.py,sha256=lUKnOtpRqK-qNL6JfOP-8tRqpmHU34CVxguR5y0Qfbw,288 +_pytest/config/findpaths.py,sha256=h4zq5AbLaZGpkeEcD2Xg-rJimh9I5pE042qQOTZT7NM,8062 +_pytest/debugging.py,sha256=yRmmOexsaDeFky37IrD2e9svz8CWebB7L2fSUy4LvuE,13260 +_pytest/deprecated.py,sha256=sO9UiqEdy9Z-NCvDoYYA0QtafYogAb7lP5M9N_Hpnak,3147 +_pytest/doctest.py,sha256=7WJprJGYj7_9Lyr-L49wJ7q5ZwDVj1FBhA9_CX7JdLc,26255 +_pytest/faulthandler.py,sha256=dT0H-MLi62SXeiKjLQJ0EVPuxkTlNOxpWtNxA5uBJPs,3674 +_pytest/fixtures.py,sha256=I5t3pW2lHaVPbN1rAQ9sdX0a3QrpoW_U5VP-Vxejxmg,73550 +_pytest/freeze_support.py,sha256=1EfzuxPd2oV9Ira26K5J4r9ppFZjnGi-xKzsBXe8B4g,1291 +_pytest/helpconfig.py,sha256=ibnZNxKzToLmx-2ZrZKCP9t6jJvpAIlmqdf9a0rhOoI,8895 +_pytest/hookspec.py,sha256=G-wKdmV3pecpeeiIAmzgPUMr22kz-CsqSpWEM-uiamg,42825 +_pytest/junitxml.py,sha256=FnYwq0wAR4Cixzj-a9qhyulUSEpMyjX9ALbjza_We74,25574 +_pytest/legacypath.py,sha256=_l6v8akNMfTc5TAjvbc6M-_t157p9QE6-118WM0DRt8,16588 +_pytest/logging.py,sha256=QfaUUx-T0FiKBJBBb3bDllt8O8eTE7Mpigq7wvDepRc,35124 +_pytest/main.py,sha256=Oowez36UkOwJXkTRq4rVuJRRr18ItBnz_YDjgAmFCV8,37416 +_pytest/mark/__init__.py,sha256=bHORyCAsGnGJq7Tpm7A2sNQX31jwU1TgufM9DYcrTfQ,9307 +_pytest/mark/__pycache__/__init__.cpython-311.pyc,, +_pytest/mark/__pycache__/expression.cpython-311.pyc,, +_pytest/mark/__pycache__/structures.cpython-311.pyc,, +_pytest/mark/expression.py,sha256=H6LmX0MWlxe0uBmuXIpQEntrLtyqIhEJv07YvA79eDQ,10152 +_pytest/mark/structures.py,sha256=6hiIR3d4zxy35Yiw961r9sYrNl-T5WS8_0auSmpdiB0,21039 +_pytest/monkeypatch.py,sha256=SKgteVJz1puqYQ3el6-ju5ZsNABqpoMUuRC6nn3tFpc,14598 +_pytest/nodes.py,sha256=Hqyplow99hb-Zz0KKzL0K3cQ0rCgDXK65vBp6ave3u8,26483 +_pytest/outcomes.py,sha256=SeW14rRKnGSt7K_NxY7HGnedoJawFHwQi2anAYYugk8,10532 +_pytest/pastebin.py,sha256=Ja1z3Z6cXNElobpwy97FiyR5DDexZrDEB6vufmNvE4o,3978 +_pytest/pathlib.py,sha256=onXoMMo4cbp-DR03XQuRimuIm_DrHNg3RujwP4Z7tic,36617 +_pytest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +_pytest/pytester.py,sha256=-D_SNLfp_AQRMP7GOo6NsXlektiYod79pxBY-2RdUT0,61552 +_pytest/pytester_assertions.py,sha256=EIHeb1llN9TVRfBJqQnwvjKeG-KpzURNq8mtbK7vcyA,2244 +_pytest/python.py,sha256=mkJek4hqp7GMIyk6uPNWclI2dYlg78tTjymdcZViTJM,64851 +_pytest/python_api.py,sha256=KyIP4xCyd2BpNFlY-28a0E50XxMXy7HSQi6ZaCw3qZg,40122 +_pytest/python_path.py,sha256=fGP7iR_XMFRPijg4niILo44gWUWLlD635fYO5Abd6IM,745 +_pytest/recwarn.py,sha256=M_xZw1EMireN8CZMmlI_sCiVsun8Rcq0FlnRkPeWdYQ,13227 +_pytest/reports.py,sha256=5OM_OyQHIS09PW6T_8kAJNS67GvcpvP-lKcna2LcSZ0,21331 +_pytest/runner.py,sha256=LDWKfhiIzWNkXqr1xwex-l1yhsWkdWCJko4bYM-etQ8,19436 +_pytest/scope.py,sha256=MyzqXUuWP1-BgbbCBzJyILiS_jicZO2LNUMUjv7vhh0,2798 +_pytest/setuponly.py,sha256=HNY9Ern-wex9iWSHxJU6ODA0yzYIH65QCkgNZ_BmbuA,3306 +_pytest/setupplan.py,sha256=l-ycFNxDZPyY52wh4f7yaqhzZ7SW1ijSKnQLmqzDZWA,1184 +_pytest/skipping.py,sha256=XbZKDPek9ex8aRXEoEy5iv0_e1b0sUi0PZrWqLBapek,10217 +_pytest/stash.py,sha256=5pE3kDx4q855TW9aVvYTdrkkKlMDU6-xiX4luKpJEgI,3090 +_pytest/stepwise.py,sha256=lYFm6kg000n_WEGOEQAho0j6dRCKJvgKz1Ya2Zz-0Zc,4596 +_pytest/terminal.py,sha256=-xT17xSJs9bu90wqRBc3WckaWTNTPOmVkZlO1X16Wyo,57393 +_pytest/threadexception.py,sha256=GHLYwCYK6I13Xv6bISO2crvPj9Z5ADKgVnUD7m1Oa14,3005 +_pytest/timing.py,sha256=URwa2JENXYkIN_9LFgEmJ4ric7SW8O6a8woS_TN6jXI,413 +_pytest/tmpdir.py,sha256=bo40r_gpxS7AdB_BANpSgh_fejHiXaGWrBxHpax9wtw,11375 +_pytest/unittest.py,sha256=wew7w2q5SqgdPppFzv0evwrTLWmMCwKFQvSUyEX2C0Q,15614 +_pytest/unraisableexception.py,sha256=-L6ln8mRnqqPBskzarua49st4ioXoKgllZ3oMmRuCKU,3252 +_pytest/warning_types.py,sha256=m2_Y3zydUZNzPpu88n8wPNWqaxfaATMKEo_zAgXMqyY,4388 +_pytest/warnings.py,sha256=ExyXdM9ZsIUX4o5GCt43fR-YWhIHSuUbV6GbKEVXeiA,5211 +py.py,sha256=txZ1tdmEW6CBTp6Idn-I2sOzzA0xKNoCi9Re27Uj6HE,329 +pytest-8.3.4.dist-info/AUTHORS,sha256=XobBrGbApvzdviFsaW1chh5icAuN555aWaMBHcmuqQE,7026 +pytest-8.3.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pytest-8.3.4.dist-info/LICENSE,sha256=yoNqX57Mo7LzUCMPqiCkj7ixRWU7VWjXhIYt-GRwa5s,1091 +pytest-8.3.4.dist-info/METADATA,sha256=f5v2O_PCDdT8dVKotHCLiHzXKMTS9hTO2YsKQ6_P3ig,7510 +pytest-8.3.4.dist-info/RECORD,, +pytest-8.3.4.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91 +pytest-8.3.4.dist-info/entry_points.txt,sha256=8IPrHPH3LNZQ7v5tNEOcNTZYk_SheNg64jsTM9erqL4,77 +pytest-8.3.4.dist-info/top_level.txt,sha256=yyhjvmXH7-JOaoQIdmNQHPuoBCxOyXS3jIths_6C8A4,18 +pytest/__init__.py,sha256=jm6h0ZECJdDXlX0i5F20mN3ypV--T7osmtMHzzzY8ug,5169 +pytest/__main__.py,sha256=oVDrGGo7N0TNyzXntUblcgTKbhHGWtivcX5TC7tEcKo,154 +pytest/__pycache__/__init__.cpython-311.pyc,, +pytest/__pycache__/__main__.cpython-311.pyc,, +pytest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ae527e7d64811439e61b93aa375defb30e06edfe --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.6.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/entry_points.txt b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..192205dfa5fda066e479ba073379747ae0abbba5 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +py.test = pytest:console_main +pytest = pytest:console_main diff --git a/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..3084ae51ecc94b5979ae9075ebb0e08fbda8bdbd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/top_level.txt @@ -0,0 +1,3 @@ +_pytest +py +pytest 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 0000000000000000000000000000000000000000..9bb6592b824bfa2e21d5c2024627effc67fa501f --- /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 0000000000000000000000000000000000000000..57f26b4742e36dae98cc7215c863901867a7abad 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 0000000000000000000000000000000000000000..e8dfc74731c82d9173382b8b13df0fc77fa882e5 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 0000000000000000000000000000000000000000..deeba543d6cfa1354d5331b0f9bfff4d87dd2851 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 0000000000000000000000000000000000000000..2279974b9d2d6857734d90aae3e8eb4ea8d0d5a8 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 0000000000000000000000000000000000000000..bfc14dccbb926df611d002a4a7620510a55d8be6 --- /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 0000000000000000000000000000000000000000..e620e1584546dc62fc367a6c502fc82f894addea --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/_testbase.py @@ -0,0 +1,552 @@ +"""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_SERVER'): + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + elif 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 0000000000000000000000000000000000000000..9e1722c0a291a4f97d41348551689908b0ba362c --- /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.21.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 0000000000000000000000000000000000000000..e594b133128fa7ca2c1321b7faa8a5412ffec5a7 --- /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 0000000000000000000000000000000000000000..2914b42e38ca2240a48fe64ac384f1eb5e065a56 --- /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 0000000000000000000000000000000000000000..67aeb595e951e26914458508c4963cdeafcadd9f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/dns.pyx @@ -0,0 +1,479 @@ +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) noexcept: + 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 + elif host == b'' and sys.platform == 'darwin': + # It seems `getaddrinfo("", ...)` on macOS is equivalent to + # `getaddrinfo("localhost", ...)`. This is inconsistent with + # libuv 1.48 which treats empty nodename as EINVAL. + chost = 'localhost' + else: + chost = host + + if port is None: + cport = NULL + else: + cport = port + + 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() + try: + if err == uv.UV_EINVAL: + # Convert UV_EINVAL to EAI_NONAME to match libc behavior + msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8') + ex = socket_gaierror(socket_EAI_NONAME, msg) + else: + ex = convert_error(err) + except Exception as ex: + callback(ex) + else: + callback(ex) + + +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 0000000000000000000000000000000000000000..d810d65ee58ad1dc40c33e2d54f5a3e8fcc1e64f --- /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 0000000000000000000000000000000000000000..5f0d820166b68ce1ab1ffaf4f5c3d43eadbdd895 --- /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 0000000000000000000000000000000000000000..5c740cfeb9c97339f2e2c0de840bec2cac2dd9c6 --- /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 0000000000000000000000000000000000000000..ba356a789441c8c58baf9f6ddb0c5a0e5b821ff9 --- /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 0000000000000000000000000000000000000000..28b30794799249c3af2d3274fbbac95b07ae8f4e --- /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 0000000000000000000000000000000000000000..86cfd8fb8e065bc3f662395d571a4b43eaae2a87 --- /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 0000000000000000000000000000000000000000..1a61c4e91bdd30a8cc238adca2c3d7c158afbfe0 --- /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 0000000000000000000000000000000000000000..3a32428506f73dc54c77088d50e602aa088596ce --- /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 0000000000000000000000000000000000000000..6ed6433ca410e829ef997f8dc519608e1948bea8 --- /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 0000000000000000000000000000000000000000..5af1c14cfcedbae283422797dfc7b1f1eb97af60 --- /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 0000000000000000000000000000000000000000..2c96458b1c897fd9a88211b683dd44d2d329ec09 --- /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) noexcept: + 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 0000000000000000000000000000000000000000..cf7b19f64b46551d815c4e6d4f6545e3d70b2636 --- /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 0000000000000000000000000000000000000000..91c641f6c0170de4cb9e7797a1179a2621198ef7 --- /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 0000000000000000000000000000000000000000..56fc2658bb1e223bbc342283bdf641aba77d4733 --- /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 0000000000000000000000000000000000000000..4b95ed6e9deb41bf3f014b06af673f1a18a55557 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx @@ -0,0 +1,247 @@ +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 + + cdef _close(self): + sock = self._fileobj + if sock is not None and sock in self._loop._unix_server_sockets: + path = sock.getsockname() + else: + path = None + + UVStreamServer._close(self) + + if path is not None: + prev_ino = self._loop._unix_server_sockets[sock] + del self._loop._unix_server_sockets[sock] + try: + if os_stat(path).st_ino == prev_ino: + os_unlink(path) + except FileNotFoundError: + pass + except OSError as err: + aio_logger.error('Unable to clean up listening UNIX socket ' + '%r: %r', path, err) + + +@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 0000000000000000000000000000000000000000..c220540269cde4f7a32d4170c966af05d046501d --- /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) noexcept + + 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 0000000000000000000000000000000000000000..c905e9b0b7ee9668099b98aea1db9ad2bbf53f4a --- /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) noexcept: + 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 0000000000000000000000000000000000000000..970abcfc84d73ea41516a81959f131ef507cb593 --- /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 0000000000000000000000000000000000000000..63b982ae597695edb63ac9b17740393bc5399786 --- /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 0000000000000000000000000000000000000000..8ca87437429dd3c3f474cb54712f43afffd1b7c5 --- /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 0000000000000000000000000000000000000000..9fbc5a51a41678c479f93482192d22fdb6e31e86 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx @@ -0,0 +1,1019 @@ +cdef extern from *: + ''' + enum {__PREALLOCED_BUFS = 4}; + ''' + const bint __PREALLOCED_BUFS + + +@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 0000000000000000000000000000000000000000..a004efd9b8df19cda9e2cb04eb9794e8f4a14487 --- /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 0000000000000000000000000000000000000000..99933177953ec00d138126634598abb32e034d24 --- /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 0000000000000000000000000000000000000000..8d388ef065f5fe3722abf53a4518c7a66d8b7aec --- /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 0000000000000000000000000000000000000000..d5fe827a07547be356976983dd93162620594f17 --- /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 0000000000000000000000000000000000000000..fda23b67c1f3e2987e67a956f77c9ca867836ac9 --- /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 0000000000000000000000000000000000000000..86d46ef02a76296c792f32be3b43182cc0937684 --- /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 0000000000000000000000000000000000000000..daa9a1beeb99cab30a00ad4f5aae76ef67f0c576 --- /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 0000000000000000000000000000000000000000..ef20c3f7cca717376c7e3ba7c259f8b980c33bd7 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/handles/udp.pyx @@ -0,0 +1,408 @@ +@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) + if isinstance(exc, OSError): + run_in_context1(self.context.copy(), self._protocol.error_received, exc) + else: + self._fatal_error(exc, True) + else: + self._maybe_pause_protocol() + + else: + self._on_sent(convert_error(err) if err < 0 else 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 0000000000000000000000000000000000000000..2ccf9cae31016b266b5a8a656b3aea0c3e571fc9 --- /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 0000000000000000000000000000000000000000..45e74eb2b0446501637e9e65a7fed18f88729fa6 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 0000000000000000000000000000000000000000..82f3c327fb26ed84ed81cc7d8422f3bf1359c49e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/includes/consts.pxi @@ -0,0 +1,33 @@ +cdef enum: + UV_STREAM_RECV_BUF_SIZE = 256000 # 250kb + + FLOW_CONTROL_HIGH_WATER = 64 # KiB + FLOW_CONTROL_HIGH_WATER_SSL_READ = 256 # KiB + FLOW_CONTROL_HIGH_WATER_SSL_WRITE = 512 # KiB + + DEFAULT_FREELIST_SIZE = 250 + DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE = 2048 + + DEBUG_STACK_DEPTH = 10 + + + __PROCESS_DEBUG_SLEEP_AFTER_FORK = 1 + + + LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5 + SSL_READ_MAX_SIZE = 256 * 1024 + + +cdef extern from *: + ''' + // Number of seconds to wait for SSL handshake to complete + // The default timeout matches that of Nginx. + #define SSL_HANDSHAKE_TIMEOUT 60.0 + + // Number of seconds to wait for SSL shutdown to complete + // The default timeout mimics lingering_time + #define SSL_SHUTDOWN_TIMEOUT 30.0 + ''' + + const float SSL_HANDSHAKE_TIMEOUT + const float SSL_SHUTDOWN_TIMEOUT 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 0000000000000000000000000000000000000000..a825def3c27511d9d860c2f4d0110fcb6561a8ff --- /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 0000000000000000000000000000000000000000..f22f1a7a541cb681b4c6f0f034bb1be248d189f1 --- /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 0000000000000000000000000000000000000000..94007e537381bb41af0749b1e67896db84e0266e --- /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 + + 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() + + void _Py_RestoreSignals() 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 0000000000000000000000000000000000000000..4152b8a7bb79e09c6971daec72c86a7cd7b23573 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/includes/stdlib.pxi @@ -0,0 +1,176 @@ +# 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_unlink = os.unlink +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 0000000000000000000000000000000000000000..367fedd1811b0978cd3431b89bbb0ab922166e48 --- /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 0000000000000000000000000000000000000000..510b14988ed20fd911d618adb9dd5ee42faed5c0 --- /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 + # use has_SO_REUSEPORT and SO_REUSEPORT in stdlib.pxi instead + 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 + + cdef 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) + uv_os_fd_t 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_os_fd_t 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.pxd b/.venv/lib/python3.11/site-packages/uvloop/loop.pxd new file mode 100644 index 0000000000000000000000000000000000000000..01e39ae12e24acfabfc6631acbbef55e32c473ce --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/loop.pxd @@ -0,0 +1,230 @@ +# 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 + dict _unix_server_sockets + + 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 0000000000000000000000000000000000000000..9c8c46239f8256e93f709ec81524345420ce1b85 --- /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 0000000000000000000000000000000000000000..f9a5a23916bb44122edb1320e6801dc3c55d91c4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/uvloop/loop.pyx @@ -0,0 +1,3424 @@ +# 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/stdlib.pxi" + +include "errors.pyx" + +cdef: + int PY39 = PY_VERSION_HEX >= 0x03090000 + int PY311 = PY_VERSION_HEX >= 0x030b0000 + int PY313 = PY_VERSION_HEX >= 0x030d0000 + 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._unix_server_sockets = {} + + 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 = 0 + 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._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, + # asyncio won't clean up socket file using create_server() API + cleanup_socket=False, + ) + + 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, 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, cleanup_socket=PY313): + """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. + + If *cleanup_socket* is true then the Unix socket will automatically + be removed from the filesystem when the server is closed, unless the + socket has been replaced after the server has been created. + This defaults to True on Python 3.13 and above, or False otherwise. + """ + 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) + + if cleanup_socket: + path = sock.getsockname() + # Check for abstract socket. `str` and `bytes` paths are supported. + if path[0] not in (0, '\x00'): + try: + self._unix_server_sockets[sock] = os_stat(path).st_ino + except FileNotFoundError: + pass + + 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=(), + **kwargs): + + # 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 + + # For tests only! Do not use in your code. Ever. + if kwargs.pop("__uvloop_sleep_after_fork", False): + debug_flags |= __PROCESS_DEBUG_SLEEP_AFTER_FORK + if kwargs: + raise ValueError( + 'unexpected kwargs: {}'.format(', '.join(kwargs.keys()))) + + 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 0000000000000000000000000000000000000000..cc7ea1dbc8aade8807a511daf6ea89521a07a13f --- /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 0000000000000000000000000000000000000000..10a1ad602e8307e655aa9247c4d2eef045bbe6b2 --- /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 0000000000000000000000000000000000000000..ef10f8131014516df68ef1a669811fbd63ff5b5b --- /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 0000000000000000000000000000000000000000..845bcfda451ebae9a0ac4c62dbca8f62443d0a59 --- /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 0000000000000000000000000000000000000000..3da10f00cf56ee227c7cda6212f73ca0acd34a1b --- /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 0000000000000000000000000000000000000000..42bb76447ce9b36506a7f494ce6b0825503d389a --- /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, + })