Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/METADATA +261 -0
- .venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/RECORD +9 -0
- .venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/WHEEL +5 -0
- .venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/top_level.txt +2 -0
- .venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/METADATA +327 -0
- .venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/RECORD +22 -0
- .venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/licenses/LICENSE +21 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/METADATA +212 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/RECORD +154 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/WHEEL +5 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/entry_points.txt +3 -0
- .venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/top_level.txt +3 -0
- .venv/lib/python3.11/site-packages/uvloop/__init__.py +168 -0
- .venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/uvloop/_noop.py +3 -0
- .venv/lib/python3.11/site-packages/uvloop/_testbase.py +552 -0
- .venv/lib/python3.11/site-packages/uvloop/_version.py +13 -0
- .venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd +39 -0
- .venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx +434 -0
- .venv/lib/python3.11/site-packages/uvloop/dns.pyx +479 -0
- .venv/lib/python3.11/site-packages/uvloop/errors.pyx +113 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd +11 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx +56 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd +54 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx +293 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/check.pxd +14 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/check.pyx +72 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd +12 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx +116 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd +48 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx +395 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd +14 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx +72 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd +33 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx +247 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd +25 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx +233 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/process.pxd +80 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/process.pyx +792 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd +50 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx +1019 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd +26 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx +150 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd +26 -0
- .venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx +228 -0
.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/METADATA
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.2
|
| 2 |
+
Name: nvidia-ml-py
|
| 3 |
+
Version: 12.570.86
|
| 4 |
+
Summary: Python Bindings for the NVIDIA Management Library
|
| 5 |
+
Home-page: https://forums.developer.nvidia.com
|
| 6 |
+
Author: NVIDIA Corporation
|
| 7 |
+
Author-email: nvml-bindings@nvidia.com
|
| 8 |
+
License: BSD
|
| 9 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 10 |
+
Classifier: Intended Audience :: Developers
|
| 11 |
+
Classifier: Intended Audience :: System Administrators
|
| 12 |
+
Classifier: License :: OSI Approved :: BSD License
|
| 13 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 14 |
+
Classifier: Operating System :: POSIX :: Linux
|
| 15 |
+
Classifier: Programming Language :: Python
|
| 16 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 17 |
+
Classifier: Topic :: System :: Hardware
|
| 18 |
+
Classifier: Topic :: System :: Systems Administration
|
| 19 |
+
Description-Content-Type: text/markdown
|
| 20 |
+
Dynamic: author
|
| 21 |
+
Dynamic: author-email
|
| 22 |
+
Dynamic: classifier
|
| 23 |
+
Dynamic: description
|
| 24 |
+
Dynamic: description-content-type
|
| 25 |
+
Dynamic: home-page
|
| 26 |
+
Dynamic: license
|
| 27 |
+
Dynamic: summary
|
| 28 |
+
|
| 29 |
+
pyNVML
|
| 30 |
+
======
|
| 31 |
+
|
| 32 |
+
Python bindings to the NVIDIA Management Library
|
| 33 |
+
------------------------------------------------
|
| 34 |
+
|
| 35 |
+
Provides a Python interface to GPU management and monitoring functions.
|
| 36 |
+
|
| 37 |
+
This is a wrapper around the NVML library.
|
| 38 |
+
For information about the NVML library, see the NVML developer page
|
| 39 |
+
http://developer.nvidia.com/nvidia-management-library-nvml
|
| 40 |
+
|
| 41 |
+
Download the latest package from:
|
| 42 |
+
http://pypi.python.org/pypi/nvidia-ml-py/
|
| 43 |
+
|
| 44 |
+
Note this file can be run with 'python -m doctest -v README.txt'
|
| 45 |
+
although the results are system dependent
|
| 46 |
+
|
| 47 |
+
The nvml header file contains function documentation that is relevant
|
| 48 |
+
to this wrapper. The header file is distributed with.
|
| 49 |
+
https://developer.nvidia.com/gpu-deployment-kit
|
| 50 |
+
|
| 51 |
+
The main difference is this library handles allocating structs and
|
| 52 |
+
passing pointers to the functions, before returning the desired value.
|
| 53 |
+
Non-success return codes are raised as exceptions as described in the
|
| 54 |
+
section below.
|
| 55 |
+
|
| 56 |
+
REQUIRES
|
| 57 |
+
--------
|
| 58 |
+
Python 2.5, or an earlier version with the ctypes module.
|
| 59 |
+
|
| 60 |
+
INSTALLATION
|
| 61 |
+
------------
|
| 62 |
+
|
| 63 |
+
Pip Installation with python3:
|
| 64 |
+
- `python3 -m pip install nvidia-ml-py`
|
| 65 |
+
|
| 66 |
+
Manual Installation:
|
| 67 |
+
```
|
| 68 |
+
$ tar -xzf nvidia-ml-py-$major-$minor-$patch.tar.gz`
|
| 69 |
+
$ cd nvidia-ml-py-$major-$minor-$patch
|
| 70 |
+
$ sudo python setup.py install
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
USAGE
|
| 74 |
+
-----
|
| 75 |
+
```
|
| 76 |
+
>>> from pynvml import *
|
| 77 |
+
>>> nvmlInit()
|
| 78 |
+
>>> print(f"Driver Version: {nvmlSystemGetDriverVersion()}")
|
| 79 |
+
Driver Version: 11.515.48
|
| 80 |
+
>>> deviceCount = nvmlDeviceGetCount()
|
| 81 |
+
>>> for i in range(deviceCount):
|
| 82 |
+
... handle = nvmlDeviceGetHandleByIndex(i)
|
| 83 |
+
... print(f"Device {i} : {nvmlDeviceGetName(handle)}")
|
| 84 |
+
...
|
| 85 |
+
Device 0 : Tesla K40c
|
| 86 |
+
|
| 87 |
+
>>> nvmlShutdown()
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
FUNCTIONS
|
| 91 |
+
---------
|
| 92 |
+
Python methods wrap NVML functions, implemented in a C shared library.
|
| 93 |
+
Each function's use is the same with the following exceptions:
|
| 94 |
+
|
| 95 |
+
- Instead of returning error codes, failing error codes are raised as Python exceptions.
|
| 96 |
+
|
| 97 |
+
```
|
| 98 |
+
>>> try:
|
| 99 |
+
... nvmlDeviceGetCount()
|
| 100 |
+
... except NVMLError as error:
|
| 101 |
+
... print(error)
|
| 102 |
+
...
|
| 103 |
+
Uninitialized
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
- C function output parameters are returned from the corresponding Python function left to right.
|
| 107 |
+
```
|
| 108 |
+
nvmlReturn_t nvmlDeviceGetEccMode(nvmlDevice_t device,
|
| 109 |
+
nvmlEnableState_t *current,
|
| 110 |
+
nvmlEnableState_t *pending);
|
| 111 |
+
|
| 112 |
+
>>> nvmlInit()
|
| 113 |
+
>>> handle = nvmlDeviceGetHandleByIndex(0)
|
| 114 |
+
>>> (current, pending) = nvmlDeviceGetEccMode(handle)
|
| 115 |
+
```
|
| 116 |
+
- C structs are converted into Python classes.
|
| 117 |
+
|
| 118 |
+
```
|
| 119 |
+
// C Function and typedef struct
|
| 120 |
+
nvmlReturn_t DECLDIR nvmlDeviceGetMemoryInfo(nvmlDevice_t device,
|
| 121 |
+
nvmlMemory_t *memory);
|
| 122 |
+
typedef struct nvmlMemory_st {
|
| 123 |
+
unsigned long long total;
|
| 124 |
+
unsigned long long free;
|
| 125 |
+
unsigned long long used;
|
| 126 |
+
} nvmlMemory_t;
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
# Python call to function and accessing members of ctype struct
|
| 130 |
+
>>> info = nvmlDeviceGetMemoryInfo(handle)
|
| 131 |
+
>>> print(f"Total memory: {info.total}")
|
| 132 |
+
Total memory: 5636292608
|
| 133 |
+
>>> print(f"Free memory:, {info.free}")
|
| 134 |
+
Free memory: 5578420224
|
| 135 |
+
>>> print(f"Used memory: {info.used}")
|
| 136 |
+
Used memory: 57872384
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
- Python handles string buffer creation.
|
| 140 |
+
|
| 141 |
+
```
|
| 142 |
+
// C Function that needs character array and length
|
| 143 |
+
nvmlReturn_t nvmlSystemGetDriverVersion(char* version,
|
| 144 |
+
unsigned int length);
|
| 145 |
+
|
| 146 |
+
# Python function handles memory
|
| 147 |
+
>>> version = nvmlSystemGetDriverVersion()
|
| 148 |
+
>>> print(version)
|
| 149 |
+
... 11.520.75
|
| 150 |
+
>>> nvmlShutdown()
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
For usage information see the NVML documentation.
|
| 154 |
+
|
| 155 |
+
VARIABLES
|
| 156 |
+
---------
|
| 157 |
+
All meaningful NVML constants and enums are exposed in Python.
|
| 158 |
+
|
| 159 |
+
The NVML_VALUE_NOT_AVAILABLE constant is not used. Instead None is mapped to the field.
|
| 160 |
+
|
| 161 |
+
EXCEPTIONS
|
| 162 |
+
----------
|
| 163 |
+
Since the C library uses return codes and python prefers exception handling, the
|
| 164 |
+
library converts all return codes to various exceptions. The exceptions are generated
|
| 165 |
+
automatically via a function at run time instead of being defined manually.
|
| 166 |
+
|
| 167 |
+
The list of exceptions can be found in NVMLError base class.
|
| 168 |
+
|
| 169 |
+
The example seen above in the FUNCTIONS section:
|
| 170 |
+
|
| 171 |
+
```
|
| 172 |
+
>>> try:
|
| 173 |
+
... nvmlDeviceGetCount()
|
| 174 |
+
... except NVMLError as error:
|
| 175 |
+
... print(error)
|
| 176 |
+
...
|
| 177 |
+
Uninitialized
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
Can be more accurately caught like this:
|
| 181 |
+
|
| 182 |
+
```
|
| 183 |
+
>>> try:
|
| 184 |
+
... nvmlDeviceGetCount()
|
| 185 |
+
... except NVMLError_Uninitialized as error:
|
| 186 |
+
... print(error)
|
| 187 |
+
...
|
| 188 |
+
Uninitialized
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
The conversion from name to exception is like this for all exceptions:
|
| 192 |
+
* `NVML_ERROR_UNINITIALIZED` => `NVMLError_Uninitialized`
|
| 193 |
+
* `NVML_ERROR_LIBRARY_NOT_FOUND` => `NVMLError_LibraryNotFound`
|
| 194 |
+
* `NVML_ERROR_ALREADY_INITIALIZED` => `NVMLError_AlreadyInitialized`
|
| 195 |
+
|
| 196 |
+
RELEASE NOTES
|
| 197 |
+
-------------
|
| 198 |
+
Version 2.285.0
|
| 199 |
+
- Added new functions for NVML 2.285. See NVML documentation for more information.
|
| 200 |
+
- Ported to support Python 3.0 and Python 2.0 syntax.
|
| 201 |
+
- Added nvidia_smi.py tool as a sample app.
|
| 202 |
+
|
| 203 |
+
Version 3.295.0
|
| 204 |
+
- Added new functions for NVML 3.295. See NVML documentation for more information.
|
| 205 |
+
- Updated nvidia_smi.py tool
|
| 206 |
+
- Includes additional error handling
|
| 207 |
+
|
| 208 |
+
Version 4.304.0
|
| 209 |
+
- Added new functions for NVML 4.304. See NVML documentation for more information.
|
| 210 |
+
- Updated nvidia_smi.py tool
|
| 211 |
+
|
| 212 |
+
Version 4.304.3
|
| 213 |
+
- Fixing nvmlUnitGetDeviceCount bug
|
| 214 |
+
|
| 215 |
+
Version 5.319.0
|
| 216 |
+
- Added new functions for NVML 5.319. See NVML documentation for more information.
|
| 217 |
+
|
| 218 |
+
Version 6.340.0
|
| 219 |
+
- Added new functions for NVML 6.340. See NVML documentation for more information.
|
| 220 |
+
|
| 221 |
+
Version 7.346.0
|
| 222 |
+
- Added new functions for NVML 7.346. See NVML documentation for more information.
|
| 223 |
+
|
| 224 |
+
Version 7.352.0
|
| 225 |
+
- Added new functions for NVML 7.352. See NVML documentation for more information.
|
| 226 |
+
|
| 227 |
+
Version 10.418
|
| 228 |
+
- Added new functions for NVML 10.418. See NVML documentation for more information.
|
| 229 |
+
- Fixed issues with using the bindings with Python 3.x
|
| 230 |
+
- Replaced sample app nvidia_smi.py with example.py
|
| 231 |
+
|
| 232 |
+
Version 11.515.48
|
| 233 |
+
- Python3 support added
|
| 234 |
+
- Updated API to add function new to NVML, bringing pynvml up to date with NVML
|
| 235 |
+
- Added auto-version to handle byte and string conversion automatically for both structs and functions
|
| 236 |
+
- Minor bug fixes
|
| 237 |
+
- Added README.txt correctly in long_description for pypi.org
|
| 238 |
+
|
| 239 |
+
Version 11.520
|
| 240 |
+
- Updated Long Description to be actual markdown
|
| 241 |
+
- Added new functions for NVML 11.520
|
| 242 |
+
|
| 243 |
+
Version 11.525
|
| 244 |
+
- Added new functions for NVML 11.525
|
| 245 |
+
|
| 246 |
+
COPYRIGHT
|
| 247 |
+
---------
|
| 248 |
+
Copyright (c) 2011-2023, NVIDIA Corporation. All rights reserved.
|
| 249 |
+
|
| 250 |
+
LICENSE
|
| 251 |
+
-------
|
| 252 |
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
| 253 |
+
|
| 254 |
+
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
| 255 |
+
|
| 256 |
+
- 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.
|
| 257 |
+
|
| 258 |
+
- 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.
|
| 259 |
+
|
| 260 |
+
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.
|
| 261 |
+
|
.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/RECORD
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/example.cpython-311.pyc,,
|
| 2 |
+
__pycache__/pynvml.cpython-311.pyc,,
|
| 3 |
+
example.py,sha256=mDXwPVyEDuiKeMApEh53r_M36xuncmzMpFOGA3Q-_Kw,7968
|
| 4 |
+
nvidia_ml_py-12.570.86.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 5 |
+
nvidia_ml_py-12.570.86.dist-info/METADATA,sha256=vY-jfk5MJsbWGy2jmbgdwfPKG4G0FHbspv-av_h5bEE,8718
|
| 6 |
+
nvidia_ml_py-12.570.86.dist-info/RECORD,,
|
| 7 |
+
nvidia_ml_py-12.570.86.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
| 8 |
+
nvidia_ml_py-12.570.86.dist-info/top_level.txt,sha256=wLINSA1WKnhsGgKsb_nuj51ZCQrXaN5qhioTL56g98A,15
|
| 9 |
+
pynvml.py,sha256=PCW5qJPhGshkIhIUOOQyUXsxkCVaPeTi30R7LsJb4YE,234473
|
.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (75.8.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
.venv/lib/python3.11/site-packages/nvidia_ml_py-12.570.86.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
example
|
| 2 |
+
pynvml
|
.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/METADATA
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.3
|
| 2 |
+
Name: platformdirs
|
| 3 |
+
Version: 4.3.6
|
| 4 |
+
Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.
|
| 5 |
+
Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases
|
| 6 |
+
Project-URL: Documentation, https://platformdirs.readthedocs.io
|
| 7 |
+
Project-URL: Homepage, https://github.com/tox-dev/platformdirs
|
| 8 |
+
Project-URL: Source, https://github.com/tox-dev/platformdirs
|
| 9 |
+
Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues
|
| 10 |
+
Maintainer-email: Bernát Gábor <gaborjbernat@gmail.com>, Julian Berman <Julian@GrayVines.com>, Ofek Lev <oss@ofek.dev>, Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
|
| 11 |
+
License-Expression: MIT
|
| 12 |
+
License-File: LICENSE
|
| 13 |
+
Keywords: appdirs,application,cache,directory,log,user
|
| 14 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 15 |
+
Classifier: Intended Audience :: Developers
|
| 16 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 17 |
+
Classifier: Operating System :: OS Independent
|
| 18 |
+
Classifier: Programming Language :: Python
|
| 19 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 26 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 27 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 28 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 29 |
+
Requires-Python: >=3.8
|
| 30 |
+
Provides-Extra: docs
|
| 31 |
+
Requires-Dist: furo>=2024.8.6; extra == 'docs'
|
| 32 |
+
Requires-Dist: proselint>=0.14; extra == 'docs'
|
| 33 |
+
Requires-Dist: sphinx-autodoc-typehints>=2.4; extra == 'docs'
|
| 34 |
+
Requires-Dist: sphinx>=8.0.2; extra == 'docs'
|
| 35 |
+
Provides-Extra: test
|
| 36 |
+
Requires-Dist: appdirs==1.4.4; extra == 'test'
|
| 37 |
+
Requires-Dist: covdefaults>=2.3; extra == 'test'
|
| 38 |
+
Requires-Dist: pytest-cov>=5; extra == 'test'
|
| 39 |
+
Requires-Dist: pytest-mock>=3.14; extra == 'test'
|
| 40 |
+
Requires-Dist: pytest>=8.3.2; extra == 'test'
|
| 41 |
+
Provides-Extra: type
|
| 42 |
+
Requires-Dist: mypy>=1.11.2; extra == 'type'
|
| 43 |
+
Description-Content-Type: text/x-rst
|
| 44 |
+
|
| 45 |
+
The problem
|
| 46 |
+
===========
|
| 47 |
+
|
| 48 |
+
.. image:: https://badge.fury.io/py/platformdirs.svg
|
| 49 |
+
:target: https://badge.fury.io/py/platformdirs
|
| 50 |
+
.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg
|
| 51 |
+
:target: https://pypi.python.org/pypi/platformdirs/
|
| 52 |
+
.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg
|
| 53 |
+
:target: https://github.com/platformdirs/platformdirs/actions
|
| 54 |
+
.. image:: https://static.pepy.tech/badge/platformdirs/month
|
| 55 |
+
:target: https://pepy.tech/project/platformdirs
|
| 56 |
+
|
| 57 |
+
When writing desktop application, finding the right location to store user data
|
| 58 |
+
and configuration varies per platform. Even for single-platform apps, there
|
| 59 |
+
may by plenty of nuances in figuring out the right location.
|
| 60 |
+
|
| 61 |
+
For example, if running on macOS, you should use::
|
| 62 |
+
|
| 63 |
+
~/Library/Application Support/<AppName>
|
| 64 |
+
|
| 65 |
+
If on Windows (at least English Win) that should be::
|
| 66 |
+
|
| 67 |
+
C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
|
| 68 |
+
|
| 69 |
+
or possibly::
|
| 70 |
+
|
| 71 |
+
C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
|
| 72 |
+
|
| 73 |
+
for `roaming profiles <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc766489(v=ws.10)>`_ but that is another story.
|
| 74 |
+
|
| 75 |
+
On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be::
|
| 76 |
+
|
| 77 |
+
~/.local/share/<AppName>
|
| 78 |
+
|
| 79 |
+
.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
| 80 |
+
|
| 81 |
+
``platformdirs`` to the rescue
|
| 82 |
+
==============================
|
| 83 |
+
|
| 84 |
+
This kind of thing is what the ``platformdirs`` package is for.
|
| 85 |
+
``platformdirs`` will help you choose an appropriate:
|
| 86 |
+
|
| 87 |
+
- user data dir (``user_data_dir``)
|
| 88 |
+
- user config dir (``user_config_dir``)
|
| 89 |
+
- user cache dir (``user_cache_dir``)
|
| 90 |
+
- site data dir (``site_data_dir``)
|
| 91 |
+
- site config dir (``site_config_dir``)
|
| 92 |
+
- user log dir (``user_log_dir``)
|
| 93 |
+
- user documents dir (``user_documents_dir``)
|
| 94 |
+
- user downloads dir (``user_downloads_dir``)
|
| 95 |
+
- user pictures dir (``user_pictures_dir``)
|
| 96 |
+
- user videos dir (``user_videos_dir``)
|
| 97 |
+
- user music dir (``user_music_dir``)
|
| 98 |
+
- user desktop dir (``user_desktop_dir``)
|
| 99 |
+
- user runtime dir (``user_runtime_dir``)
|
| 100 |
+
|
| 101 |
+
And also:
|
| 102 |
+
|
| 103 |
+
- Is slightly opinionated on the directory names used. Look for "OPINION" in
|
| 104 |
+
documentation and code for when an opinion is being applied.
|
| 105 |
+
|
| 106 |
+
Example output
|
| 107 |
+
==============
|
| 108 |
+
|
| 109 |
+
On macOS:
|
| 110 |
+
|
| 111 |
+
.. code-block:: pycon
|
| 112 |
+
|
| 113 |
+
>>> from platformdirs import *
|
| 114 |
+
>>> appname = "SuperApp"
|
| 115 |
+
>>> appauthor = "Acme"
|
| 116 |
+
>>> user_data_dir(appname, appauthor)
|
| 117 |
+
'/Users/trentm/Library/Application Support/SuperApp'
|
| 118 |
+
>>> site_data_dir(appname, appauthor)
|
| 119 |
+
'/Library/Application Support/SuperApp'
|
| 120 |
+
>>> user_cache_dir(appname, appauthor)
|
| 121 |
+
'/Users/trentm/Library/Caches/SuperApp'
|
| 122 |
+
>>> user_log_dir(appname, appauthor)
|
| 123 |
+
'/Users/trentm/Library/Logs/SuperApp'
|
| 124 |
+
>>> user_documents_dir()
|
| 125 |
+
'/Users/trentm/Documents'
|
| 126 |
+
>>> user_downloads_dir()
|
| 127 |
+
'/Users/trentm/Downloads'
|
| 128 |
+
>>> user_pictures_dir()
|
| 129 |
+
'/Users/trentm/Pictures'
|
| 130 |
+
>>> user_videos_dir()
|
| 131 |
+
'/Users/trentm/Movies'
|
| 132 |
+
>>> user_music_dir()
|
| 133 |
+
'/Users/trentm/Music'
|
| 134 |
+
>>> user_desktop_dir()
|
| 135 |
+
'/Users/trentm/Desktop'
|
| 136 |
+
>>> user_runtime_dir(appname, appauthor)
|
| 137 |
+
'/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
|
| 138 |
+
|
| 139 |
+
On Windows:
|
| 140 |
+
|
| 141 |
+
.. code-block:: pycon
|
| 142 |
+
|
| 143 |
+
>>> from platformdirs import *
|
| 144 |
+
>>> appname = "SuperApp"
|
| 145 |
+
>>> appauthor = "Acme"
|
| 146 |
+
>>> user_data_dir(appname, appauthor)
|
| 147 |
+
'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
|
| 148 |
+
>>> user_data_dir(appname, appauthor, roaming=True)
|
| 149 |
+
'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
|
| 150 |
+
>>> user_cache_dir(appname, appauthor)
|
| 151 |
+
'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
|
| 152 |
+
>>> user_log_dir(appname, appauthor)
|
| 153 |
+
'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
|
| 154 |
+
>>> user_documents_dir()
|
| 155 |
+
'C:\\Users\\trentm\\Documents'
|
| 156 |
+
>>> user_downloads_dir()
|
| 157 |
+
'C:\\Users\\trentm\\Downloads'
|
| 158 |
+
>>> user_pictures_dir()
|
| 159 |
+
'C:\\Users\\trentm\\Pictures'
|
| 160 |
+
>>> user_videos_dir()
|
| 161 |
+
'C:\\Users\\trentm\\Videos'
|
| 162 |
+
>>> user_music_dir()
|
| 163 |
+
'C:\\Users\\trentm\\Music'
|
| 164 |
+
>>> user_desktop_dir()
|
| 165 |
+
'C:\\Users\\trentm\\Desktop'
|
| 166 |
+
>>> user_runtime_dir(appname, appauthor)
|
| 167 |
+
'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp'
|
| 168 |
+
|
| 169 |
+
On Linux:
|
| 170 |
+
|
| 171 |
+
.. code-block:: pycon
|
| 172 |
+
|
| 173 |
+
>>> from platformdirs import *
|
| 174 |
+
>>> appname = "SuperApp"
|
| 175 |
+
>>> appauthor = "Acme"
|
| 176 |
+
>>> user_data_dir(appname, appauthor)
|
| 177 |
+
'/home/trentm/.local/share/SuperApp'
|
| 178 |
+
>>> site_data_dir(appname, appauthor)
|
| 179 |
+
'/usr/local/share/SuperApp'
|
| 180 |
+
>>> site_data_dir(appname, appauthor, multipath=True)
|
| 181 |
+
'/usr/local/share/SuperApp:/usr/share/SuperApp'
|
| 182 |
+
>>> user_cache_dir(appname, appauthor)
|
| 183 |
+
'/home/trentm/.cache/SuperApp'
|
| 184 |
+
>>> user_log_dir(appname, appauthor)
|
| 185 |
+
'/home/trentm/.local/state/SuperApp/log'
|
| 186 |
+
>>> user_config_dir(appname)
|
| 187 |
+
'/home/trentm/.config/SuperApp'
|
| 188 |
+
>>> user_documents_dir()
|
| 189 |
+
'/home/trentm/Documents'
|
| 190 |
+
>>> user_downloads_dir()
|
| 191 |
+
'/home/trentm/Downloads'
|
| 192 |
+
>>> user_pictures_dir()
|
| 193 |
+
'/home/trentm/Pictures'
|
| 194 |
+
>>> user_videos_dir()
|
| 195 |
+
'/home/trentm/Videos'
|
| 196 |
+
>>> user_music_dir()
|
| 197 |
+
'/home/trentm/Music'
|
| 198 |
+
>>> user_desktop_dir()
|
| 199 |
+
'/home/trentm/Desktop'
|
| 200 |
+
>>> user_runtime_dir(appname, appauthor)
|
| 201 |
+
'/run/user/{os.getuid()}/SuperApp'
|
| 202 |
+
>>> site_config_dir(appname)
|
| 203 |
+
'/etc/xdg/SuperApp'
|
| 204 |
+
>>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc"
|
| 205 |
+
>>> site_config_dir(appname, multipath=True)
|
| 206 |
+
'/etc/SuperApp:/usr/local/etc/SuperApp'
|
| 207 |
+
|
| 208 |
+
On Android::
|
| 209 |
+
|
| 210 |
+
>>> from platformdirs import *
|
| 211 |
+
>>> appname = "SuperApp"
|
| 212 |
+
>>> appauthor = "Acme"
|
| 213 |
+
>>> user_data_dir(appname, appauthor)
|
| 214 |
+
'/data/data/com.myApp/files/SuperApp'
|
| 215 |
+
>>> user_cache_dir(appname, appauthor)
|
| 216 |
+
'/data/data/com.myApp/cache/SuperApp'
|
| 217 |
+
>>> user_log_dir(appname, appauthor)
|
| 218 |
+
'/data/data/com.myApp/cache/SuperApp/log'
|
| 219 |
+
>>> user_config_dir(appname)
|
| 220 |
+
'/data/data/com.myApp/shared_prefs/SuperApp'
|
| 221 |
+
>>> user_documents_dir()
|
| 222 |
+
'/storage/emulated/0/Documents'
|
| 223 |
+
>>> user_downloads_dir()
|
| 224 |
+
'/storage/emulated/0/Downloads'
|
| 225 |
+
>>> user_pictures_dir()
|
| 226 |
+
'/storage/emulated/0/Pictures'
|
| 227 |
+
>>> user_videos_dir()
|
| 228 |
+
'/storage/emulated/0/DCIM/Camera'
|
| 229 |
+
>>> user_music_dir()
|
| 230 |
+
'/storage/emulated/0/Music'
|
| 231 |
+
>>> user_desktop_dir()
|
| 232 |
+
'/storage/emulated/0/Desktop'
|
| 233 |
+
>>> user_runtime_dir(appname, appauthor)
|
| 234 |
+
'/data/data/com.myApp/cache/SuperApp/tmp'
|
| 235 |
+
|
| 236 |
+
Note: Some android apps like Termux and Pydroid are used as shells. These
|
| 237 |
+
apps are used by the end user to emulate Linux environment. Presence of
|
| 238 |
+
``SHELL`` environment variable is used by Platformdirs to differentiate
|
| 239 |
+
between general android apps and android apps used as shells. Shell android
|
| 240 |
+
apps also support ``XDG_*`` environment variables.
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
``PlatformDirs`` for convenience
|
| 244 |
+
================================
|
| 245 |
+
|
| 246 |
+
.. code-block:: pycon
|
| 247 |
+
|
| 248 |
+
>>> from platformdirs import PlatformDirs
|
| 249 |
+
>>> dirs = PlatformDirs("SuperApp", "Acme")
|
| 250 |
+
>>> dirs.user_data_dir
|
| 251 |
+
'/Users/trentm/Library/Application Support/SuperApp'
|
| 252 |
+
>>> dirs.site_data_dir
|
| 253 |
+
'/Library/Application Support/SuperApp'
|
| 254 |
+
>>> dirs.user_cache_dir
|
| 255 |
+
'/Users/trentm/Library/Caches/SuperApp'
|
| 256 |
+
>>> dirs.user_log_dir
|
| 257 |
+
'/Users/trentm/Library/Logs/SuperApp'
|
| 258 |
+
>>> dirs.user_documents_dir
|
| 259 |
+
'/Users/trentm/Documents'
|
| 260 |
+
>>> dirs.user_downloads_dir
|
| 261 |
+
'/Users/trentm/Downloads'
|
| 262 |
+
>>> dirs.user_pictures_dir
|
| 263 |
+
'/Users/trentm/Pictures'
|
| 264 |
+
>>> dirs.user_videos_dir
|
| 265 |
+
'/Users/trentm/Movies'
|
| 266 |
+
>>> dirs.user_music_dir
|
| 267 |
+
'/Users/trentm/Music'
|
| 268 |
+
>>> dirs.user_desktop_dir
|
| 269 |
+
'/Users/trentm/Desktop'
|
| 270 |
+
>>> dirs.user_runtime_dir
|
| 271 |
+
'/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
|
| 272 |
+
|
| 273 |
+
Per-version isolation
|
| 274 |
+
=====================
|
| 275 |
+
|
| 276 |
+
If you have multiple versions of your app in use that you want to be
|
| 277 |
+
able to run side-by-side, then you may want version-isolation for these
|
| 278 |
+
dirs::
|
| 279 |
+
|
| 280 |
+
>>> from platformdirs import PlatformDirs
|
| 281 |
+
>>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0")
|
| 282 |
+
>>> dirs.user_data_dir
|
| 283 |
+
'/Users/trentm/Library/Application Support/SuperApp/1.0'
|
| 284 |
+
>>> dirs.site_data_dir
|
| 285 |
+
'/Library/Application Support/SuperApp/1.0'
|
| 286 |
+
>>> dirs.user_cache_dir
|
| 287 |
+
'/Users/trentm/Library/Caches/SuperApp/1.0'
|
| 288 |
+
>>> dirs.user_log_dir
|
| 289 |
+
'/Users/trentm/Library/Logs/SuperApp/1.0'
|
| 290 |
+
>>> dirs.user_documents_dir
|
| 291 |
+
'/Users/trentm/Documents'
|
| 292 |
+
>>> dirs.user_downloads_dir
|
| 293 |
+
'/Users/trentm/Downloads'
|
| 294 |
+
>>> dirs.user_pictures_dir
|
| 295 |
+
'/Users/trentm/Pictures'
|
| 296 |
+
>>> dirs.user_videos_dir
|
| 297 |
+
'/Users/trentm/Movies'
|
| 298 |
+
>>> dirs.user_music_dir
|
| 299 |
+
'/Users/trentm/Music'
|
| 300 |
+
>>> dirs.user_desktop_dir
|
| 301 |
+
'/Users/trentm/Desktop'
|
| 302 |
+
>>> dirs.user_runtime_dir
|
| 303 |
+
'/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0'
|
| 304 |
+
|
| 305 |
+
Be wary of using this for configuration files though; you'll need to handle
|
| 306 |
+
migrating configuration files manually.
|
| 307 |
+
|
| 308 |
+
Why this Fork?
|
| 309 |
+
==============
|
| 310 |
+
|
| 311 |
+
This repository is a friendly fork of the wonderful work started by
|
| 312 |
+
`ActiveState <https://github.com/ActiveState/appdirs>`_ who created
|
| 313 |
+
``appdirs``, this package's ancestor.
|
| 314 |
+
|
| 315 |
+
Maintaining an open source project is no easy task, particularly
|
| 316 |
+
from within an organization, and the Python community is indebted
|
| 317 |
+
to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for
|
| 318 |
+
creating an incredibly useful simple module, as evidenced by the wide
|
| 319 |
+
number of users it has attracted over the years.
|
| 320 |
+
|
| 321 |
+
Nonetheless, given the number of long-standing open issues
|
| 322 |
+
and pull requests, and no clear path towards `ensuring
|
| 323 |
+
that maintenance of the package would continue or grow
|
| 324 |
+
<https://github.com/ActiveState/appdirs/issues/79>`_, this fork was
|
| 325 |
+
created.
|
| 326 |
+
|
| 327 |
+
Contributions are most welcome.
|
.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/RECORD
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
platformdirs-4.3.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
platformdirs-4.3.6.dist-info/METADATA,sha256=085GgRFo5U1nc9NR8e6unEWKxUjDMsgSHDyaCETsCQ4,11868
|
| 3 |
+
platformdirs-4.3.6.dist-info/RECORD,,
|
| 4 |
+
platformdirs-4.3.6.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
| 5 |
+
platformdirs-4.3.6.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
|
| 6 |
+
platformdirs/__init__.py,sha256=mVCfMmBM4q24lq6336V3VJncdxaOegI4qQSmQCjkR5E,22284
|
| 7 |
+
platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493
|
| 8 |
+
platformdirs/__pycache__/__init__.cpython-311.pyc,,
|
| 9 |
+
platformdirs/__pycache__/__main__.cpython-311.pyc,,
|
| 10 |
+
platformdirs/__pycache__/android.cpython-311.pyc,,
|
| 11 |
+
platformdirs/__pycache__/api.cpython-311.pyc,,
|
| 12 |
+
platformdirs/__pycache__/macos.cpython-311.pyc,,
|
| 13 |
+
platformdirs/__pycache__/unix.cpython-311.pyc,,
|
| 14 |
+
platformdirs/__pycache__/version.cpython-311.pyc,,
|
| 15 |
+
platformdirs/__pycache__/windows.cpython-311.pyc,,
|
| 16 |
+
platformdirs/android.py,sha256=kV5oL3V3DZ6WZKu9yFiQupv18yp_jlSV2ChH1TmPcds,9007
|
| 17 |
+
platformdirs/api.py,sha256=2dfUDNbEXeDhDKarqtR5NY7oUikUZ4RZhs3ozstmhBQ,9246
|
| 18 |
+
platformdirs/macos.py,sha256=UlbyFZ8Rzu3xndCqQEHrfsYTeHwYdFap1Ioz-yxveT4,6154
|
| 19 |
+
platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 20 |
+
platformdirs/unix.py,sha256=uRPJWRyQEtv7yOSvU94rUmsblo5XKDLA1SzFg55kbK0,10393
|
| 21 |
+
platformdirs/version.py,sha256=oH4KgTfK4AklbTYVcV_yynvJ9JLI3pyvDVay0hRsLCs,411
|
| 22 |
+
platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
|
.venv/lib/python3.11/site-packages/platformdirs-4.3.6.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2010-202x The platformdirs developers
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/METADATA
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: pytest
|
| 3 |
+
Version: 8.3.4
|
| 4 |
+
Summary: pytest: simple powerful testing with Python
|
| 5 |
+
Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin, Others (See AUTHORS)
|
| 6 |
+
License: MIT
|
| 7 |
+
Project-URL: Changelog, https://docs.pytest.org/en/stable/changelog.html
|
| 8 |
+
Project-URL: Homepage, https://docs.pytest.org/en/latest/
|
| 9 |
+
Project-URL: Source, https://github.com/pytest-dev/pytest
|
| 10 |
+
Project-URL: Tracker, https://github.com/pytest-dev/pytest/issues
|
| 11 |
+
Project-URL: Twitter, https://twitter.com/pytestdotorg
|
| 12 |
+
Keywords: test,unittest
|
| 13 |
+
Classifier: Development Status :: 6 - Mature
|
| 14 |
+
Classifier: Intended Audience :: Developers
|
| 15 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 16 |
+
Classifier: Operating System :: MacOS
|
| 17 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 18 |
+
Classifier: Operating System :: POSIX
|
| 19 |
+
Classifier: Operating System :: Unix
|
| 20 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 26 |
+
Classifier: Topic :: Software Development :: Libraries
|
| 27 |
+
Classifier: Topic :: Software Development :: Testing
|
| 28 |
+
Classifier: Topic :: Utilities
|
| 29 |
+
Requires-Python: >=3.8
|
| 30 |
+
Description-Content-Type: text/x-rst
|
| 31 |
+
License-File: LICENSE
|
| 32 |
+
License-File: AUTHORS
|
| 33 |
+
Requires-Dist: colorama; sys_platform == "win32"
|
| 34 |
+
Requires-Dist: exceptiongroup>=1.0.0rc8; python_version < "3.11"
|
| 35 |
+
Requires-Dist: iniconfig
|
| 36 |
+
Requires-Dist: packaging
|
| 37 |
+
Requires-Dist: pluggy<2,>=1.5
|
| 38 |
+
Requires-Dist: tomli>=1; python_version < "3.11"
|
| 39 |
+
Provides-Extra: dev
|
| 40 |
+
Requires-Dist: argcomplete; extra == "dev"
|
| 41 |
+
Requires-Dist: attrs>=19.2; extra == "dev"
|
| 42 |
+
Requires-Dist: hypothesis>=3.56; extra == "dev"
|
| 43 |
+
Requires-Dist: mock; extra == "dev"
|
| 44 |
+
Requires-Dist: pygments>=2.7.2; extra == "dev"
|
| 45 |
+
Requires-Dist: requests; extra == "dev"
|
| 46 |
+
Requires-Dist: setuptools; extra == "dev"
|
| 47 |
+
Requires-Dist: xmlschema; extra == "dev"
|
| 48 |
+
|
| 49 |
+
.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg
|
| 50 |
+
:target: https://docs.pytest.org/en/stable/
|
| 51 |
+
:align: center
|
| 52 |
+
:height: 200
|
| 53 |
+
:alt: pytest
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
------
|
| 57 |
+
|
| 58 |
+
.. image:: https://img.shields.io/pypi/v/pytest.svg
|
| 59 |
+
:target: https://pypi.org/project/pytest/
|
| 60 |
+
|
| 61 |
+
.. image:: https://img.shields.io/conda/vn/conda-forge/pytest.svg
|
| 62 |
+
:target: https://anaconda.org/conda-forge/pytest
|
| 63 |
+
|
| 64 |
+
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
|
| 65 |
+
:target: https://pypi.org/project/pytest/
|
| 66 |
+
|
| 67 |
+
.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg
|
| 68 |
+
:target: https://codecov.io/gh/pytest-dev/pytest
|
| 69 |
+
:alt: Code coverage Status
|
| 70 |
+
|
| 71 |
+
.. image:: https://github.com/pytest-dev/pytest/actions/workflows/test.yml/badge.svg
|
| 72 |
+
:target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest
|
| 73 |
+
|
| 74 |
+
.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg
|
| 75 |
+
:target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main
|
| 76 |
+
:alt: pre-commit.ci status
|
| 77 |
+
|
| 78 |
+
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
| 79 |
+
:target: https://www.codetriage.com/pytest-dev/pytest
|
| 80 |
+
|
| 81 |
+
.. image:: https://readthedocs.org/projects/pytest/badge/?version=latest
|
| 82 |
+
:target: https://pytest.readthedocs.io/en/latest/?badge=latest
|
| 83 |
+
:alt: Documentation Status
|
| 84 |
+
|
| 85 |
+
.. image:: https://img.shields.io/badge/Discord-pytest--dev-blue
|
| 86 |
+
:target: https://discord.com/invite/pytest-dev
|
| 87 |
+
:alt: Discord
|
| 88 |
+
|
| 89 |
+
.. image:: https://img.shields.io/badge/Libera%20chat-%23pytest-orange
|
| 90 |
+
:target: https://web.libera.chat/#pytest
|
| 91 |
+
:alt: Libera chat
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
The ``pytest`` framework makes it easy to write small tests, yet
|
| 95 |
+
scales to support complex functional testing for applications and libraries.
|
| 96 |
+
|
| 97 |
+
An example of a simple test:
|
| 98 |
+
|
| 99 |
+
.. code-block:: python
|
| 100 |
+
|
| 101 |
+
# content of test_sample.py
|
| 102 |
+
def inc(x):
|
| 103 |
+
return x + 1
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_answer():
|
| 107 |
+
assert inc(3) == 5
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
To execute it::
|
| 111 |
+
|
| 112 |
+
$ pytest
|
| 113 |
+
============================= test session starts =============================
|
| 114 |
+
collected 1 items
|
| 115 |
+
|
| 116 |
+
test_sample.py F
|
| 117 |
+
|
| 118 |
+
================================== FAILURES ===================================
|
| 119 |
+
_________________________________ test_answer _________________________________
|
| 120 |
+
|
| 121 |
+
def test_answer():
|
| 122 |
+
> assert inc(3) == 5
|
| 123 |
+
E assert 4 == 5
|
| 124 |
+
E + where 4 = inc(3)
|
| 125 |
+
|
| 126 |
+
test_sample.py:5: AssertionError
|
| 127 |
+
========================== 1 failed in 0.04 seconds ===========================
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <https://docs.pytest.org/en/stable/getting-started.html#our-first-test-run>`_ for more examples.
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
Features
|
| 134 |
+
--------
|
| 135 |
+
|
| 136 |
+
- Detailed info on failing `assert statements <https://docs.pytest.org/en/stable/how-to/assert.html>`_ (no need to remember ``self.assert*`` names)
|
| 137 |
+
|
| 138 |
+
- `Auto-discovery
|
| 139 |
+
<https://docs.pytest.org/en/stable/explanation/goodpractices.html#python-test-discovery>`_
|
| 140 |
+
of test modules and functions
|
| 141 |
+
|
| 142 |
+
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
|
| 143 |
+
managing small or parametrized long-lived test resources
|
| 144 |
+
|
| 145 |
+
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial)
|
| 146 |
+
test suites out of the box
|
| 147 |
+
|
| 148 |
+
- Python 3.8+ or PyPy3
|
| 149 |
+
|
| 150 |
+
- Rich plugin architecture, with over 1300+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
Documentation
|
| 154 |
+
-------------
|
| 155 |
+
|
| 156 |
+
For full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/stable/.
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
Bugs/Requests
|
| 160 |
+
-------------
|
| 161 |
+
|
| 162 |
+
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
Changelog
|
| 166 |
+
---------
|
| 167 |
+
|
| 168 |
+
Consult the `Changelog <https://docs.pytest.org/en/stable/changelog.html>`__ page for fixes and enhancements of each version.
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
Support pytest
|
| 172 |
+
--------------
|
| 173 |
+
|
| 174 |
+
`Open Collective`_ is an online funding platform for open and transparent communities.
|
| 175 |
+
It provides tools to raise money and share your finances in full transparency.
|
| 176 |
+
|
| 177 |
+
It is the platform of choice for individuals and companies that want to make one-time or
|
| 178 |
+
monthly donations directly to the project.
|
| 179 |
+
|
| 180 |
+
See more details in the `pytest collective`_.
|
| 181 |
+
|
| 182 |
+
.. _Open Collective: https://opencollective.com
|
| 183 |
+
.. _pytest collective: https://opencollective.com/pytest
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
pytest for enterprise
|
| 187 |
+
---------------------
|
| 188 |
+
|
| 189 |
+
Available as part of the Tidelift Subscription.
|
| 190 |
+
|
| 191 |
+
The maintainers of pytest and thousands of other packages are working with Tidelift to deliver commercial support and
|
| 192 |
+
maintenance for the open source dependencies you use to build your applications.
|
| 193 |
+
Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
|
| 194 |
+
|
| 195 |
+
`Learn more. <https://tidelift.com/subscription/pkg/pypi-pytest?utm_source=pypi-pytest&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`_
|
| 196 |
+
|
| 197 |
+
Security
|
| 198 |
+
^^^^^^^^
|
| 199 |
+
|
| 200 |
+
pytest has never been associated with a security vulnerability, but in any case, to report a
|
| 201 |
+
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
| 202 |
+
Tidelift will coordinate the fix and disclosure.
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
License
|
| 206 |
+
-------
|
| 207 |
+
|
| 208 |
+
Copyright Holger Krekel and others, 2004.
|
| 209 |
+
|
| 210 |
+
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
| 211 |
+
|
| 212 |
+
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/RECORD
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/py.test,sha256=vvHOr9KK5AGKvqYrdl0oQCYePyyaJI8PmXGxzS68qLc,239
|
| 2 |
+
../../../bin/pytest,sha256=vvHOr9KK5AGKvqYrdl0oQCYePyyaJI8PmXGxzS68qLc,239
|
| 3 |
+
__pycache__/py.cpython-311.pyc,,
|
| 4 |
+
_pytest/__init__.py,sha256=4IdRJhnW5XG2KlaJkOxn5_TC9WeQ5tXDSF7tbb4vEso,391
|
| 5 |
+
_pytest/__pycache__/__init__.cpython-311.pyc,,
|
| 6 |
+
_pytest/__pycache__/_argcomplete.cpython-311.pyc,,
|
| 7 |
+
_pytest/__pycache__/_version.cpython-311.pyc,,
|
| 8 |
+
_pytest/__pycache__/cacheprovider.cpython-311.pyc,,
|
| 9 |
+
_pytest/__pycache__/capture.cpython-311.pyc,,
|
| 10 |
+
_pytest/__pycache__/compat.cpython-311.pyc,,
|
| 11 |
+
_pytest/__pycache__/debugging.cpython-311.pyc,,
|
| 12 |
+
_pytest/__pycache__/deprecated.cpython-311.pyc,,
|
| 13 |
+
_pytest/__pycache__/doctest.cpython-311.pyc,,
|
| 14 |
+
_pytest/__pycache__/faulthandler.cpython-311.pyc,,
|
| 15 |
+
_pytest/__pycache__/fixtures.cpython-311.pyc,,
|
| 16 |
+
_pytest/__pycache__/freeze_support.cpython-311.pyc,,
|
| 17 |
+
_pytest/__pycache__/helpconfig.cpython-311.pyc,,
|
| 18 |
+
_pytest/__pycache__/hookspec.cpython-311.pyc,,
|
| 19 |
+
_pytest/__pycache__/junitxml.cpython-311.pyc,,
|
| 20 |
+
_pytest/__pycache__/legacypath.cpython-311.pyc,,
|
| 21 |
+
_pytest/__pycache__/logging.cpython-311.pyc,,
|
| 22 |
+
_pytest/__pycache__/main.cpython-311.pyc,,
|
| 23 |
+
_pytest/__pycache__/monkeypatch.cpython-311.pyc,,
|
| 24 |
+
_pytest/__pycache__/nodes.cpython-311.pyc,,
|
| 25 |
+
_pytest/__pycache__/outcomes.cpython-311.pyc,,
|
| 26 |
+
_pytest/__pycache__/pastebin.cpython-311.pyc,,
|
| 27 |
+
_pytest/__pycache__/pathlib.cpython-311.pyc,,
|
| 28 |
+
_pytest/__pycache__/pytester.cpython-311.pyc,,
|
| 29 |
+
_pytest/__pycache__/pytester_assertions.cpython-311.pyc,,
|
| 30 |
+
_pytest/__pycache__/python.cpython-311.pyc,,
|
| 31 |
+
_pytest/__pycache__/python_api.cpython-311.pyc,,
|
| 32 |
+
_pytest/__pycache__/python_path.cpython-311.pyc,,
|
| 33 |
+
_pytest/__pycache__/recwarn.cpython-311.pyc,,
|
| 34 |
+
_pytest/__pycache__/reports.cpython-311.pyc,,
|
| 35 |
+
_pytest/__pycache__/runner.cpython-311.pyc,,
|
| 36 |
+
_pytest/__pycache__/scope.cpython-311.pyc,,
|
| 37 |
+
_pytest/__pycache__/setuponly.cpython-311.pyc,,
|
| 38 |
+
_pytest/__pycache__/setupplan.cpython-311.pyc,,
|
| 39 |
+
_pytest/__pycache__/skipping.cpython-311.pyc,,
|
| 40 |
+
_pytest/__pycache__/stash.cpython-311.pyc,,
|
| 41 |
+
_pytest/__pycache__/stepwise.cpython-311.pyc,,
|
| 42 |
+
_pytest/__pycache__/terminal.cpython-311.pyc,,
|
| 43 |
+
_pytest/__pycache__/threadexception.cpython-311.pyc,,
|
| 44 |
+
_pytest/__pycache__/timing.cpython-311.pyc,,
|
| 45 |
+
_pytest/__pycache__/tmpdir.cpython-311.pyc,,
|
| 46 |
+
_pytest/__pycache__/unittest.cpython-311.pyc,,
|
| 47 |
+
_pytest/__pycache__/unraisableexception.cpython-311.pyc,,
|
| 48 |
+
_pytest/__pycache__/warning_types.cpython-311.pyc,,
|
| 49 |
+
_pytest/__pycache__/warnings.cpython-311.pyc,,
|
| 50 |
+
_pytest/_argcomplete.py,sha256=gh0pna66p4LVb2D8ST4568WGxvdInGT43m6slYhqNqU,3776
|
| 51 |
+
_pytest/_code/__init__.py,sha256=5h7R-LFINKh7p8QR1HgdjvSGo1ysVJz28MQ9h7ipHK4,521
|
| 52 |
+
_pytest/_code/__pycache__/__init__.cpython-311.pyc,,
|
| 53 |
+
_pytest/_code/__pycache__/code.cpython-311.pyc,,
|
| 54 |
+
_pytest/_code/__pycache__/source.cpython-311.pyc,,
|
| 55 |
+
_pytest/_code/code.py,sha256=umPdqLxq8UgWKAItTEvF6ZOq5dF65mzCJHFaZHzTNGY,50133
|
| 56 |
+
_pytest/_code/source.py,sha256=2w9OZFOrRpiVaD_UdUS1T2XC7c2Is2GZn0iQy-lZfwk,7278
|
| 57 |
+
_pytest/_io/__init__.py,sha256=pkLF29VEFr6Dlr3eOtJL8sf47RLFt1Jf4X1DZBPlYmc,190
|
| 58 |
+
_pytest/_io/__pycache__/__init__.cpython-311.pyc,,
|
| 59 |
+
_pytest/_io/__pycache__/pprint.cpython-311.pyc,,
|
| 60 |
+
_pytest/_io/__pycache__/saferepr.cpython-311.pyc,,
|
| 61 |
+
_pytest/_io/__pycache__/terminalwriter.cpython-311.pyc,,
|
| 62 |
+
_pytest/_io/__pycache__/wcwidth.cpython-311.pyc,,
|
| 63 |
+
_pytest/_io/pprint.py,sha256=BCe8K7Zc0drYC5_JKZBBMVrhK84ARlmPpk9vSWPYhaE,19633
|
| 64 |
+
_pytest/_io/saferepr.py,sha256=Hhx5F-75iz03hdk-WO86Bmy9RBuRHsuJj-YUzozfrgo,4082
|
| 65 |
+
_pytest/_io/terminalwriter.py,sha256=dQ07zJ1-vlpFqWBBu_c0cHxT0yXcGSu7o7LxDCEyB3s,9319
|
| 66 |
+
_pytest/_io/wcwidth.py,sha256=cUEJ74UhweICwbKvU2q6noZcNgD0QlBEB9CfakGYaqA,1289
|
| 67 |
+
_pytest/_py/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 68 |
+
_pytest/_py/__pycache__/__init__.cpython-311.pyc,,
|
| 69 |
+
_pytest/_py/__pycache__/error.cpython-311.pyc,,
|
| 70 |
+
_pytest/_py/__pycache__/path.cpython-311.pyc,,
|
| 71 |
+
_pytest/_py/error.py,sha256=S1BRlfXSD248OFNzAuZ5O9w9W6fr2NUn0X8wYFGMNk0,3015
|
| 72 |
+
_pytest/_py/path.py,sha256=Xl4UspvrwwKYNlLZDGbjhUrnD6fuBmFxxchgltmwGek,49211
|
| 73 |
+
_pytest/_version.py,sha256=lMdMvJTmZKMLyCj9eyDEhOf6ttbh5vLbkANi6yBfmkc,411
|
| 74 |
+
_pytest/assertion/__init__.py,sha256=lK1YNNAk1VqCK-Y5C5hMJMqJQyxQT11HuDH3w85a3Zc,6791
|
| 75 |
+
_pytest/assertion/__pycache__/__init__.cpython-311.pyc,,
|
| 76 |
+
_pytest/assertion/__pycache__/rewrite.cpython-311.pyc,,
|
| 77 |
+
_pytest/assertion/__pycache__/truncate.cpython-311.pyc,,
|
| 78 |
+
_pytest/assertion/__pycache__/util.cpython-311.pyc,,
|
| 79 |
+
_pytest/assertion/rewrite.py,sha256=OltHwIVPXCPe0Ejgta_4hWjY8Xo3BIGbQEyLREfXZCA,48272
|
| 80 |
+
_pytest/assertion/truncate.py,sha256=GYl5iqDXUuKQHgd_mthWl3ZjxBbDVQliBhks1Ty00kE,4459
|
| 81 |
+
_pytest/assertion/util.py,sha256=6Vg5dZDuIXak9OLupvKyavLDroATgpU6ilkclc0OlJY,20265
|
| 82 |
+
_pytest/cacheprovider.py,sha256=BFQVkna56tlO-v9qaNJVHIcF30hIVGrP9St_vMp4w98,22373
|
| 83 |
+
_pytest/capture.py,sha256=iiu_k5_0ASbINS5_o7ZxMShGaWZFQD-y7jtU-NiJtMs,34680
|
| 84 |
+
_pytest/compat.py,sha256=sPcVQwPd45GaqsgIZEbCTR04GKhkVmIfft6QnKj3hmo,11467
|
| 85 |
+
_pytest/config/__init__.py,sha256=Ch5YizaRrCfMykEuZdHF0RaIyvtWvoSXL4v6E1Cu-FY,70645
|
| 86 |
+
_pytest/config/__pycache__/__init__.cpython-311.pyc,,
|
| 87 |
+
_pytest/config/__pycache__/argparsing.cpython-311.pyc,,
|
| 88 |
+
_pytest/config/__pycache__/compat.cpython-311.pyc,,
|
| 89 |
+
_pytest/config/__pycache__/exceptions.cpython-311.pyc,,
|
| 90 |
+
_pytest/config/__pycache__/findpaths.cpython-311.pyc,,
|
| 91 |
+
_pytest/config/argparsing.py,sha256=dNjEvFh2C34XMoiE_R7liJv5cryXUz2WR2VsxdnQdjo,20562
|
| 92 |
+
_pytest/config/compat.py,sha256=-m8G4-LLezCd4KZO6JQufEz7cRDqUSOjIwCtiKWpJvY,2938
|
| 93 |
+
_pytest/config/exceptions.py,sha256=lUKnOtpRqK-qNL6JfOP-8tRqpmHU34CVxguR5y0Qfbw,288
|
| 94 |
+
_pytest/config/findpaths.py,sha256=h4zq5AbLaZGpkeEcD2Xg-rJimh9I5pE042qQOTZT7NM,8062
|
| 95 |
+
_pytest/debugging.py,sha256=yRmmOexsaDeFky37IrD2e9svz8CWebB7L2fSUy4LvuE,13260
|
| 96 |
+
_pytest/deprecated.py,sha256=sO9UiqEdy9Z-NCvDoYYA0QtafYogAb7lP5M9N_Hpnak,3147
|
| 97 |
+
_pytest/doctest.py,sha256=7WJprJGYj7_9Lyr-L49wJ7q5ZwDVj1FBhA9_CX7JdLc,26255
|
| 98 |
+
_pytest/faulthandler.py,sha256=dT0H-MLi62SXeiKjLQJ0EVPuxkTlNOxpWtNxA5uBJPs,3674
|
| 99 |
+
_pytest/fixtures.py,sha256=I5t3pW2lHaVPbN1rAQ9sdX0a3QrpoW_U5VP-Vxejxmg,73550
|
| 100 |
+
_pytest/freeze_support.py,sha256=1EfzuxPd2oV9Ira26K5J4r9ppFZjnGi-xKzsBXe8B4g,1291
|
| 101 |
+
_pytest/helpconfig.py,sha256=ibnZNxKzToLmx-2ZrZKCP9t6jJvpAIlmqdf9a0rhOoI,8895
|
| 102 |
+
_pytest/hookspec.py,sha256=G-wKdmV3pecpeeiIAmzgPUMr22kz-CsqSpWEM-uiamg,42825
|
| 103 |
+
_pytest/junitxml.py,sha256=FnYwq0wAR4Cixzj-a9qhyulUSEpMyjX9ALbjza_We74,25574
|
| 104 |
+
_pytest/legacypath.py,sha256=_l6v8akNMfTc5TAjvbc6M-_t157p9QE6-118WM0DRt8,16588
|
| 105 |
+
_pytest/logging.py,sha256=QfaUUx-T0FiKBJBBb3bDllt8O8eTE7Mpigq7wvDepRc,35124
|
| 106 |
+
_pytest/main.py,sha256=Oowez36UkOwJXkTRq4rVuJRRr18ItBnz_YDjgAmFCV8,37416
|
| 107 |
+
_pytest/mark/__init__.py,sha256=bHORyCAsGnGJq7Tpm7A2sNQX31jwU1TgufM9DYcrTfQ,9307
|
| 108 |
+
_pytest/mark/__pycache__/__init__.cpython-311.pyc,,
|
| 109 |
+
_pytest/mark/__pycache__/expression.cpython-311.pyc,,
|
| 110 |
+
_pytest/mark/__pycache__/structures.cpython-311.pyc,,
|
| 111 |
+
_pytest/mark/expression.py,sha256=H6LmX0MWlxe0uBmuXIpQEntrLtyqIhEJv07YvA79eDQ,10152
|
| 112 |
+
_pytest/mark/structures.py,sha256=6hiIR3d4zxy35Yiw961r9sYrNl-T5WS8_0auSmpdiB0,21039
|
| 113 |
+
_pytest/monkeypatch.py,sha256=SKgteVJz1puqYQ3el6-ju5ZsNABqpoMUuRC6nn3tFpc,14598
|
| 114 |
+
_pytest/nodes.py,sha256=Hqyplow99hb-Zz0KKzL0K3cQ0rCgDXK65vBp6ave3u8,26483
|
| 115 |
+
_pytest/outcomes.py,sha256=SeW14rRKnGSt7K_NxY7HGnedoJawFHwQi2anAYYugk8,10532
|
| 116 |
+
_pytest/pastebin.py,sha256=Ja1z3Z6cXNElobpwy97FiyR5DDexZrDEB6vufmNvE4o,3978
|
| 117 |
+
_pytest/pathlib.py,sha256=onXoMMo4cbp-DR03XQuRimuIm_DrHNg3RujwP4Z7tic,36617
|
| 118 |
+
_pytest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 119 |
+
_pytest/pytester.py,sha256=-D_SNLfp_AQRMP7GOo6NsXlektiYod79pxBY-2RdUT0,61552
|
| 120 |
+
_pytest/pytester_assertions.py,sha256=EIHeb1llN9TVRfBJqQnwvjKeG-KpzURNq8mtbK7vcyA,2244
|
| 121 |
+
_pytest/python.py,sha256=mkJek4hqp7GMIyk6uPNWclI2dYlg78tTjymdcZViTJM,64851
|
| 122 |
+
_pytest/python_api.py,sha256=KyIP4xCyd2BpNFlY-28a0E50XxMXy7HSQi6ZaCw3qZg,40122
|
| 123 |
+
_pytest/python_path.py,sha256=fGP7iR_XMFRPijg4niILo44gWUWLlD635fYO5Abd6IM,745
|
| 124 |
+
_pytest/recwarn.py,sha256=M_xZw1EMireN8CZMmlI_sCiVsun8Rcq0FlnRkPeWdYQ,13227
|
| 125 |
+
_pytest/reports.py,sha256=5OM_OyQHIS09PW6T_8kAJNS67GvcpvP-lKcna2LcSZ0,21331
|
| 126 |
+
_pytest/runner.py,sha256=LDWKfhiIzWNkXqr1xwex-l1yhsWkdWCJko4bYM-etQ8,19436
|
| 127 |
+
_pytest/scope.py,sha256=MyzqXUuWP1-BgbbCBzJyILiS_jicZO2LNUMUjv7vhh0,2798
|
| 128 |
+
_pytest/setuponly.py,sha256=HNY9Ern-wex9iWSHxJU6ODA0yzYIH65QCkgNZ_BmbuA,3306
|
| 129 |
+
_pytest/setupplan.py,sha256=l-ycFNxDZPyY52wh4f7yaqhzZ7SW1ijSKnQLmqzDZWA,1184
|
| 130 |
+
_pytest/skipping.py,sha256=XbZKDPek9ex8aRXEoEy5iv0_e1b0sUi0PZrWqLBapek,10217
|
| 131 |
+
_pytest/stash.py,sha256=5pE3kDx4q855TW9aVvYTdrkkKlMDU6-xiX4luKpJEgI,3090
|
| 132 |
+
_pytest/stepwise.py,sha256=lYFm6kg000n_WEGOEQAho0j6dRCKJvgKz1Ya2Zz-0Zc,4596
|
| 133 |
+
_pytest/terminal.py,sha256=-xT17xSJs9bu90wqRBc3WckaWTNTPOmVkZlO1X16Wyo,57393
|
| 134 |
+
_pytest/threadexception.py,sha256=GHLYwCYK6I13Xv6bISO2crvPj9Z5ADKgVnUD7m1Oa14,3005
|
| 135 |
+
_pytest/timing.py,sha256=URwa2JENXYkIN_9LFgEmJ4ric7SW8O6a8woS_TN6jXI,413
|
| 136 |
+
_pytest/tmpdir.py,sha256=bo40r_gpxS7AdB_BANpSgh_fejHiXaGWrBxHpax9wtw,11375
|
| 137 |
+
_pytest/unittest.py,sha256=wew7w2q5SqgdPppFzv0evwrTLWmMCwKFQvSUyEX2C0Q,15614
|
| 138 |
+
_pytest/unraisableexception.py,sha256=-L6ln8mRnqqPBskzarua49st4ioXoKgllZ3oMmRuCKU,3252
|
| 139 |
+
_pytest/warning_types.py,sha256=m2_Y3zydUZNzPpu88n8wPNWqaxfaATMKEo_zAgXMqyY,4388
|
| 140 |
+
_pytest/warnings.py,sha256=ExyXdM9ZsIUX4o5GCt43fR-YWhIHSuUbV6GbKEVXeiA,5211
|
| 141 |
+
py.py,sha256=txZ1tdmEW6CBTp6Idn-I2sOzzA0xKNoCi9Re27Uj6HE,329
|
| 142 |
+
pytest-8.3.4.dist-info/AUTHORS,sha256=XobBrGbApvzdviFsaW1chh5icAuN555aWaMBHcmuqQE,7026
|
| 143 |
+
pytest-8.3.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 144 |
+
pytest-8.3.4.dist-info/LICENSE,sha256=yoNqX57Mo7LzUCMPqiCkj7ixRWU7VWjXhIYt-GRwa5s,1091
|
| 145 |
+
pytest-8.3.4.dist-info/METADATA,sha256=f5v2O_PCDdT8dVKotHCLiHzXKMTS9hTO2YsKQ6_P3ig,7510
|
| 146 |
+
pytest-8.3.4.dist-info/RECORD,,
|
| 147 |
+
pytest-8.3.4.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
| 148 |
+
pytest-8.3.4.dist-info/entry_points.txt,sha256=8IPrHPH3LNZQ7v5tNEOcNTZYk_SheNg64jsTM9erqL4,77
|
| 149 |
+
pytest-8.3.4.dist-info/top_level.txt,sha256=yyhjvmXH7-JOaoQIdmNQHPuoBCxOyXS3jIths_6C8A4,18
|
| 150 |
+
pytest/__init__.py,sha256=jm6h0ZECJdDXlX0i5F20mN3ypV--T7osmtMHzzzY8ug,5169
|
| 151 |
+
pytest/__main__.py,sha256=oVDrGGo7N0TNyzXntUblcgTKbhHGWtivcX5TC7tEcKo,154
|
| 152 |
+
pytest/__pycache__/__init__.cpython-311.pyc,,
|
| 153 |
+
pytest/__pycache__/__main__.cpython-311.pyc,,
|
| 154 |
+
pytest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (75.6.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
py.test = pytest:console_main
|
| 3 |
+
pytest = pytest:console_main
|
.venv/lib/python3.11/site-packages/pytest-8.3.4.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_pytest
|
| 2 |
+
py
|
| 3 |
+
pytest
|
.venv/lib/python3.11/site-packages/uvloop/__init__.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio as __asyncio
|
| 2 |
+
import typing as _typing
|
| 3 |
+
import sys as _sys
|
| 4 |
+
import warnings as _warnings
|
| 5 |
+
|
| 6 |
+
from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy
|
| 7 |
+
|
| 8 |
+
from . import includes as __includes # NOQA
|
| 9 |
+
from .loop import Loop as __BaseLoop # NOQA
|
| 10 |
+
from ._version import __version__ # NOQA
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
__all__ = ('new_event_loop', 'install', 'EventLoopPolicy')
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
_T = _typing.TypeVar("_T")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class Loop(__BaseLoop, __asyncio.AbstractEventLoop): # type: ignore[misc]
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def new_event_loop() -> Loop:
|
| 24 |
+
"""Return a new event loop."""
|
| 25 |
+
return Loop()
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def install() -> None:
|
| 29 |
+
"""A helper function to install uvloop policy."""
|
| 30 |
+
if _sys.version_info[:2] >= (3, 12):
|
| 31 |
+
_warnings.warn(
|
| 32 |
+
'uvloop.install() is deprecated in favor of uvloop.run() '
|
| 33 |
+
'starting with Python 3.12.',
|
| 34 |
+
DeprecationWarning,
|
| 35 |
+
stacklevel=1,
|
| 36 |
+
)
|
| 37 |
+
__asyncio.set_event_loop_policy(EventLoopPolicy())
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if _typing.TYPE_CHECKING:
|
| 41 |
+
def run(
|
| 42 |
+
main: _typing.Coroutine[_typing.Any, _typing.Any, _T],
|
| 43 |
+
*,
|
| 44 |
+
loop_factory: _typing.Optional[
|
| 45 |
+
_typing.Callable[[], Loop]
|
| 46 |
+
] = new_event_loop,
|
| 47 |
+
debug: _typing.Optional[bool]=None,
|
| 48 |
+
) -> _T:
|
| 49 |
+
"""The preferred way of running a coroutine with uvloop."""
|
| 50 |
+
else:
|
| 51 |
+
def run(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs):
|
| 52 |
+
"""The preferred way of running a coroutine with uvloop."""
|
| 53 |
+
|
| 54 |
+
async def wrapper():
|
| 55 |
+
# If `loop_factory` is provided we want it to return
|
| 56 |
+
# either uvloop.Loop or a subtype of it, assuming the user
|
| 57 |
+
# is using `uvloop.run()` intentionally.
|
| 58 |
+
loop = __asyncio._get_running_loop()
|
| 59 |
+
if not isinstance(loop, Loop):
|
| 60 |
+
raise TypeError('uvloop.run() uses a non-uvloop event loop')
|
| 61 |
+
return await main
|
| 62 |
+
|
| 63 |
+
vi = _sys.version_info[:2]
|
| 64 |
+
|
| 65 |
+
if vi <= (3, 10):
|
| 66 |
+
# Copied from python/cpython
|
| 67 |
+
|
| 68 |
+
if __asyncio._get_running_loop() is not None:
|
| 69 |
+
raise RuntimeError(
|
| 70 |
+
"asyncio.run() cannot be called from a running event loop")
|
| 71 |
+
|
| 72 |
+
if not __asyncio.iscoroutine(main):
|
| 73 |
+
raise ValueError(
|
| 74 |
+
"a coroutine was expected, got {!r}".format(main)
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
loop = loop_factory()
|
| 78 |
+
try:
|
| 79 |
+
__asyncio.set_event_loop(loop)
|
| 80 |
+
if debug is not None:
|
| 81 |
+
loop.set_debug(debug)
|
| 82 |
+
return loop.run_until_complete(wrapper())
|
| 83 |
+
finally:
|
| 84 |
+
try:
|
| 85 |
+
_cancel_all_tasks(loop)
|
| 86 |
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
| 87 |
+
if hasattr(loop, 'shutdown_default_executor'):
|
| 88 |
+
loop.run_until_complete(
|
| 89 |
+
loop.shutdown_default_executor()
|
| 90 |
+
)
|
| 91 |
+
finally:
|
| 92 |
+
__asyncio.set_event_loop(None)
|
| 93 |
+
loop.close()
|
| 94 |
+
|
| 95 |
+
elif vi == (3, 11):
|
| 96 |
+
if __asyncio._get_running_loop() is not None:
|
| 97 |
+
raise RuntimeError(
|
| 98 |
+
"asyncio.run() cannot be called from a running event loop")
|
| 99 |
+
|
| 100 |
+
with __asyncio.Runner(
|
| 101 |
+
loop_factory=loop_factory,
|
| 102 |
+
debug=debug,
|
| 103 |
+
**run_kwargs
|
| 104 |
+
) as runner:
|
| 105 |
+
return runner.run(wrapper())
|
| 106 |
+
|
| 107 |
+
else:
|
| 108 |
+
assert vi >= (3, 12)
|
| 109 |
+
return __asyncio.run(
|
| 110 |
+
wrapper(),
|
| 111 |
+
loop_factory=loop_factory,
|
| 112 |
+
debug=debug,
|
| 113 |
+
**run_kwargs
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _cancel_all_tasks(loop: __asyncio.AbstractEventLoop) -> None:
|
| 118 |
+
# Copied from python/cpython
|
| 119 |
+
|
| 120 |
+
to_cancel = __asyncio.all_tasks(loop)
|
| 121 |
+
if not to_cancel:
|
| 122 |
+
return
|
| 123 |
+
|
| 124 |
+
for task in to_cancel:
|
| 125 |
+
task.cancel()
|
| 126 |
+
|
| 127 |
+
loop.run_until_complete(
|
| 128 |
+
__asyncio.gather(*to_cancel, return_exceptions=True)
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
for task in to_cancel:
|
| 132 |
+
if task.cancelled():
|
| 133 |
+
continue
|
| 134 |
+
if task.exception() is not None:
|
| 135 |
+
loop.call_exception_handler({
|
| 136 |
+
'message': 'unhandled exception during asyncio.run() shutdown',
|
| 137 |
+
'exception': task.exception(),
|
| 138 |
+
'task': task,
|
| 139 |
+
})
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class EventLoopPolicy(__BasePolicy):
|
| 143 |
+
"""Event loop policy.
|
| 144 |
+
|
| 145 |
+
The preferred way to make your application use uvloop:
|
| 146 |
+
|
| 147 |
+
>>> import asyncio
|
| 148 |
+
>>> import uvloop
|
| 149 |
+
>>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
| 150 |
+
>>> asyncio.get_event_loop()
|
| 151 |
+
<uvloop.Loop running=False closed=False debug=False>
|
| 152 |
+
"""
|
| 153 |
+
|
| 154 |
+
def _loop_factory(self) -> Loop:
|
| 155 |
+
return new_event_loop()
|
| 156 |
+
|
| 157 |
+
if _typing.TYPE_CHECKING:
|
| 158 |
+
# EventLoopPolicy doesn't implement these, but since they are marked
|
| 159 |
+
# as abstract in typeshed, we have to put them in so mypy thinks
|
| 160 |
+
# the base methods are overridden. This is the same approach taken
|
| 161 |
+
# for the Windows event loop policy classes in typeshed.
|
| 162 |
+
def get_child_watcher(self) -> _typing.NoReturn:
|
| 163 |
+
...
|
| 164 |
+
|
| 165 |
+
def set_child_watcher(
|
| 166 |
+
self, watcher: _typing.Any
|
| 167 |
+
) -> _typing.NoReturn:
|
| 168 |
+
...
|
.venv/lib/python3.11/site-packages/uvloop/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (7.92 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/uvloop/__pycache__/_noop.cpython-311.pyc
ADDED
|
Binary file (355 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/uvloop/__pycache__/_testbase.cpython-311.pyc
ADDED
|
Binary file (32 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/uvloop/__pycache__/_version.cpython-311.pyc
ADDED
|
Binary file (202 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/uvloop/_noop.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def noop() -> None:
|
| 2 |
+
"""Empty function to invoke CPython ceval loop."""
|
| 3 |
+
return
|
.venv/lib/python3.11/site-packages/uvloop/_testbase.py
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test utilities. Don't use outside of the uvloop project."""
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import asyncio
|
| 5 |
+
import asyncio.events
|
| 6 |
+
import collections
|
| 7 |
+
import contextlib
|
| 8 |
+
import gc
|
| 9 |
+
import logging
|
| 10 |
+
import os
|
| 11 |
+
import pprint
|
| 12 |
+
import re
|
| 13 |
+
import select
|
| 14 |
+
import socket
|
| 15 |
+
import ssl
|
| 16 |
+
import sys
|
| 17 |
+
import tempfile
|
| 18 |
+
import threading
|
| 19 |
+
import time
|
| 20 |
+
import unittest
|
| 21 |
+
import uvloop
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class MockPattern(str):
|
| 25 |
+
def __eq__(self, other):
|
| 26 |
+
return bool(re.search(str(self), other, re.S))
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class TestCaseDict(collections.UserDict):
|
| 30 |
+
|
| 31 |
+
def __init__(self, name):
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.name = name
|
| 34 |
+
|
| 35 |
+
def __setitem__(self, key, value):
|
| 36 |
+
if key in self.data:
|
| 37 |
+
raise RuntimeError('duplicate test {}.{}'.format(
|
| 38 |
+
self.name, key))
|
| 39 |
+
super().__setitem__(key, value)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class BaseTestCaseMeta(type):
|
| 43 |
+
|
| 44 |
+
@classmethod
|
| 45 |
+
def __prepare__(mcls, name, bases):
|
| 46 |
+
return TestCaseDict(name)
|
| 47 |
+
|
| 48 |
+
def __new__(mcls, name, bases, dct):
|
| 49 |
+
for test_name in dct:
|
| 50 |
+
if not test_name.startswith('test_'):
|
| 51 |
+
continue
|
| 52 |
+
for base in bases:
|
| 53 |
+
if hasattr(base, test_name):
|
| 54 |
+
raise RuntimeError(
|
| 55 |
+
'duplicate test {}.{} (also defined in {} '
|
| 56 |
+
'parent class)'.format(
|
| 57 |
+
name, test_name, base.__name__))
|
| 58 |
+
|
| 59 |
+
return super().__new__(mcls, name, bases, dict(dct))
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class BaseTestCase(unittest.TestCase, metaclass=BaseTestCaseMeta):
|
| 63 |
+
|
| 64 |
+
def new_loop(self):
|
| 65 |
+
raise NotImplementedError
|
| 66 |
+
|
| 67 |
+
def new_policy(self):
|
| 68 |
+
raise NotImplementedError
|
| 69 |
+
|
| 70 |
+
def mock_pattern(self, str):
|
| 71 |
+
return MockPattern(str)
|
| 72 |
+
|
| 73 |
+
async def wait_closed(self, obj):
|
| 74 |
+
if not isinstance(obj, asyncio.StreamWriter):
|
| 75 |
+
return
|
| 76 |
+
try:
|
| 77 |
+
await obj.wait_closed()
|
| 78 |
+
except (BrokenPipeError, ConnectionError):
|
| 79 |
+
pass
|
| 80 |
+
|
| 81 |
+
def is_asyncio_loop(self):
|
| 82 |
+
return type(self.loop).__module__.startswith('asyncio.')
|
| 83 |
+
|
| 84 |
+
def run_loop_briefly(self, *, delay=0.01):
|
| 85 |
+
self.loop.run_until_complete(asyncio.sleep(delay))
|
| 86 |
+
|
| 87 |
+
def loop_exception_handler(self, loop, context):
|
| 88 |
+
self.__unhandled_exceptions.append(context)
|
| 89 |
+
self.loop.default_exception_handler(context)
|
| 90 |
+
|
| 91 |
+
def setUp(self):
|
| 92 |
+
self.loop = self.new_loop()
|
| 93 |
+
asyncio.set_event_loop_policy(self.new_policy())
|
| 94 |
+
asyncio.set_event_loop(self.loop)
|
| 95 |
+
self._check_unclosed_resources_in_debug = True
|
| 96 |
+
|
| 97 |
+
self.loop.set_exception_handler(self.loop_exception_handler)
|
| 98 |
+
self.__unhandled_exceptions = []
|
| 99 |
+
|
| 100 |
+
def tearDown(self):
|
| 101 |
+
self.loop.close()
|
| 102 |
+
|
| 103 |
+
if self.__unhandled_exceptions:
|
| 104 |
+
print('Unexpected calls to loop.call_exception_handler():')
|
| 105 |
+
pprint.pprint(self.__unhandled_exceptions)
|
| 106 |
+
self.fail('unexpected calls to loop.call_exception_handler()')
|
| 107 |
+
return
|
| 108 |
+
|
| 109 |
+
if not self._check_unclosed_resources_in_debug:
|
| 110 |
+
return
|
| 111 |
+
|
| 112 |
+
# GC to show any resource warnings as the test completes
|
| 113 |
+
gc.collect()
|
| 114 |
+
gc.collect()
|
| 115 |
+
gc.collect()
|
| 116 |
+
|
| 117 |
+
if getattr(self.loop, '_debug_cc', False):
|
| 118 |
+
gc.collect()
|
| 119 |
+
gc.collect()
|
| 120 |
+
gc.collect()
|
| 121 |
+
|
| 122 |
+
self.assertEqual(
|
| 123 |
+
self.loop._debug_uv_handles_total,
|
| 124 |
+
self.loop._debug_uv_handles_freed,
|
| 125 |
+
'not all uv_handle_t handles were freed')
|
| 126 |
+
|
| 127 |
+
self.assertEqual(
|
| 128 |
+
self.loop._debug_cb_handles_count, 0,
|
| 129 |
+
'not all callbacks (call_soon) are GCed')
|
| 130 |
+
|
| 131 |
+
self.assertEqual(
|
| 132 |
+
self.loop._debug_cb_timer_handles_count, 0,
|
| 133 |
+
'not all timer callbacks (call_later) are GCed')
|
| 134 |
+
|
| 135 |
+
self.assertEqual(
|
| 136 |
+
self.loop._debug_stream_write_ctx_cnt, 0,
|
| 137 |
+
'not all stream write contexts are GCed')
|
| 138 |
+
|
| 139 |
+
for h_name, h_cnt in self.loop._debug_handles_current.items():
|
| 140 |
+
with self.subTest('Alive handle after test',
|
| 141 |
+
handle_name=h_name):
|
| 142 |
+
self.assertEqual(
|
| 143 |
+
h_cnt, 0,
|
| 144 |
+
'alive {} after test'.format(h_name))
|
| 145 |
+
|
| 146 |
+
for h_name, h_cnt in self.loop._debug_handles_total.items():
|
| 147 |
+
with self.subTest('Total/closed handles',
|
| 148 |
+
handle_name=h_name):
|
| 149 |
+
self.assertEqual(
|
| 150 |
+
h_cnt, self.loop._debug_handles_closed[h_name],
|
| 151 |
+
'total != closed for {}'.format(h_name))
|
| 152 |
+
|
| 153 |
+
asyncio.set_event_loop(None)
|
| 154 |
+
asyncio.set_event_loop_policy(None)
|
| 155 |
+
self.loop = None
|
| 156 |
+
|
| 157 |
+
def skip_unclosed_handles_check(self):
|
| 158 |
+
self._check_unclosed_resources_in_debug = False
|
| 159 |
+
|
| 160 |
+
def tcp_server(self, server_prog, *,
|
| 161 |
+
family=socket.AF_INET,
|
| 162 |
+
addr=None,
|
| 163 |
+
timeout=5,
|
| 164 |
+
backlog=1,
|
| 165 |
+
max_clients=10):
|
| 166 |
+
|
| 167 |
+
if addr is None:
|
| 168 |
+
if family == socket.AF_UNIX:
|
| 169 |
+
with tempfile.NamedTemporaryFile() as tmp:
|
| 170 |
+
addr = tmp.name
|
| 171 |
+
else:
|
| 172 |
+
addr = ('127.0.0.1', 0)
|
| 173 |
+
|
| 174 |
+
sock = socket.socket(family, socket.SOCK_STREAM)
|
| 175 |
+
|
| 176 |
+
if timeout is None:
|
| 177 |
+
raise RuntimeError('timeout is required')
|
| 178 |
+
if timeout <= 0:
|
| 179 |
+
raise RuntimeError('only blocking sockets are supported')
|
| 180 |
+
sock.settimeout(timeout)
|
| 181 |
+
|
| 182 |
+
try:
|
| 183 |
+
sock.bind(addr)
|
| 184 |
+
sock.listen(backlog)
|
| 185 |
+
except OSError as ex:
|
| 186 |
+
sock.close()
|
| 187 |
+
raise ex
|
| 188 |
+
|
| 189 |
+
return TestThreadedServer(
|
| 190 |
+
self, sock, server_prog, timeout, max_clients)
|
| 191 |
+
|
| 192 |
+
def tcp_client(self, client_prog,
|
| 193 |
+
family=socket.AF_INET,
|
| 194 |
+
timeout=10):
|
| 195 |
+
|
| 196 |
+
sock = socket.socket(family, socket.SOCK_STREAM)
|
| 197 |
+
|
| 198 |
+
if timeout is None:
|
| 199 |
+
raise RuntimeError('timeout is required')
|
| 200 |
+
if timeout <= 0:
|
| 201 |
+
raise RuntimeError('only blocking sockets are supported')
|
| 202 |
+
sock.settimeout(timeout)
|
| 203 |
+
|
| 204 |
+
return TestThreadedClient(
|
| 205 |
+
self, sock, client_prog, timeout)
|
| 206 |
+
|
| 207 |
+
def unix_server(self, *args, **kwargs):
|
| 208 |
+
return self.tcp_server(*args, family=socket.AF_UNIX, **kwargs)
|
| 209 |
+
|
| 210 |
+
def unix_client(self, *args, **kwargs):
|
| 211 |
+
return self.tcp_client(*args, family=socket.AF_UNIX, **kwargs)
|
| 212 |
+
|
| 213 |
+
@contextlib.contextmanager
|
| 214 |
+
def unix_sock_name(self):
|
| 215 |
+
with tempfile.TemporaryDirectory() as td:
|
| 216 |
+
fn = os.path.join(td, 'sock')
|
| 217 |
+
try:
|
| 218 |
+
yield fn
|
| 219 |
+
finally:
|
| 220 |
+
try:
|
| 221 |
+
os.unlink(fn)
|
| 222 |
+
except OSError:
|
| 223 |
+
pass
|
| 224 |
+
|
| 225 |
+
def _abort_socket_test(self, ex):
|
| 226 |
+
try:
|
| 227 |
+
self.loop.stop()
|
| 228 |
+
finally:
|
| 229 |
+
self.fail(ex)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def _cert_fullname(test_file_name, cert_file_name):
|
| 233 |
+
fullname = os.path.abspath(os.path.join(
|
| 234 |
+
os.path.dirname(test_file_name), 'certs', cert_file_name))
|
| 235 |
+
assert os.path.isfile(fullname)
|
| 236 |
+
return fullname
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
@contextlib.contextmanager
|
| 240 |
+
def silence_long_exec_warning():
|
| 241 |
+
|
| 242 |
+
class Filter(logging.Filter):
|
| 243 |
+
def filter(self, record):
|
| 244 |
+
return not (record.msg.startswith('Executing') and
|
| 245 |
+
record.msg.endswith('seconds'))
|
| 246 |
+
|
| 247 |
+
logger = logging.getLogger('asyncio')
|
| 248 |
+
filter = Filter()
|
| 249 |
+
logger.addFilter(filter)
|
| 250 |
+
try:
|
| 251 |
+
yield
|
| 252 |
+
finally:
|
| 253 |
+
logger.removeFilter(filter)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def find_free_port(start_from=50000):
|
| 257 |
+
for port in range(start_from, start_from + 500):
|
| 258 |
+
sock = socket.socket()
|
| 259 |
+
with sock:
|
| 260 |
+
try:
|
| 261 |
+
sock.bind(('', port))
|
| 262 |
+
except socket.error:
|
| 263 |
+
continue
|
| 264 |
+
else:
|
| 265 |
+
return port
|
| 266 |
+
raise RuntimeError('could not find a free port')
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
class SSLTestCase:
|
| 270 |
+
|
| 271 |
+
def _create_server_ssl_context(self, certfile, keyfile=None):
|
| 272 |
+
if hasattr(ssl, 'PROTOCOL_TLS_SERVER'):
|
| 273 |
+
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
| 274 |
+
elif hasattr(ssl, 'PROTOCOL_TLS'):
|
| 275 |
+
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
| 276 |
+
else:
|
| 277 |
+
sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
| 278 |
+
sslcontext.options |= ssl.OP_NO_SSLv2
|
| 279 |
+
sslcontext.load_cert_chain(certfile, keyfile)
|
| 280 |
+
return sslcontext
|
| 281 |
+
|
| 282 |
+
def _create_client_ssl_context(self, *, disable_verify=True):
|
| 283 |
+
sslcontext = ssl.create_default_context()
|
| 284 |
+
sslcontext.check_hostname = False
|
| 285 |
+
if disable_verify:
|
| 286 |
+
sslcontext.verify_mode = ssl.CERT_NONE
|
| 287 |
+
return sslcontext
|
| 288 |
+
|
| 289 |
+
@contextlib.contextmanager
|
| 290 |
+
def _silence_eof_received_warning(self):
|
| 291 |
+
# TODO This warning has to be fixed in asyncio.
|
| 292 |
+
logger = logging.getLogger('asyncio')
|
| 293 |
+
filter = logging.Filter('has no effect when using ssl')
|
| 294 |
+
logger.addFilter(filter)
|
| 295 |
+
try:
|
| 296 |
+
yield
|
| 297 |
+
finally:
|
| 298 |
+
logger.removeFilter(filter)
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
class UVTestCase(BaseTestCase):
|
| 302 |
+
|
| 303 |
+
implementation = 'uvloop'
|
| 304 |
+
|
| 305 |
+
def new_loop(self):
|
| 306 |
+
return uvloop.new_event_loop()
|
| 307 |
+
|
| 308 |
+
def new_policy(self):
|
| 309 |
+
return uvloop.EventLoopPolicy()
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
class AIOTestCase(BaseTestCase):
|
| 313 |
+
|
| 314 |
+
implementation = 'asyncio'
|
| 315 |
+
|
| 316 |
+
def setUp(self):
|
| 317 |
+
super().setUp()
|
| 318 |
+
|
| 319 |
+
if sys.version_info < (3, 12):
|
| 320 |
+
watcher = asyncio.SafeChildWatcher()
|
| 321 |
+
watcher.attach_loop(self.loop)
|
| 322 |
+
asyncio.set_child_watcher(watcher)
|
| 323 |
+
|
| 324 |
+
def tearDown(self):
|
| 325 |
+
if sys.version_info < (3, 12):
|
| 326 |
+
asyncio.set_child_watcher(None)
|
| 327 |
+
super().tearDown()
|
| 328 |
+
|
| 329 |
+
def new_loop(self):
|
| 330 |
+
return asyncio.new_event_loop()
|
| 331 |
+
|
| 332 |
+
def new_policy(self):
|
| 333 |
+
return asyncio.DefaultEventLoopPolicy()
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def has_IPv6():
|
| 337 |
+
server_sock = socket.socket(socket.AF_INET6)
|
| 338 |
+
with server_sock:
|
| 339 |
+
try:
|
| 340 |
+
server_sock.bind(('::1', 0))
|
| 341 |
+
except OSError:
|
| 342 |
+
return False
|
| 343 |
+
else:
|
| 344 |
+
return True
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
has_IPv6 = has_IPv6()
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
###############################################################################
|
| 351 |
+
# Socket Testing Utilities
|
| 352 |
+
###############################################################################
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
class TestSocketWrapper:
|
| 356 |
+
|
| 357 |
+
def __init__(self, sock):
|
| 358 |
+
self.__sock = sock
|
| 359 |
+
|
| 360 |
+
def recv_all(self, n):
|
| 361 |
+
buf = b''
|
| 362 |
+
while len(buf) < n:
|
| 363 |
+
data = self.recv(n - len(buf))
|
| 364 |
+
if data == b'':
|
| 365 |
+
raise ConnectionAbortedError
|
| 366 |
+
buf += data
|
| 367 |
+
return buf
|
| 368 |
+
|
| 369 |
+
def starttls(self, ssl_context, *,
|
| 370 |
+
server_side=False,
|
| 371 |
+
server_hostname=None,
|
| 372 |
+
do_handshake_on_connect=True):
|
| 373 |
+
|
| 374 |
+
assert isinstance(ssl_context, ssl.SSLContext)
|
| 375 |
+
|
| 376 |
+
ssl_sock = ssl_context.wrap_socket(
|
| 377 |
+
self.__sock, server_side=server_side,
|
| 378 |
+
server_hostname=server_hostname,
|
| 379 |
+
do_handshake_on_connect=do_handshake_on_connect)
|
| 380 |
+
|
| 381 |
+
if server_side:
|
| 382 |
+
ssl_sock.do_handshake()
|
| 383 |
+
|
| 384 |
+
self.__sock.close()
|
| 385 |
+
self.__sock = ssl_sock
|
| 386 |
+
|
| 387 |
+
def __getattr__(self, name):
|
| 388 |
+
return getattr(self.__sock, name)
|
| 389 |
+
|
| 390 |
+
def __repr__(self):
|
| 391 |
+
return '<{} {!r}>'.format(type(self).__name__, self.__sock)
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
class SocketThread(threading.Thread):
|
| 395 |
+
|
| 396 |
+
def stop(self):
|
| 397 |
+
self._active = False
|
| 398 |
+
self.join()
|
| 399 |
+
|
| 400 |
+
def __enter__(self):
|
| 401 |
+
self.start()
|
| 402 |
+
return self
|
| 403 |
+
|
| 404 |
+
def __exit__(self, *exc):
|
| 405 |
+
self.stop()
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
class TestThreadedClient(SocketThread):
|
| 409 |
+
|
| 410 |
+
def __init__(self, test, sock, prog, timeout):
|
| 411 |
+
threading.Thread.__init__(self, None, None, 'test-client')
|
| 412 |
+
self.daemon = True
|
| 413 |
+
|
| 414 |
+
self._timeout = timeout
|
| 415 |
+
self._sock = sock
|
| 416 |
+
self._active = True
|
| 417 |
+
self._prog = prog
|
| 418 |
+
self._test = test
|
| 419 |
+
|
| 420 |
+
def run(self):
|
| 421 |
+
try:
|
| 422 |
+
self._prog(TestSocketWrapper(self._sock))
|
| 423 |
+
except (KeyboardInterrupt, SystemExit):
|
| 424 |
+
raise
|
| 425 |
+
except BaseException as ex:
|
| 426 |
+
self._test._abort_socket_test(ex)
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
class TestThreadedServer(SocketThread):
|
| 430 |
+
|
| 431 |
+
def __init__(self, test, sock, prog, timeout, max_clients):
|
| 432 |
+
threading.Thread.__init__(self, None, None, 'test-server')
|
| 433 |
+
self.daemon = True
|
| 434 |
+
|
| 435 |
+
self._clients = 0
|
| 436 |
+
self._finished_clients = 0
|
| 437 |
+
self._max_clients = max_clients
|
| 438 |
+
self._timeout = timeout
|
| 439 |
+
self._sock = sock
|
| 440 |
+
self._active = True
|
| 441 |
+
|
| 442 |
+
self._prog = prog
|
| 443 |
+
|
| 444 |
+
self._s1, self._s2 = socket.socketpair()
|
| 445 |
+
self._s1.setblocking(False)
|
| 446 |
+
|
| 447 |
+
self._test = test
|
| 448 |
+
|
| 449 |
+
def stop(self):
|
| 450 |
+
try:
|
| 451 |
+
if self._s2 and self._s2.fileno() != -1:
|
| 452 |
+
try:
|
| 453 |
+
self._s2.send(b'stop')
|
| 454 |
+
except OSError:
|
| 455 |
+
pass
|
| 456 |
+
finally:
|
| 457 |
+
super().stop()
|
| 458 |
+
|
| 459 |
+
def run(self):
|
| 460 |
+
try:
|
| 461 |
+
with self._sock:
|
| 462 |
+
self._sock.setblocking(0)
|
| 463 |
+
self._run()
|
| 464 |
+
finally:
|
| 465 |
+
self._s1.close()
|
| 466 |
+
self._s2.close()
|
| 467 |
+
|
| 468 |
+
def _run(self):
|
| 469 |
+
while self._active:
|
| 470 |
+
if self._clients >= self._max_clients:
|
| 471 |
+
return
|
| 472 |
+
|
| 473 |
+
r, w, x = select.select(
|
| 474 |
+
[self._sock, self._s1], [], [], self._timeout)
|
| 475 |
+
|
| 476 |
+
if self._s1 in r:
|
| 477 |
+
return
|
| 478 |
+
|
| 479 |
+
if self._sock in r:
|
| 480 |
+
try:
|
| 481 |
+
conn, addr = self._sock.accept()
|
| 482 |
+
except BlockingIOError:
|
| 483 |
+
continue
|
| 484 |
+
except socket.timeout:
|
| 485 |
+
if not self._active:
|
| 486 |
+
return
|
| 487 |
+
else:
|
| 488 |
+
raise
|
| 489 |
+
else:
|
| 490 |
+
self._clients += 1
|
| 491 |
+
conn.settimeout(self._timeout)
|
| 492 |
+
try:
|
| 493 |
+
with conn:
|
| 494 |
+
self._handle_client(conn)
|
| 495 |
+
except (KeyboardInterrupt, SystemExit):
|
| 496 |
+
raise
|
| 497 |
+
except BaseException as ex:
|
| 498 |
+
self._active = False
|
| 499 |
+
try:
|
| 500 |
+
raise
|
| 501 |
+
finally:
|
| 502 |
+
self._test._abort_socket_test(ex)
|
| 503 |
+
|
| 504 |
+
def _handle_client(self, sock):
|
| 505 |
+
self._prog(TestSocketWrapper(sock))
|
| 506 |
+
|
| 507 |
+
@property
|
| 508 |
+
def addr(self):
|
| 509 |
+
return self._sock.getsockname()
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
###############################################################################
|
| 513 |
+
# A few helpers from asyncio/tests/testutils.py
|
| 514 |
+
###############################################################################
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def run_briefly(loop):
|
| 518 |
+
async def once():
|
| 519 |
+
pass
|
| 520 |
+
gen = once()
|
| 521 |
+
t = loop.create_task(gen)
|
| 522 |
+
# Don't log a warning if the task is not done after run_until_complete().
|
| 523 |
+
# It occurs if the loop is stopped or if a task raises a BaseException.
|
| 524 |
+
t._log_destroy_pending = False
|
| 525 |
+
try:
|
| 526 |
+
loop.run_until_complete(t)
|
| 527 |
+
finally:
|
| 528 |
+
gen.close()
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def run_until(loop, pred, timeout=30):
|
| 532 |
+
deadline = time.time() + timeout
|
| 533 |
+
while not pred():
|
| 534 |
+
if timeout is not None:
|
| 535 |
+
timeout = deadline - time.time()
|
| 536 |
+
if timeout <= 0:
|
| 537 |
+
raise asyncio.futures.TimeoutError()
|
| 538 |
+
loop.run_until_complete(asyncio.tasks.sleep(0.001))
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
@contextlib.contextmanager
|
| 542 |
+
def disable_logger():
|
| 543 |
+
"""Context manager to disable asyncio logger.
|
| 544 |
+
|
| 545 |
+
For example, it can be used to ignore warnings in debug mode.
|
| 546 |
+
"""
|
| 547 |
+
old_level = asyncio.log.logger.level
|
| 548 |
+
try:
|
| 549 |
+
asyncio.log.logger.setLevel(logging.CRITICAL + 1)
|
| 550 |
+
yield
|
| 551 |
+
finally:
|
| 552 |
+
asyncio.log.logger.setLevel(old_level)
|
.venv/lib/python3.11/site-packages/uvloop/_version.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file MUST NOT contain anything but the __version__ assignment.
|
| 2 |
+
#
|
| 3 |
+
# When making a release, change the value of __version__
|
| 4 |
+
# to an appropriate value, and open a pull request against
|
| 5 |
+
# the correct branch (master if making a new feature release).
|
| 6 |
+
# The commit message MUST contain a properly formatted release
|
| 7 |
+
# log, and the commit must be signed.
|
| 8 |
+
#
|
| 9 |
+
# The release automation will: build and test the packages for the
|
| 10 |
+
# supported platforms, publish the packages on PyPI, merge the PR
|
| 11 |
+
# to the target branch, create a Git tag pointing to the commit.
|
| 12 |
+
|
| 13 |
+
__version__ = '0.21.0'
|
.venv/lib/python3.11/site-packages/uvloop/cbhandles.pxd
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class Handle:
|
| 2 |
+
cdef:
|
| 3 |
+
Loop loop
|
| 4 |
+
object context
|
| 5 |
+
bint _cancelled
|
| 6 |
+
|
| 7 |
+
str meth_name
|
| 8 |
+
int cb_type
|
| 9 |
+
void *callback
|
| 10 |
+
object arg1, arg2, arg3, arg4
|
| 11 |
+
|
| 12 |
+
object __weakref__
|
| 13 |
+
|
| 14 |
+
readonly _source_traceback
|
| 15 |
+
|
| 16 |
+
cdef inline _set_loop(self, Loop loop)
|
| 17 |
+
cdef inline _set_context(self, object context)
|
| 18 |
+
|
| 19 |
+
cdef inline _run(self)
|
| 20 |
+
cdef _cancel(self)
|
| 21 |
+
|
| 22 |
+
cdef _format_handle(self)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
cdef class TimerHandle:
|
| 26 |
+
cdef:
|
| 27 |
+
object callback
|
| 28 |
+
tuple args
|
| 29 |
+
bint _cancelled
|
| 30 |
+
UVTimer timer
|
| 31 |
+
Loop loop
|
| 32 |
+
object context
|
| 33 |
+
tuple _debug_info
|
| 34 |
+
object __weakref__
|
| 35 |
+
object _when
|
| 36 |
+
|
| 37 |
+
cdef _run(self)
|
| 38 |
+
cdef _cancel(self)
|
| 39 |
+
cdef inline _clear(self)
|
.venv/lib/python3.11/site-packages/uvloop/cbhandles.pyx
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
@cython.freelist(DEFAULT_FREELIST_SIZE)
|
| 3 |
+
cdef class Handle:
|
| 4 |
+
def __cinit__(self):
|
| 5 |
+
self._cancelled = 0
|
| 6 |
+
self.cb_type = 0
|
| 7 |
+
self._source_traceback = None
|
| 8 |
+
|
| 9 |
+
cdef inline _set_loop(self, Loop loop):
|
| 10 |
+
self.loop = loop
|
| 11 |
+
if UVLOOP_DEBUG:
|
| 12 |
+
loop._debug_cb_handles_total += 1
|
| 13 |
+
loop._debug_cb_handles_count += 1
|
| 14 |
+
if loop._debug:
|
| 15 |
+
self._source_traceback = extract_stack()
|
| 16 |
+
|
| 17 |
+
cdef inline _set_context(self, object context):
|
| 18 |
+
if context is None:
|
| 19 |
+
context = Context_CopyCurrent()
|
| 20 |
+
self.context = context
|
| 21 |
+
|
| 22 |
+
def __dealloc__(self):
|
| 23 |
+
if UVLOOP_DEBUG and self.loop is not None:
|
| 24 |
+
self.loop._debug_cb_handles_count -= 1
|
| 25 |
+
if self.loop is None:
|
| 26 |
+
raise RuntimeError('Handle.loop is None in Handle.__dealloc__')
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
raise TypeError(
|
| 30 |
+
'{} is not supposed to be instantiated from Python'.format(
|
| 31 |
+
self.__class__.__name__))
|
| 32 |
+
|
| 33 |
+
cdef inline _run(self):
|
| 34 |
+
cdef:
|
| 35 |
+
int cb_type
|
| 36 |
+
object callback
|
| 37 |
+
|
| 38 |
+
if self._cancelled:
|
| 39 |
+
return
|
| 40 |
+
|
| 41 |
+
cb_type = self.cb_type
|
| 42 |
+
|
| 43 |
+
# Since _run is a cdef and there's no BoundMethod,
|
| 44 |
+
# we guard 'self' manually (since the callback
|
| 45 |
+
# might cause GC of the handle.)
|
| 46 |
+
Py_INCREF(self)
|
| 47 |
+
|
| 48 |
+
try:
|
| 49 |
+
assert self.context is not None
|
| 50 |
+
Context_Enter(self.context)
|
| 51 |
+
|
| 52 |
+
if cb_type == 1:
|
| 53 |
+
callback = self.arg1
|
| 54 |
+
if callback is None:
|
| 55 |
+
raise RuntimeError(
|
| 56 |
+
'cannot run Handle; callback is not set')
|
| 57 |
+
|
| 58 |
+
args = self.arg2
|
| 59 |
+
|
| 60 |
+
if args is None:
|
| 61 |
+
callback()
|
| 62 |
+
else:
|
| 63 |
+
callback(*args)
|
| 64 |
+
|
| 65 |
+
elif cb_type == 2:
|
| 66 |
+
(<method_t>self.callback)(self.arg1)
|
| 67 |
+
|
| 68 |
+
elif cb_type == 3:
|
| 69 |
+
(<method1_t>self.callback)(self.arg1, self.arg2)
|
| 70 |
+
|
| 71 |
+
elif cb_type == 4:
|
| 72 |
+
(<method2_t>self.callback)(self.arg1, self.arg2, self.arg3)
|
| 73 |
+
|
| 74 |
+
elif cb_type == 5:
|
| 75 |
+
(<method3_t>self.callback)(
|
| 76 |
+
self.arg1, self.arg2, self.arg3, self.arg4)
|
| 77 |
+
|
| 78 |
+
else:
|
| 79 |
+
raise RuntimeError('invalid Handle.cb_type: {}'.format(
|
| 80 |
+
cb_type))
|
| 81 |
+
|
| 82 |
+
except (KeyboardInterrupt, SystemExit):
|
| 83 |
+
raise
|
| 84 |
+
except BaseException as ex:
|
| 85 |
+
if cb_type == 1:
|
| 86 |
+
msg = 'Exception in callback {}'.format(callback)
|
| 87 |
+
else:
|
| 88 |
+
msg = 'Exception in callback {}'.format(self.meth_name)
|
| 89 |
+
|
| 90 |
+
context = {
|
| 91 |
+
'message': msg,
|
| 92 |
+
'exception': ex,
|
| 93 |
+
'handle': self,
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
if self._source_traceback is not None:
|
| 97 |
+
context['source_traceback'] = self._source_traceback
|
| 98 |
+
|
| 99 |
+
self.loop.call_exception_handler(context)
|
| 100 |
+
|
| 101 |
+
finally:
|
| 102 |
+
context = self.context
|
| 103 |
+
Py_DECREF(self)
|
| 104 |
+
Context_Exit(context)
|
| 105 |
+
|
| 106 |
+
cdef _cancel(self):
|
| 107 |
+
self._cancelled = 1
|
| 108 |
+
self.callback = NULL
|
| 109 |
+
self.arg1 = self.arg2 = self.arg3 = self.arg4 = None
|
| 110 |
+
|
| 111 |
+
cdef _format_handle(self):
|
| 112 |
+
# Mirrors `asyncio.base_events._format_handle`.
|
| 113 |
+
if self.cb_type == 1 and self.arg1 is not None:
|
| 114 |
+
cb = self.arg1
|
| 115 |
+
if isinstance(getattr(cb, '__self__', None), aio_Task):
|
| 116 |
+
try:
|
| 117 |
+
return repr(cb.__self__)
|
| 118 |
+
except (AttributeError, TypeError, ValueError) as ex:
|
| 119 |
+
# Cython generates empty __code__ objects for coroutines
|
| 120 |
+
# that can crash asyncio.Task.__repr__ with an
|
| 121 |
+
# AttributeError etc. Guard against that.
|
| 122 |
+
self.loop.call_exception_handler({
|
| 123 |
+
'message': 'exception in Task.__repr__',
|
| 124 |
+
'task': cb.__self__,
|
| 125 |
+
'exception': ex,
|
| 126 |
+
'handle': self,
|
| 127 |
+
})
|
| 128 |
+
return repr(self)
|
| 129 |
+
|
| 130 |
+
# Public API
|
| 131 |
+
|
| 132 |
+
def __repr__(self):
|
| 133 |
+
info = [self.__class__.__name__]
|
| 134 |
+
|
| 135 |
+
if self._cancelled:
|
| 136 |
+
info.append('cancelled')
|
| 137 |
+
|
| 138 |
+
if self.cb_type == 1 and self.arg1 is not None:
|
| 139 |
+
func = self.arg1
|
| 140 |
+
# Cython can unset func.__qualname__/__name__, hence the checks.
|
| 141 |
+
if hasattr(func, '__qualname__') and func.__qualname__:
|
| 142 |
+
cb_name = func.__qualname__
|
| 143 |
+
elif hasattr(func, '__name__') and func.__name__:
|
| 144 |
+
cb_name = func.__name__
|
| 145 |
+
else:
|
| 146 |
+
cb_name = repr(func)
|
| 147 |
+
|
| 148 |
+
info.append(cb_name)
|
| 149 |
+
elif self.meth_name is not None:
|
| 150 |
+
info.append(self.meth_name)
|
| 151 |
+
|
| 152 |
+
if self._source_traceback is not None:
|
| 153 |
+
frame = self._source_traceback[-1]
|
| 154 |
+
info.append('created at {}:{}'.format(frame[0], frame[1]))
|
| 155 |
+
|
| 156 |
+
return '<' + ' '.join(info) + '>'
|
| 157 |
+
|
| 158 |
+
def cancel(self):
|
| 159 |
+
self._cancel()
|
| 160 |
+
|
| 161 |
+
def cancelled(self):
|
| 162 |
+
return self._cancelled
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
@cython.no_gc_clear
|
| 166 |
+
@cython.freelist(DEFAULT_FREELIST_SIZE)
|
| 167 |
+
cdef class TimerHandle:
|
| 168 |
+
def __cinit__(self, Loop loop, object callback, object args,
|
| 169 |
+
uint64_t delay, object context):
|
| 170 |
+
|
| 171 |
+
self.loop = loop
|
| 172 |
+
self.callback = callback
|
| 173 |
+
self.args = args
|
| 174 |
+
self._cancelled = 0
|
| 175 |
+
|
| 176 |
+
if UVLOOP_DEBUG:
|
| 177 |
+
self.loop._debug_cb_timer_handles_total += 1
|
| 178 |
+
self.loop._debug_cb_timer_handles_count += 1
|
| 179 |
+
|
| 180 |
+
if context is None:
|
| 181 |
+
context = Context_CopyCurrent()
|
| 182 |
+
self.context = context
|
| 183 |
+
|
| 184 |
+
if loop._debug:
|
| 185 |
+
self._debug_info = (
|
| 186 |
+
format_callback_name(callback),
|
| 187 |
+
extract_stack()
|
| 188 |
+
)
|
| 189 |
+
else:
|
| 190 |
+
self._debug_info = None
|
| 191 |
+
|
| 192 |
+
self.timer = UVTimer.new(
|
| 193 |
+
loop, <method_t>self._run, self, delay)
|
| 194 |
+
|
| 195 |
+
self.timer.start()
|
| 196 |
+
self._when = self.timer.get_when() * 1e-3
|
| 197 |
+
|
| 198 |
+
# Only add to loop._timers when `self.timer` is successfully created
|
| 199 |
+
loop._timers.add(self)
|
| 200 |
+
|
| 201 |
+
property _source_traceback:
|
| 202 |
+
def __get__(self):
|
| 203 |
+
if self._debug_info is not None:
|
| 204 |
+
return self._debug_info[1]
|
| 205 |
+
|
| 206 |
+
def __dealloc__(self):
|
| 207 |
+
if UVLOOP_DEBUG:
|
| 208 |
+
self.loop._debug_cb_timer_handles_count -= 1
|
| 209 |
+
if self.timer is not None:
|
| 210 |
+
raise RuntimeError('active TimerHandle is deallacating')
|
| 211 |
+
|
| 212 |
+
cdef _cancel(self):
|
| 213 |
+
if self._cancelled == 1:
|
| 214 |
+
return
|
| 215 |
+
self._cancelled = 1
|
| 216 |
+
self._clear()
|
| 217 |
+
|
| 218 |
+
cdef inline _clear(self):
|
| 219 |
+
if self.timer is None:
|
| 220 |
+
return
|
| 221 |
+
|
| 222 |
+
self.callback = None
|
| 223 |
+
self.args = None
|
| 224 |
+
|
| 225 |
+
try:
|
| 226 |
+
self.loop._timers.remove(self)
|
| 227 |
+
finally:
|
| 228 |
+
self.timer._close()
|
| 229 |
+
self.timer = None # let the UVTimer handle GC
|
| 230 |
+
|
| 231 |
+
cdef _run(self):
|
| 232 |
+
if self._cancelled == 1:
|
| 233 |
+
return
|
| 234 |
+
if self.callback is None:
|
| 235 |
+
raise RuntimeError('cannot run TimerHandle; callback is not set')
|
| 236 |
+
|
| 237 |
+
callback = self.callback
|
| 238 |
+
args = self.args
|
| 239 |
+
|
| 240 |
+
# Since _run is a cdef and there's no BoundMethod,
|
| 241 |
+
# we guard 'self' manually.
|
| 242 |
+
Py_INCREF(self)
|
| 243 |
+
|
| 244 |
+
if self.loop._debug:
|
| 245 |
+
started = time_monotonic()
|
| 246 |
+
try:
|
| 247 |
+
assert self.context is not None
|
| 248 |
+
Context_Enter(self.context)
|
| 249 |
+
|
| 250 |
+
if args is not None:
|
| 251 |
+
callback(*args)
|
| 252 |
+
else:
|
| 253 |
+
callback()
|
| 254 |
+
except (KeyboardInterrupt, SystemExit):
|
| 255 |
+
raise
|
| 256 |
+
except BaseException as ex:
|
| 257 |
+
context = {
|
| 258 |
+
'message': 'Exception in callback {}'.format(callback),
|
| 259 |
+
'exception': ex,
|
| 260 |
+
'handle': self,
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
if self._debug_info is not None:
|
| 264 |
+
context['source_traceback'] = self._debug_info[1]
|
| 265 |
+
|
| 266 |
+
self.loop.call_exception_handler(context)
|
| 267 |
+
else:
|
| 268 |
+
if self.loop._debug:
|
| 269 |
+
delta = time_monotonic() - started
|
| 270 |
+
if delta > self.loop.slow_callback_duration:
|
| 271 |
+
aio_logger.warning(
|
| 272 |
+
'Executing %r took %.3f seconds',
|
| 273 |
+
self, delta)
|
| 274 |
+
finally:
|
| 275 |
+
context = self.context
|
| 276 |
+
Py_DECREF(self)
|
| 277 |
+
Context_Exit(context)
|
| 278 |
+
self._clear()
|
| 279 |
+
|
| 280 |
+
# Public API
|
| 281 |
+
|
| 282 |
+
def __repr__(self):
|
| 283 |
+
info = [self.__class__.__name__]
|
| 284 |
+
|
| 285 |
+
if self._cancelled:
|
| 286 |
+
info.append('cancelled')
|
| 287 |
+
|
| 288 |
+
if self._debug_info is not None:
|
| 289 |
+
callback_name = self._debug_info[0]
|
| 290 |
+
source_traceback = self._debug_info[1]
|
| 291 |
+
else:
|
| 292 |
+
callback_name = None
|
| 293 |
+
source_traceback = None
|
| 294 |
+
|
| 295 |
+
if callback_name is not None:
|
| 296 |
+
info.append(callback_name)
|
| 297 |
+
elif self.callback is not None:
|
| 298 |
+
info.append(format_callback_name(self.callback))
|
| 299 |
+
|
| 300 |
+
if source_traceback is not None:
|
| 301 |
+
frame = source_traceback[-1]
|
| 302 |
+
info.append('created at {}:{}'.format(frame[0], frame[1]))
|
| 303 |
+
|
| 304 |
+
return '<' + ' '.join(info) + '>'
|
| 305 |
+
|
| 306 |
+
def cancelled(self):
|
| 307 |
+
return self._cancelled
|
| 308 |
+
|
| 309 |
+
def cancel(self):
|
| 310 |
+
self._cancel()
|
| 311 |
+
|
| 312 |
+
def when(self):
|
| 313 |
+
return self._when
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
cdef format_callback_name(func):
|
| 317 |
+
if hasattr(func, '__qualname__'):
|
| 318 |
+
cb_name = getattr(func, '__qualname__')
|
| 319 |
+
elif hasattr(func, '__name__'):
|
| 320 |
+
cb_name = getattr(func, '__name__')
|
| 321 |
+
else:
|
| 322 |
+
cb_name = repr(func)
|
| 323 |
+
return cb_name
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
cdef new_Handle(Loop loop, object callback, object args, object context):
|
| 327 |
+
cdef Handle handle
|
| 328 |
+
handle = Handle.__new__(Handle)
|
| 329 |
+
handle._set_loop(loop)
|
| 330 |
+
handle._set_context(context)
|
| 331 |
+
|
| 332 |
+
handle.cb_type = 1
|
| 333 |
+
|
| 334 |
+
handle.arg1 = callback
|
| 335 |
+
handle.arg2 = args
|
| 336 |
+
|
| 337 |
+
return handle
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
cdef new_MethodHandle(Loop loop, str name, method_t callback, object context,
|
| 341 |
+
object bound_to):
|
| 342 |
+
cdef Handle handle
|
| 343 |
+
handle = Handle.__new__(Handle)
|
| 344 |
+
handle._set_loop(loop)
|
| 345 |
+
handle._set_context(context)
|
| 346 |
+
|
| 347 |
+
handle.cb_type = 2
|
| 348 |
+
handle.meth_name = name
|
| 349 |
+
|
| 350 |
+
handle.callback = <void*> callback
|
| 351 |
+
handle.arg1 = bound_to
|
| 352 |
+
|
| 353 |
+
return handle
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
cdef new_MethodHandle1(Loop loop, str name, method1_t callback, object context,
|
| 357 |
+
object bound_to, object arg):
|
| 358 |
+
|
| 359 |
+
cdef Handle handle
|
| 360 |
+
handle = Handle.__new__(Handle)
|
| 361 |
+
handle._set_loop(loop)
|
| 362 |
+
handle._set_context(context)
|
| 363 |
+
|
| 364 |
+
handle.cb_type = 3
|
| 365 |
+
handle.meth_name = name
|
| 366 |
+
|
| 367 |
+
handle.callback = <void*> callback
|
| 368 |
+
handle.arg1 = bound_to
|
| 369 |
+
handle.arg2 = arg
|
| 370 |
+
|
| 371 |
+
return handle
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
cdef new_MethodHandle2(Loop loop, str name, method2_t callback, object context,
|
| 375 |
+
object bound_to, object arg1, object arg2):
|
| 376 |
+
|
| 377 |
+
cdef Handle handle
|
| 378 |
+
handle = Handle.__new__(Handle)
|
| 379 |
+
handle._set_loop(loop)
|
| 380 |
+
handle._set_context(context)
|
| 381 |
+
|
| 382 |
+
handle.cb_type = 4
|
| 383 |
+
handle.meth_name = name
|
| 384 |
+
|
| 385 |
+
handle.callback = <void*> callback
|
| 386 |
+
handle.arg1 = bound_to
|
| 387 |
+
handle.arg2 = arg1
|
| 388 |
+
handle.arg3 = arg2
|
| 389 |
+
|
| 390 |
+
return handle
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
cdef new_MethodHandle3(Loop loop, str name, method3_t callback, object context,
|
| 394 |
+
object bound_to, object arg1, object arg2, object arg3):
|
| 395 |
+
|
| 396 |
+
cdef Handle handle
|
| 397 |
+
handle = Handle.__new__(Handle)
|
| 398 |
+
handle._set_loop(loop)
|
| 399 |
+
handle._set_context(context)
|
| 400 |
+
|
| 401 |
+
handle.cb_type = 5
|
| 402 |
+
handle.meth_name = name
|
| 403 |
+
|
| 404 |
+
handle.callback = <void*> callback
|
| 405 |
+
handle.arg1 = bound_to
|
| 406 |
+
handle.arg2 = arg1
|
| 407 |
+
handle.arg3 = arg2
|
| 408 |
+
handle.arg4 = arg3
|
| 409 |
+
|
| 410 |
+
return handle
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
cdef extract_stack():
|
| 414 |
+
"""Replacement for traceback.extract_stack() that only does the
|
| 415 |
+
necessary work for asyncio debug mode.
|
| 416 |
+
"""
|
| 417 |
+
try:
|
| 418 |
+
f = sys_getframe()
|
| 419 |
+
# sys._getframe() might raise ValueError if being called without a frame, e.g.
|
| 420 |
+
# from Cython or similar C extensions.
|
| 421 |
+
except ValueError:
|
| 422 |
+
return None
|
| 423 |
+
if f is None:
|
| 424 |
+
return
|
| 425 |
+
|
| 426 |
+
try:
|
| 427 |
+
stack = tb_StackSummary.extract(tb_walk_stack(f),
|
| 428 |
+
limit=DEBUG_STACK_DEPTH,
|
| 429 |
+
lookup_lines=False)
|
| 430 |
+
finally:
|
| 431 |
+
f = None
|
| 432 |
+
|
| 433 |
+
stack.reverse()
|
| 434 |
+
return stack
|
.venv/lib/python3.11/site-packages/uvloop/dns.pyx
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef __port_to_int(port, proto):
|
| 2 |
+
if type(port) is int:
|
| 3 |
+
return port
|
| 4 |
+
|
| 5 |
+
if port is None or port == '' or port == b'':
|
| 6 |
+
return 0
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
return int(port)
|
| 10 |
+
except (ValueError, TypeError):
|
| 11 |
+
pass
|
| 12 |
+
|
| 13 |
+
if isinstance(port, bytes):
|
| 14 |
+
port = port.decode()
|
| 15 |
+
|
| 16 |
+
if isinstance(port, str) and proto is not None:
|
| 17 |
+
if proto == uv.IPPROTO_TCP:
|
| 18 |
+
return socket_getservbyname(port, 'tcp')
|
| 19 |
+
elif proto == uv.IPPROTO_UDP:
|
| 20 |
+
return socket_getservbyname(port, 'udp')
|
| 21 |
+
|
| 22 |
+
raise OSError('service/proto not found')
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
|
| 26 |
+
# Converts sockaddr structs into what Python socket
|
| 27 |
+
# module can understand:
|
| 28 |
+
# - for IPv4 a tuple of (host, port)
|
| 29 |
+
# - for IPv6 a tuple of (host, port, flowinfo, scope_id)
|
| 30 |
+
|
| 31 |
+
cdef:
|
| 32 |
+
char buf[128] # INET6_ADDRSTRLEN is usually 46
|
| 33 |
+
int err
|
| 34 |
+
system.sockaddr_in *addr4
|
| 35 |
+
system.sockaddr_in6 *addr6
|
| 36 |
+
system.sockaddr_un *addr_un
|
| 37 |
+
|
| 38 |
+
if addr.sa_family == uv.AF_INET:
|
| 39 |
+
addr4 = <system.sockaddr_in*>addr
|
| 40 |
+
|
| 41 |
+
err = uv.uv_ip4_name(addr4, buf, sizeof(buf))
|
| 42 |
+
if err < 0:
|
| 43 |
+
raise convert_error(err)
|
| 44 |
+
|
| 45 |
+
return (
|
| 46 |
+
PyUnicode_FromString(buf),
|
| 47 |
+
system.ntohs(addr4.sin_port)
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
elif addr.sa_family == uv.AF_INET6:
|
| 51 |
+
addr6 = <system.sockaddr_in6*>addr
|
| 52 |
+
|
| 53 |
+
err = uv.uv_ip6_name(addr6, buf, sizeof(buf))
|
| 54 |
+
if err < 0:
|
| 55 |
+
raise convert_error(err)
|
| 56 |
+
|
| 57 |
+
return (
|
| 58 |
+
PyUnicode_FromString(buf),
|
| 59 |
+
system.ntohs(addr6.sin6_port),
|
| 60 |
+
system.ntohl(addr6.sin6_flowinfo),
|
| 61 |
+
addr6.sin6_scope_id
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
elif addr.sa_family == uv.AF_UNIX:
|
| 65 |
+
addr_un = <system.sockaddr_un*>addr
|
| 66 |
+
return system.MakeUnixSockPyAddr(addr_un)
|
| 67 |
+
|
| 68 |
+
raise RuntimeError("cannot convert sockaddr into Python object")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@cython.freelist(DEFAULT_FREELIST_SIZE)
|
| 72 |
+
cdef class SockAddrHolder:
|
| 73 |
+
cdef:
|
| 74 |
+
int family
|
| 75 |
+
system.sockaddr_storage addr
|
| 76 |
+
Py_ssize_t addr_size
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
cdef LruCache sockaddrs = LruCache(maxsize=DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
cdef __convert_pyaddr_to_sockaddr(int family, object addr,
|
| 83 |
+
system.sockaddr* res):
|
| 84 |
+
cdef:
|
| 85 |
+
int err
|
| 86 |
+
int addr_len
|
| 87 |
+
int scope_id = 0
|
| 88 |
+
int flowinfo = 0
|
| 89 |
+
char *buf
|
| 90 |
+
Py_ssize_t buflen
|
| 91 |
+
SockAddrHolder ret
|
| 92 |
+
|
| 93 |
+
ret = sockaddrs.get(addr, None)
|
| 94 |
+
if ret is not None and ret.family == family:
|
| 95 |
+
memcpy(res, &ret.addr, ret.addr_size)
|
| 96 |
+
return
|
| 97 |
+
|
| 98 |
+
ret = SockAddrHolder.__new__(SockAddrHolder)
|
| 99 |
+
if family == uv.AF_INET:
|
| 100 |
+
if not isinstance(addr, tuple):
|
| 101 |
+
raise TypeError('AF_INET address must be tuple')
|
| 102 |
+
if len(addr) != 2:
|
| 103 |
+
raise ValueError('AF_INET address must be tuple of (host, port)')
|
| 104 |
+
host, port = addr
|
| 105 |
+
if isinstance(host, str):
|
| 106 |
+
try:
|
| 107 |
+
# idna codec is rather slow, so we try ascii first.
|
| 108 |
+
host = host.encode('ascii')
|
| 109 |
+
except UnicodeEncodeError:
|
| 110 |
+
host = host.encode('idna')
|
| 111 |
+
if not isinstance(host, (bytes, bytearray)):
|
| 112 |
+
raise TypeError('host must be a string or bytes object')
|
| 113 |
+
|
| 114 |
+
port = __port_to_int(port, None)
|
| 115 |
+
|
| 116 |
+
ret.addr_size = sizeof(system.sockaddr_in)
|
| 117 |
+
err = uv.uv_ip4_addr(host, <int>port, <system.sockaddr_in*>&ret.addr)
|
| 118 |
+
if err < 0:
|
| 119 |
+
raise convert_error(err)
|
| 120 |
+
|
| 121 |
+
elif family == uv.AF_INET6:
|
| 122 |
+
if not isinstance(addr, tuple):
|
| 123 |
+
raise TypeError('AF_INET6 address must be tuple')
|
| 124 |
+
|
| 125 |
+
addr_len = len(addr)
|
| 126 |
+
if addr_len < 2 or addr_len > 4:
|
| 127 |
+
raise ValueError(
|
| 128 |
+
'AF_INET6 must be a tuple of 2-4 parameters: '
|
| 129 |
+
'(host, port, flowinfo?, scope_id?)')
|
| 130 |
+
|
| 131 |
+
host = addr[0]
|
| 132 |
+
if isinstance(host, str):
|
| 133 |
+
try:
|
| 134 |
+
# idna codec is rather slow, so we try ascii first.
|
| 135 |
+
host = host.encode('ascii')
|
| 136 |
+
except UnicodeEncodeError:
|
| 137 |
+
host = host.encode('idna')
|
| 138 |
+
if not isinstance(host, (bytes, bytearray)):
|
| 139 |
+
raise TypeError('host must be a string or bytes object')
|
| 140 |
+
|
| 141 |
+
port = __port_to_int(addr[1], None)
|
| 142 |
+
|
| 143 |
+
if addr_len > 2:
|
| 144 |
+
flowinfo = addr[2]
|
| 145 |
+
if addr_len > 3:
|
| 146 |
+
scope_id = addr[3]
|
| 147 |
+
|
| 148 |
+
ret.addr_size = sizeof(system.sockaddr_in6)
|
| 149 |
+
|
| 150 |
+
err = uv.uv_ip6_addr(host, port, <system.sockaddr_in6*>&ret.addr)
|
| 151 |
+
if err < 0:
|
| 152 |
+
raise convert_error(err)
|
| 153 |
+
|
| 154 |
+
(<system.sockaddr_in6*>&ret.addr).sin6_flowinfo = flowinfo
|
| 155 |
+
(<system.sockaddr_in6*>&ret.addr).sin6_scope_id = scope_id
|
| 156 |
+
|
| 157 |
+
elif family == uv.AF_UNIX:
|
| 158 |
+
if isinstance(addr, str):
|
| 159 |
+
addr = addr.encode(sys_getfilesystemencoding())
|
| 160 |
+
elif not isinstance(addr, bytes):
|
| 161 |
+
raise TypeError('AF_UNIX address must be a str or a bytes object')
|
| 162 |
+
|
| 163 |
+
PyBytes_AsStringAndSize(addr, &buf, &buflen)
|
| 164 |
+
if buflen > 107:
|
| 165 |
+
raise ValueError(
|
| 166 |
+
f'unix socket path {addr!r} is longer than 107 characters')
|
| 167 |
+
|
| 168 |
+
ret.addr_size = sizeof(system.sockaddr_un)
|
| 169 |
+
memset(&ret.addr, 0, sizeof(system.sockaddr_un))
|
| 170 |
+
(<system.sockaddr_un*>&ret.addr).sun_family = uv.AF_UNIX
|
| 171 |
+
memcpy((<system.sockaddr_un*>&ret.addr).sun_path, buf, buflen)
|
| 172 |
+
|
| 173 |
+
else:
|
| 174 |
+
raise ValueError(
|
| 175 |
+
f'expected AF_INET, AF_INET6, or AF_UNIX family, got {family}')
|
| 176 |
+
|
| 177 |
+
ret.family = family
|
| 178 |
+
sockaddrs[addr] = ret
|
| 179 |
+
memcpy(res, &ret.addr, ret.addr_size)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
cdef __static_getaddrinfo(object host, object port,
|
| 183 |
+
int family, int type,
|
| 184 |
+
int proto,
|
| 185 |
+
system.sockaddr *addr):
|
| 186 |
+
|
| 187 |
+
if proto not in {0, uv.IPPROTO_TCP, uv.IPPROTO_UDP}:
|
| 188 |
+
return
|
| 189 |
+
|
| 190 |
+
if _is_sock_stream(type):
|
| 191 |
+
proto = uv.IPPROTO_TCP
|
| 192 |
+
elif _is_sock_dgram(type):
|
| 193 |
+
proto = uv.IPPROTO_UDP
|
| 194 |
+
else:
|
| 195 |
+
return
|
| 196 |
+
|
| 197 |
+
try:
|
| 198 |
+
port = __port_to_int(port, proto)
|
| 199 |
+
except Exception:
|
| 200 |
+
return
|
| 201 |
+
|
| 202 |
+
hp = (host, port)
|
| 203 |
+
if family == uv.AF_UNSPEC:
|
| 204 |
+
try:
|
| 205 |
+
__convert_pyaddr_to_sockaddr(uv.AF_INET, hp, addr)
|
| 206 |
+
except Exception:
|
| 207 |
+
pass
|
| 208 |
+
else:
|
| 209 |
+
return (uv.AF_INET, type, proto)
|
| 210 |
+
|
| 211 |
+
try:
|
| 212 |
+
__convert_pyaddr_to_sockaddr(uv.AF_INET6, hp, addr)
|
| 213 |
+
except Exception:
|
| 214 |
+
pass
|
| 215 |
+
else:
|
| 216 |
+
return (uv.AF_INET6, type, proto)
|
| 217 |
+
|
| 218 |
+
else:
|
| 219 |
+
try:
|
| 220 |
+
__convert_pyaddr_to_sockaddr(family, hp, addr)
|
| 221 |
+
except Exception:
|
| 222 |
+
pass
|
| 223 |
+
else:
|
| 224 |
+
return (family, type, proto)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
cdef __static_getaddrinfo_pyaddr(object host, object port,
|
| 228 |
+
int family, int type,
|
| 229 |
+
int proto, int flags):
|
| 230 |
+
|
| 231 |
+
cdef:
|
| 232 |
+
system.sockaddr_storage addr
|
| 233 |
+
object triplet
|
| 234 |
+
|
| 235 |
+
triplet = __static_getaddrinfo(
|
| 236 |
+
host, port, family, type,
|
| 237 |
+
proto, <system.sockaddr*>&addr)
|
| 238 |
+
if triplet is None:
|
| 239 |
+
return
|
| 240 |
+
|
| 241 |
+
af, type, proto = triplet
|
| 242 |
+
|
| 243 |
+
try:
|
| 244 |
+
pyaddr = __convert_sockaddr_to_pyaddr(<system.sockaddr*>&addr)
|
| 245 |
+
except Exception:
|
| 246 |
+
return
|
| 247 |
+
|
| 248 |
+
# When the host is an IP while type is one of TCP or UDP, different libc
|
| 249 |
+
# implementations of getaddrinfo() behave differently:
|
| 250 |
+
# 1. When AI_CANONNAME is set:
|
| 251 |
+
# * glibc: returns ai_canonname
|
| 252 |
+
# * musl: returns ai_canonname
|
| 253 |
+
# * macOS: returns an empty string for ai_canonname
|
| 254 |
+
# 2. When AI_CANONNAME is NOT set:
|
| 255 |
+
# * glibc: returns an empty string for ai_canonname
|
| 256 |
+
# * musl: returns ai_canonname
|
| 257 |
+
# * macOS: returns an empty string for ai_canonname
|
| 258 |
+
# At the same time, libuv and CPython both uses libc directly, even though
|
| 259 |
+
# this different behavior is violating what is in the documentation.
|
| 260 |
+
#
|
| 261 |
+
# uvloop potentially should be a 100% drop-in replacement for asyncio,
|
| 262 |
+
# doing whatever asyncio does, especially when the libc implementations are
|
| 263 |
+
# also different in the same way. However, making our implementation to be
|
| 264 |
+
# consistent with libc/CPython would be complex and hard to maintain
|
| 265 |
+
# (including caching libc behaviors when flag is/not set), therefore we
|
| 266 |
+
# decided to simply normalize the behavior in uvloop for this very marginal
|
| 267 |
+
# case following the documentation, even though uvloop would behave
|
| 268 |
+
# differently to asyncio on macOS and musl platforms, when again the host
|
| 269 |
+
# is an IP and type is one of TCP or UDP.
|
| 270 |
+
# All other cases are still asyncio-compatible.
|
| 271 |
+
if flags & socket_AI_CANONNAME:
|
| 272 |
+
if isinstance(host, str):
|
| 273 |
+
canon_name = host
|
| 274 |
+
else:
|
| 275 |
+
canon_name = host.decode('ascii')
|
| 276 |
+
else:
|
| 277 |
+
canon_name = ''
|
| 278 |
+
|
| 279 |
+
return (
|
| 280 |
+
_intenum_converter(af, socket_AddressFamily),
|
| 281 |
+
_intenum_converter(type, socket_SocketKind),
|
| 282 |
+
proto,
|
| 283 |
+
canon_name,
|
| 284 |
+
pyaddr,
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
@cython.freelist(DEFAULT_FREELIST_SIZE)
|
| 289 |
+
cdef class AddrInfo:
|
| 290 |
+
cdef:
|
| 291 |
+
system.addrinfo *data
|
| 292 |
+
|
| 293 |
+
def __cinit__(self):
|
| 294 |
+
self.data = NULL
|
| 295 |
+
|
| 296 |
+
def __dealloc__(self):
|
| 297 |
+
if self.data is not NULL:
|
| 298 |
+
uv.uv_freeaddrinfo(self.data) # returns void
|
| 299 |
+
self.data = NULL
|
| 300 |
+
|
| 301 |
+
cdef void set_data(self, system.addrinfo *data) noexcept:
|
| 302 |
+
self.data = data
|
| 303 |
+
|
| 304 |
+
cdef unpack(self):
|
| 305 |
+
cdef:
|
| 306 |
+
list result = []
|
| 307 |
+
system.addrinfo *ptr
|
| 308 |
+
|
| 309 |
+
if self.data is NULL:
|
| 310 |
+
raise RuntimeError('AddrInfo.data is NULL')
|
| 311 |
+
|
| 312 |
+
ptr = self.data
|
| 313 |
+
while ptr != NULL:
|
| 314 |
+
if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6):
|
| 315 |
+
result.append((
|
| 316 |
+
_intenum_converter(ptr.ai_family, socket_AddressFamily),
|
| 317 |
+
_intenum_converter(ptr.ai_socktype, socket_SocketKind),
|
| 318 |
+
ptr.ai_protocol,
|
| 319 |
+
('' if ptr.ai_canonname is NULL else
|
| 320 |
+
(<bytes>ptr.ai_canonname).decode()),
|
| 321 |
+
__convert_sockaddr_to_pyaddr(ptr.ai_addr)
|
| 322 |
+
))
|
| 323 |
+
|
| 324 |
+
ptr = ptr.ai_next
|
| 325 |
+
|
| 326 |
+
return result
|
| 327 |
+
|
| 328 |
+
@staticmethod
|
| 329 |
+
cdef int isinstance(object other):
|
| 330 |
+
return type(other) is AddrInfo
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
cdef class AddrInfoRequest(UVRequest):
|
| 334 |
+
cdef:
|
| 335 |
+
system.addrinfo hints
|
| 336 |
+
object callback
|
| 337 |
+
uv.uv_getaddrinfo_t _req_data
|
| 338 |
+
|
| 339 |
+
def __cinit__(self, Loop loop,
|
| 340 |
+
bytes host, bytes port,
|
| 341 |
+
int family, int type, int proto, int flags,
|
| 342 |
+
object callback):
|
| 343 |
+
|
| 344 |
+
cdef:
|
| 345 |
+
int err
|
| 346 |
+
char *chost
|
| 347 |
+
char *cport
|
| 348 |
+
|
| 349 |
+
if host is None:
|
| 350 |
+
chost = NULL
|
| 351 |
+
elif host == b'' and sys.platform == 'darwin':
|
| 352 |
+
# It seems `getaddrinfo("", ...)` on macOS is equivalent to
|
| 353 |
+
# `getaddrinfo("localhost", ...)`. This is inconsistent with
|
| 354 |
+
# libuv 1.48 which treats empty nodename as EINVAL.
|
| 355 |
+
chost = <char*>'localhost'
|
| 356 |
+
else:
|
| 357 |
+
chost = <char*>host
|
| 358 |
+
|
| 359 |
+
if port is None:
|
| 360 |
+
cport = NULL
|
| 361 |
+
else:
|
| 362 |
+
cport = <char*>port
|
| 363 |
+
|
| 364 |
+
memset(&self.hints, 0, sizeof(system.addrinfo))
|
| 365 |
+
self.hints.ai_flags = flags
|
| 366 |
+
self.hints.ai_family = family
|
| 367 |
+
self.hints.ai_socktype = type
|
| 368 |
+
self.hints.ai_protocol = proto
|
| 369 |
+
|
| 370 |
+
self.request = <uv.uv_req_t*> &self._req_data
|
| 371 |
+
self.callback = callback
|
| 372 |
+
self.request.data = <void*>self
|
| 373 |
+
|
| 374 |
+
err = uv.uv_getaddrinfo(loop.uvloop,
|
| 375 |
+
<uv.uv_getaddrinfo_t*>self.request,
|
| 376 |
+
__on_addrinfo_resolved,
|
| 377 |
+
chost,
|
| 378 |
+
cport,
|
| 379 |
+
&self.hints)
|
| 380 |
+
|
| 381 |
+
if err < 0:
|
| 382 |
+
self.on_done()
|
| 383 |
+
try:
|
| 384 |
+
if err == uv.UV_EINVAL:
|
| 385 |
+
# Convert UV_EINVAL to EAI_NONAME to match libc behavior
|
| 386 |
+
msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8')
|
| 387 |
+
ex = socket_gaierror(socket_EAI_NONAME, msg)
|
| 388 |
+
else:
|
| 389 |
+
ex = convert_error(err)
|
| 390 |
+
except Exception as ex:
|
| 391 |
+
callback(ex)
|
| 392 |
+
else:
|
| 393 |
+
callback(ex)
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
cdef class NameInfoRequest(UVRequest):
|
| 397 |
+
cdef:
|
| 398 |
+
object callback
|
| 399 |
+
uv.uv_getnameinfo_t _req_data
|
| 400 |
+
|
| 401 |
+
def __cinit__(self, Loop loop, callback):
|
| 402 |
+
self.request = <uv.uv_req_t*> &self._req_data
|
| 403 |
+
self.callback = callback
|
| 404 |
+
self.request.data = <void*>self
|
| 405 |
+
|
| 406 |
+
cdef query(self, system.sockaddr *addr, int flags):
|
| 407 |
+
cdef int err
|
| 408 |
+
err = uv.uv_getnameinfo(self.loop.uvloop,
|
| 409 |
+
<uv.uv_getnameinfo_t*>self.request,
|
| 410 |
+
__on_nameinfo_resolved,
|
| 411 |
+
addr,
|
| 412 |
+
flags)
|
| 413 |
+
if err < 0:
|
| 414 |
+
self.on_done()
|
| 415 |
+
self.callback(convert_error(err))
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
cdef _intenum_converter(value, enum_klass):
|
| 419 |
+
try:
|
| 420 |
+
return enum_klass(value)
|
| 421 |
+
except ValueError:
|
| 422 |
+
return value
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
cdef void __on_addrinfo_resolved(
|
| 426 |
+
uv.uv_getaddrinfo_t *resolver,
|
| 427 |
+
int status,
|
| 428 |
+
system.addrinfo *res,
|
| 429 |
+
) noexcept with gil:
|
| 430 |
+
|
| 431 |
+
if resolver.data is NULL:
|
| 432 |
+
aio_logger.error(
|
| 433 |
+
'AddrInfoRequest callback called with NULL resolver.data')
|
| 434 |
+
return
|
| 435 |
+
|
| 436 |
+
cdef:
|
| 437 |
+
AddrInfoRequest request = <AddrInfoRequest> resolver.data
|
| 438 |
+
Loop loop = request.loop
|
| 439 |
+
object callback = request.callback
|
| 440 |
+
AddrInfo ai
|
| 441 |
+
|
| 442 |
+
try:
|
| 443 |
+
if status < 0:
|
| 444 |
+
callback(convert_error(status))
|
| 445 |
+
else:
|
| 446 |
+
ai = AddrInfo()
|
| 447 |
+
ai.set_data(res)
|
| 448 |
+
callback(ai)
|
| 449 |
+
except (KeyboardInterrupt, SystemExit):
|
| 450 |
+
raise
|
| 451 |
+
except BaseException as ex:
|
| 452 |
+
loop._handle_exception(ex)
|
| 453 |
+
finally:
|
| 454 |
+
request.on_done()
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
cdef void __on_nameinfo_resolved(
|
| 458 |
+
uv.uv_getnameinfo_t* req,
|
| 459 |
+
int status,
|
| 460 |
+
const char* hostname,
|
| 461 |
+
const char* service,
|
| 462 |
+
) noexcept with gil:
|
| 463 |
+
cdef:
|
| 464 |
+
NameInfoRequest request = <NameInfoRequest> req.data
|
| 465 |
+
Loop loop = request.loop
|
| 466 |
+
object callback = request.callback
|
| 467 |
+
|
| 468 |
+
try:
|
| 469 |
+
if status < 0:
|
| 470 |
+
callback(convert_error(status))
|
| 471 |
+
else:
|
| 472 |
+
callback(((<bytes>hostname).decode(),
|
| 473 |
+
(<bytes>service).decode()))
|
| 474 |
+
except (KeyboardInterrupt, SystemExit):
|
| 475 |
+
raise
|
| 476 |
+
except BaseException as ex:
|
| 477 |
+
loop._handle_exception(ex)
|
| 478 |
+
finally:
|
| 479 |
+
request.on_done()
|
.venv/lib/python3.11/site-packages/uvloop/errors.pyx
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef str __strerr(int errno):
|
| 2 |
+
return strerror(errno).decode()
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
cdef __convert_python_error(int uverr):
|
| 6 |
+
# XXX Won't work for Windows:
|
| 7 |
+
# From libuv docs:
|
| 8 |
+
# Implementation detail: on Unix error codes are the
|
| 9 |
+
# negated errno (or -errno), while on Windows they
|
| 10 |
+
# are defined by libuv to arbitrary negative numbers.
|
| 11 |
+
cdef int oserr = -uverr
|
| 12 |
+
|
| 13 |
+
exc = OSError
|
| 14 |
+
|
| 15 |
+
if uverr in (uv.UV_EACCES, uv.UV_EPERM):
|
| 16 |
+
exc = PermissionError
|
| 17 |
+
|
| 18 |
+
elif uverr in (uv.UV_EAGAIN, uv.UV_EALREADY):
|
| 19 |
+
exc = BlockingIOError
|
| 20 |
+
|
| 21 |
+
elif uverr in (uv.UV_EPIPE, uv.UV_ESHUTDOWN):
|
| 22 |
+
exc = BrokenPipeError
|
| 23 |
+
|
| 24 |
+
elif uverr == uv.UV_ECONNABORTED:
|
| 25 |
+
exc = ConnectionAbortedError
|
| 26 |
+
|
| 27 |
+
elif uverr == uv.UV_ECONNREFUSED:
|
| 28 |
+
exc = ConnectionRefusedError
|
| 29 |
+
|
| 30 |
+
elif uverr == uv.UV_ECONNRESET:
|
| 31 |
+
exc = ConnectionResetError
|
| 32 |
+
|
| 33 |
+
elif uverr == uv.UV_EEXIST:
|
| 34 |
+
exc = FileExistsError
|
| 35 |
+
|
| 36 |
+
elif uverr == uv.UV_ENOENT:
|
| 37 |
+
exc = FileNotFoundError
|
| 38 |
+
|
| 39 |
+
elif uverr == uv.UV_EINTR:
|
| 40 |
+
exc = InterruptedError
|
| 41 |
+
|
| 42 |
+
elif uverr == uv.UV_EISDIR:
|
| 43 |
+
exc = IsADirectoryError
|
| 44 |
+
|
| 45 |
+
elif uverr == uv.UV_ESRCH:
|
| 46 |
+
exc = ProcessLookupError
|
| 47 |
+
|
| 48 |
+
elif uverr == uv.UV_ETIMEDOUT:
|
| 49 |
+
exc = TimeoutError
|
| 50 |
+
|
| 51 |
+
return exc(oserr, __strerr(oserr))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
cdef int __convert_socket_error(int uverr):
|
| 55 |
+
cdef int sock_err = 0
|
| 56 |
+
|
| 57 |
+
if uverr == uv.UV_EAI_ADDRFAMILY:
|
| 58 |
+
sock_err = socket_EAI_ADDRFAMILY
|
| 59 |
+
|
| 60 |
+
elif uverr == uv.UV_EAI_AGAIN:
|
| 61 |
+
sock_err = socket_EAI_AGAIN
|
| 62 |
+
|
| 63 |
+
elif uverr == uv.UV_EAI_BADFLAGS:
|
| 64 |
+
sock_err = socket_EAI_BADFLAGS
|
| 65 |
+
|
| 66 |
+
elif uverr == uv.UV_EAI_BADHINTS:
|
| 67 |
+
sock_err = socket_EAI_BADHINTS
|
| 68 |
+
|
| 69 |
+
elif uverr == uv.UV_EAI_CANCELED:
|
| 70 |
+
sock_err = socket_EAI_CANCELED
|
| 71 |
+
|
| 72 |
+
elif uverr == uv.UV_EAI_FAIL:
|
| 73 |
+
sock_err = socket_EAI_FAIL
|
| 74 |
+
|
| 75 |
+
elif uverr == uv.UV_EAI_FAMILY:
|
| 76 |
+
sock_err = socket_EAI_FAMILY
|
| 77 |
+
|
| 78 |
+
elif uverr == uv.UV_EAI_MEMORY:
|
| 79 |
+
sock_err = socket_EAI_MEMORY
|
| 80 |
+
|
| 81 |
+
elif uverr == uv.UV_EAI_NODATA:
|
| 82 |
+
sock_err = socket_EAI_NODATA
|
| 83 |
+
|
| 84 |
+
elif uverr == uv.UV_EAI_NONAME:
|
| 85 |
+
sock_err = socket_EAI_NONAME
|
| 86 |
+
|
| 87 |
+
elif uverr == uv.UV_EAI_OVERFLOW:
|
| 88 |
+
sock_err = socket_EAI_OVERFLOW
|
| 89 |
+
|
| 90 |
+
elif uverr == uv.UV_EAI_PROTOCOL:
|
| 91 |
+
sock_err = socket_EAI_PROTOCOL
|
| 92 |
+
|
| 93 |
+
elif uverr == uv.UV_EAI_SERVICE:
|
| 94 |
+
sock_err = socket_EAI_SERVICE
|
| 95 |
+
|
| 96 |
+
elif uverr == uv.UV_EAI_SOCKTYPE:
|
| 97 |
+
sock_err = socket_EAI_SOCKTYPE
|
| 98 |
+
|
| 99 |
+
return sock_err
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
cdef convert_error(int uverr):
|
| 103 |
+
cdef int sock_err
|
| 104 |
+
|
| 105 |
+
if uverr == uv.UV_ECANCELED:
|
| 106 |
+
return aio_CancelledError()
|
| 107 |
+
|
| 108 |
+
sock_err = __convert_socket_error(uverr)
|
| 109 |
+
if sock_err:
|
| 110 |
+
msg = system.gai_strerror(sock_err).decode('utf-8')
|
| 111 |
+
return socket_gaierror(sock_err, msg)
|
| 112 |
+
|
| 113 |
+
return __convert_python_error(uverr)
|
.venv/lib/python3.11/site-packages/uvloop/handles/async_.pxd
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVAsync(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
method_t callback
|
| 4 |
+
object ctx
|
| 5 |
+
|
| 6 |
+
cdef _init(self, Loop loop, method_t callback, object ctx)
|
| 7 |
+
|
| 8 |
+
cdef send(self)
|
| 9 |
+
|
| 10 |
+
@staticmethod
|
| 11 |
+
cdef UVAsync new(Loop loop, method_t callback, object ctx)
|
.venv/lib/python3.11/site-packages/uvloop/handles/async_.pyx
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVAsync(UVHandle):
|
| 3 |
+
cdef _init(self, Loop loop, method_t callback, object ctx):
|
| 4 |
+
cdef int err
|
| 5 |
+
|
| 6 |
+
self._start_init(loop)
|
| 7 |
+
|
| 8 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_async_t))
|
| 9 |
+
if self._handle is NULL:
|
| 10 |
+
self._abort_init()
|
| 11 |
+
raise MemoryError()
|
| 12 |
+
|
| 13 |
+
err = uv.uv_async_init(self._loop.uvloop,
|
| 14 |
+
<uv.uv_async_t*>self._handle,
|
| 15 |
+
__uvasync_callback)
|
| 16 |
+
if err < 0:
|
| 17 |
+
self._abort_init()
|
| 18 |
+
raise convert_error(err)
|
| 19 |
+
|
| 20 |
+
self._finish_init()
|
| 21 |
+
|
| 22 |
+
self.callback = callback
|
| 23 |
+
self.ctx = ctx
|
| 24 |
+
|
| 25 |
+
cdef send(self):
|
| 26 |
+
cdef int err
|
| 27 |
+
|
| 28 |
+
self._ensure_alive()
|
| 29 |
+
|
| 30 |
+
err = uv.uv_async_send(<uv.uv_async_t*>self._handle)
|
| 31 |
+
if err < 0:
|
| 32 |
+
exc = convert_error(err)
|
| 33 |
+
self._fatal_error(exc, True)
|
| 34 |
+
return
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
cdef UVAsync new(Loop loop, method_t callback, object ctx):
|
| 38 |
+
cdef UVAsync handle
|
| 39 |
+
handle = UVAsync.__new__(UVAsync)
|
| 40 |
+
handle._init(loop, callback, ctx)
|
| 41 |
+
return handle
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
cdef void __uvasync_callback(
|
| 45 |
+
uv.uv_async_t* handle,
|
| 46 |
+
) noexcept with gil:
|
| 47 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle, "UVAsync callback") == 0:
|
| 48 |
+
return
|
| 49 |
+
|
| 50 |
+
cdef:
|
| 51 |
+
UVAsync async_ = <UVAsync> handle.data
|
| 52 |
+
method_t cb = async_.callback
|
| 53 |
+
try:
|
| 54 |
+
cb(async_.ctx)
|
| 55 |
+
except BaseException as ex:
|
| 56 |
+
async_._error(ex, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pxd
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVBaseTransport(UVSocketHandle):
|
| 2 |
+
|
| 3 |
+
cdef:
|
| 4 |
+
readonly bint _closing
|
| 5 |
+
|
| 6 |
+
bint _protocol_connected
|
| 7 |
+
bint _protocol_paused
|
| 8 |
+
object _protocol_data_received
|
| 9 |
+
size_t _high_water
|
| 10 |
+
size_t _low_water
|
| 11 |
+
|
| 12 |
+
object _protocol
|
| 13 |
+
Server _server
|
| 14 |
+
object _waiter
|
| 15 |
+
|
| 16 |
+
dict _extra_info
|
| 17 |
+
|
| 18 |
+
uint32_t _conn_lost
|
| 19 |
+
|
| 20 |
+
object __weakref__
|
| 21 |
+
|
| 22 |
+
# All "inline" methods are final
|
| 23 |
+
|
| 24 |
+
cdef inline _maybe_pause_protocol(self)
|
| 25 |
+
cdef inline _maybe_resume_protocol(self)
|
| 26 |
+
|
| 27 |
+
cdef inline _schedule_call_connection_made(self)
|
| 28 |
+
cdef inline _schedule_call_connection_lost(self, exc)
|
| 29 |
+
|
| 30 |
+
cdef _wakeup_waiter(self)
|
| 31 |
+
cdef _call_connection_made(self)
|
| 32 |
+
cdef _call_connection_lost(self, exc)
|
| 33 |
+
|
| 34 |
+
# Overloads of UVHandle methods:
|
| 35 |
+
cdef _fatal_error(self, exc, throw, reason=?)
|
| 36 |
+
cdef _close(self)
|
| 37 |
+
|
| 38 |
+
cdef inline _set_server(self, Server server)
|
| 39 |
+
cdef inline _set_waiter(self, object waiter)
|
| 40 |
+
|
| 41 |
+
cdef _set_protocol(self, object protocol)
|
| 42 |
+
cdef _clear_protocol(self)
|
| 43 |
+
|
| 44 |
+
cdef inline _init_protocol(self)
|
| 45 |
+
cdef inline _add_extra_info(self, str name, object obj)
|
| 46 |
+
|
| 47 |
+
# === overloads ===
|
| 48 |
+
|
| 49 |
+
cdef _new_socket(self)
|
| 50 |
+
cdef size_t _get_write_buffer_size(self)
|
| 51 |
+
|
| 52 |
+
cdef bint _is_reading(self)
|
| 53 |
+
cdef _start_reading(self)
|
| 54 |
+
cdef _stop_reading(self)
|
.venv/lib/python3.11/site-packages/uvloop/handles/basetransport.pyx
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVBaseTransport(UVSocketHandle):
|
| 2 |
+
|
| 3 |
+
def __cinit__(self):
|
| 4 |
+
# Flow control
|
| 5 |
+
self._high_water = FLOW_CONTROL_HIGH_WATER * 1024
|
| 6 |
+
self._low_water = FLOW_CONTROL_HIGH_WATER // 4
|
| 7 |
+
|
| 8 |
+
self._protocol = None
|
| 9 |
+
self._protocol_connected = 0
|
| 10 |
+
self._protocol_paused = 0
|
| 11 |
+
self._protocol_data_received = None
|
| 12 |
+
|
| 13 |
+
self._server = None
|
| 14 |
+
self._waiter = None
|
| 15 |
+
self._extra_info = None
|
| 16 |
+
|
| 17 |
+
self._conn_lost = 0
|
| 18 |
+
|
| 19 |
+
self._closing = 0
|
| 20 |
+
|
| 21 |
+
cdef size_t _get_write_buffer_size(self):
|
| 22 |
+
return 0
|
| 23 |
+
|
| 24 |
+
cdef inline _schedule_call_connection_made(self):
|
| 25 |
+
self._loop._call_soon_handle(
|
| 26 |
+
new_MethodHandle(self._loop,
|
| 27 |
+
"UVTransport._call_connection_made",
|
| 28 |
+
<method_t>self._call_connection_made,
|
| 29 |
+
self.context,
|
| 30 |
+
self))
|
| 31 |
+
|
| 32 |
+
cdef inline _schedule_call_connection_lost(self, exc):
|
| 33 |
+
self._loop._call_soon_handle(
|
| 34 |
+
new_MethodHandle1(self._loop,
|
| 35 |
+
"UVTransport._call_connection_lost",
|
| 36 |
+
<method1_t>self._call_connection_lost,
|
| 37 |
+
self.context,
|
| 38 |
+
self, exc))
|
| 39 |
+
|
| 40 |
+
cdef _fatal_error(self, exc, throw, reason=None):
|
| 41 |
+
# Overload UVHandle._fatal_error
|
| 42 |
+
|
| 43 |
+
self._force_close(exc)
|
| 44 |
+
|
| 45 |
+
if not isinstance(exc, OSError):
|
| 46 |
+
|
| 47 |
+
if throw or self._loop is None:
|
| 48 |
+
raise exc
|
| 49 |
+
|
| 50 |
+
msg = f'Fatal error on transport {self.__class__.__name__}'
|
| 51 |
+
if reason is not None:
|
| 52 |
+
msg = f'{msg} ({reason})'
|
| 53 |
+
|
| 54 |
+
self._loop.call_exception_handler({
|
| 55 |
+
'message': msg,
|
| 56 |
+
'exception': exc,
|
| 57 |
+
'transport': self,
|
| 58 |
+
'protocol': self._protocol,
|
| 59 |
+
})
|
| 60 |
+
|
| 61 |
+
cdef inline _maybe_pause_protocol(self):
|
| 62 |
+
cdef:
|
| 63 |
+
size_t size = self._get_write_buffer_size()
|
| 64 |
+
|
| 65 |
+
if size <= self._high_water:
|
| 66 |
+
return
|
| 67 |
+
|
| 68 |
+
if not self._protocol_paused:
|
| 69 |
+
self._protocol_paused = 1
|
| 70 |
+
try:
|
| 71 |
+
# _maybe_pause_protocol() is always triggered from user-calls,
|
| 72 |
+
# so we must copy the context to avoid entering context twice
|
| 73 |
+
run_in_context(
|
| 74 |
+
self.context.copy(), self._protocol.pause_writing,
|
| 75 |
+
)
|
| 76 |
+
except (KeyboardInterrupt, SystemExit):
|
| 77 |
+
raise
|
| 78 |
+
except BaseException as exc:
|
| 79 |
+
self._loop.call_exception_handler({
|
| 80 |
+
'message': 'protocol.pause_writing() failed',
|
| 81 |
+
'exception': exc,
|
| 82 |
+
'transport': self,
|
| 83 |
+
'protocol': self._protocol,
|
| 84 |
+
})
|
| 85 |
+
|
| 86 |
+
cdef inline _maybe_resume_protocol(self):
|
| 87 |
+
cdef:
|
| 88 |
+
size_t size = self._get_write_buffer_size()
|
| 89 |
+
|
| 90 |
+
if self._protocol_paused and size <= self._low_water:
|
| 91 |
+
self._protocol_paused = 0
|
| 92 |
+
try:
|
| 93 |
+
# We're copying the context to avoid entering context twice,
|
| 94 |
+
# even though it's not always necessary to copy - it's easier
|
| 95 |
+
# to copy here than passing down a copied context.
|
| 96 |
+
run_in_context(
|
| 97 |
+
self.context.copy(), self._protocol.resume_writing,
|
| 98 |
+
)
|
| 99 |
+
except (KeyboardInterrupt, SystemExit):
|
| 100 |
+
raise
|
| 101 |
+
except BaseException as exc:
|
| 102 |
+
self._loop.call_exception_handler({
|
| 103 |
+
'message': 'protocol.resume_writing() failed',
|
| 104 |
+
'exception': exc,
|
| 105 |
+
'transport': self,
|
| 106 |
+
'protocol': self._protocol,
|
| 107 |
+
})
|
| 108 |
+
|
| 109 |
+
cdef _wakeup_waiter(self):
|
| 110 |
+
if self._waiter is not None:
|
| 111 |
+
if not self._waiter.cancelled():
|
| 112 |
+
if not self._is_alive():
|
| 113 |
+
self._waiter.set_exception(
|
| 114 |
+
RuntimeError(
|
| 115 |
+
'closed Transport handle and unset waiter'))
|
| 116 |
+
else:
|
| 117 |
+
self._waiter.set_result(True)
|
| 118 |
+
self._waiter = None
|
| 119 |
+
|
| 120 |
+
cdef _call_connection_made(self):
|
| 121 |
+
if self._protocol is None:
|
| 122 |
+
raise RuntimeError(
|
| 123 |
+
'protocol is not set, cannot call connection_made()')
|
| 124 |
+
|
| 125 |
+
# We use `_is_alive()` and not `_closing`, because we call
|
| 126 |
+
# `transport._close()` in `loop.create_connection()` if an
|
| 127 |
+
# exception happens during `await waiter`.
|
| 128 |
+
if not self._is_alive():
|
| 129 |
+
# A connection waiter can be cancelled between
|
| 130 |
+
# 'await loop.create_connection()' and
|
| 131 |
+
# `_schedule_call_connection_made` and
|
| 132 |
+
# the actual `_call_connection_made`.
|
| 133 |
+
self._wakeup_waiter()
|
| 134 |
+
return
|
| 135 |
+
|
| 136 |
+
# Set _protocol_connected to 1 before calling "connection_made":
|
| 137 |
+
# if transport is aborted or closed, "connection_lost" will
|
| 138 |
+
# still be scheduled.
|
| 139 |
+
self._protocol_connected = 1
|
| 140 |
+
|
| 141 |
+
try:
|
| 142 |
+
self._protocol.connection_made(self)
|
| 143 |
+
except BaseException:
|
| 144 |
+
self._wakeup_waiter()
|
| 145 |
+
raise
|
| 146 |
+
|
| 147 |
+
if not self._is_alive():
|
| 148 |
+
# This might happen when "transport.abort()" is called
|
| 149 |
+
# from "Protocol.connection_made".
|
| 150 |
+
self._wakeup_waiter()
|
| 151 |
+
return
|
| 152 |
+
|
| 153 |
+
self._start_reading()
|
| 154 |
+
self._wakeup_waiter()
|
| 155 |
+
|
| 156 |
+
cdef _call_connection_lost(self, exc):
|
| 157 |
+
if self._waiter is not None:
|
| 158 |
+
if not self._waiter.done():
|
| 159 |
+
self._waiter.set_exception(exc)
|
| 160 |
+
self._waiter = None
|
| 161 |
+
|
| 162 |
+
if self._closed:
|
| 163 |
+
# The handle is closed -- likely, _call_connection_lost
|
| 164 |
+
# was already called before.
|
| 165 |
+
return
|
| 166 |
+
|
| 167 |
+
try:
|
| 168 |
+
if self._protocol_connected:
|
| 169 |
+
self._protocol.connection_lost(exc)
|
| 170 |
+
finally:
|
| 171 |
+
self._clear_protocol()
|
| 172 |
+
|
| 173 |
+
self._close()
|
| 174 |
+
|
| 175 |
+
server = self._server
|
| 176 |
+
if server is not None:
|
| 177 |
+
(<Server>server)._detach()
|
| 178 |
+
self._server = None
|
| 179 |
+
|
| 180 |
+
cdef inline _set_server(self, Server server):
|
| 181 |
+
self._server = server
|
| 182 |
+
(<Server>server)._attach()
|
| 183 |
+
|
| 184 |
+
cdef inline _set_waiter(self, object waiter):
|
| 185 |
+
if waiter is not None and not isfuture(waiter):
|
| 186 |
+
raise TypeError(
|
| 187 |
+
f'invalid waiter object {waiter!r}, expected asyncio.Future')
|
| 188 |
+
|
| 189 |
+
self._waiter = waiter
|
| 190 |
+
|
| 191 |
+
cdef _set_protocol(self, object protocol):
|
| 192 |
+
self._protocol = protocol
|
| 193 |
+
# Store a reference to the bound method directly
|
| 194 |
+
try:
|
| 195 |
+
self._protocol_data_received = protocol.data_received
|
| 196 |
+
except AttributeError:
|
| 197 |
+
pass
|
| 198 |
+
|
| 199 |
+
cdef _clear_protocol(self):
|
| 200 |
+
self._protocol = None
|
| 201 |
+
self._protocol_data_received = None
|
| 202 |
+
|
| 203 |
+
cdef inline _init_protocol(self):
|
| 204 |
+
self._loop._track_transport(self)
|
| 205 |
+
if self._protocol is None:
|
| 206 |
+
raise RuntimeError('invalid _init_protocol call')
|
| 207 |
+
self._schedule_call_connection_made()
|
| 208 |
+
|
| 209 |
+
cdef inline _add_extra_info(self, str name, object obj):
|
| 210 |
+
if self._extra_info is None:
|
| 211 |
+
self._extra_info = {}
|
| 212 |
+
self._extra_info[name] = obj
|
| 213 |
+
|
| 214 |
+
cdef bint _is_reading(self):
|
| 215 |
+
raise NotImplementedError
|
| 216 |
+
|
| 217 |
+
cdef _start_reading(self):
|
| 218 |
+
raise NotImplementedError
|
| 219 |
+
|
| 220 |
+
cdef _stop_reading(self):
|
| 221 |
+
raise NotImplementedError
|
| 222 |
+
|
| 223 |
+
# === Public API ===
|
| 224 |
+
|
| 225 |
+
property _paused:
|
| 226 |
+
# Used by SSLProto. Might be removed in the future.
|
| 227 |
+
def __get__(self):
|
| 228 |
+
return bool(not self._is_reading())
|
| 229 |
+
|
| 230 |
+
def get_protocol(self):
|
| 231 |
+
return self._protocol
|
| 232 |
+
|
| 233 |
+
def set_protocol(self, protocol):
|
| 234 |
+
self._set_protocol(protocol)
|
| 235 |
+
if self._is_reading():
|
| 236 |
+
self._stop_reading()
|
| 237 |
+
self._start_reading()
|
| 238 |
+
|
| 239 |
+
def _force_close(self, exc):
|
| 240 |
+
# Used by SSLProto. Might be removed in the future.
|
| 241 |
+
if self._conn_lost or self._closed:
|
| 242 |
+
return
|
| 243 |
+
if not self._closing:
|
| 244 |
+
self._closing = 1
|
| 245 |
+
self._stop_reading()
|
| 246 |
+
self._conn_lost += 1
|
| 247 |
+
self._schedule_call_connection_lost(exc)
|
| 248 |
+
|
| 249 |
+
def abort(self):
|
| 250 |
+
self._force_close(None)
|
| 251 |
+
|
| 252 |
+
def close(self):
|
| 253 |
+
if self._closing or self._closed:
|
| 254 |
+
return
|
| 255 |
+
|
| 256 |
+
self._closing = 1
|
| 257 |
+
self._stop_reading()
|
| 258 |
+
|
| 259 |
+
if not self._get_write_buffer_size():
|
| 260 |
+
# The write buffer is empty
|
| 261 |
+
self._conn_lost += 1
|
| 262 |
+
self._schedule_call_connection_lost(None)
|
| 263 |
+
|
| 264 |
+
def is_closing(self):
|
| 265 |
+
return self._closing
|
| 266 |
+
|
| 267 |
+
def get_write_buffer_size(self):
|
| 268 |
+
return self._get_write_buffer_size()
|
| 269 |
+
|
| 270 |
+
def set_write_buffer_limits(self, high=None, low=None):
|
| 271 |
+
self._ensure_alive()
|
| 272 |
+
|
| 273 |
+
self._high_water, self._low_water = add_flowcontrol_defaults(
|
| 274 |
+
high, low, FLOW_CONTROL_HIGH_WATER)
|
| 275 |
+
|
| 276 |
+
self._maybe_pause_protocol()
|
| 277 |
+
|
| 278 |
+
def get_write_buffer_limits(self):
|
| 279 |
+
return (self._low_water, self._high_water)
|
| 280 |
+
|
| 281 |
+
def get_extra_info(self, name, default=None):
|
| 282 |
+
if self._extra_info is not None and name in self._extra_info:
|
| 283 |
+
return self._extra_info[name]
|
| 284 |
+
if name == 'socket':
|
| 285 |
+
return self._get_socket()
|
| 286 |
+
if name == 'sockname':
|
| 287 |
+
return self._get_socket().getsockname()
|
| 288 |
+
if name == 'peername':
|
| 289 |
+
try:
|
| 290 |
+
return self._get_socket().getpeername()
|
| 291 |
+
except socket_error:
|
| 292 |
+
return default
|
| 293 |
+
return default
|
.venv/lib/python3.11/site-packages/uvloop/handles/check.pxd
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVCheck(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
Handle h
|
| 4 |
+
bint running
|
| 5 |
+
|
| 6 |
+
# All "inline" methods are final
|
| 7 |
+
|
| 8 |
+
cdef _init(self, Loop loop, Handle h)
|
| 9 |
+
|
| 10 |
+
cdef inline stop(self)
|
| 11 |
+
cdef inline start(self)
|
| 12 |
+
|
| 13 |
+
@staticmethod
|
| 14 |
+
cdef UVCheck new(Loop loop, Handle h)
|
.venv/lib/python3.11/site-packages/uvloop/handles/check.pyx
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVCheck(UVHandle):
|
| 3 |
+
cdef _init(self, Loop loop, Handle h):
|
| 4 |
+
cdef int err
|
| 5 |
+
|
| 6 |
+
self._start_init(loop)
|
| 7 |
+
|
| 8 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_check_t))
|
| 9 |
+
if self._handle is NULL:
|
| 10 |
+
self._abort_init()
|
| 11 |
+
raise MemoryError()
|
| 12 |
+
|
| 13 |
+
err = uv.uv_check_init(self._loop.uvloop, <uv.uv_check_t*>self._handle)
|
| 14 |
+
if err < 0:
|
| 15 |
+
self._abort_init()
|
| 16 |
+
raise convert_error(err)
|
| 17 |
+
|
| 18 |
+
self._finish_init()
|
| 19 |
+
|
| 20 |
+
self.h = h
|
| 21 |
+
self.running = 0
|
| 22 |
+
|
| 23 |
+
cdef inline stop(self):
|
| 24 |
+
cdef int err
|
| 25 |
+
|
| 26 |
+
if not self._is_alive():
|
| 27 |
+
self.running = 0
|
| 28 |
+
return
|
| 29 |
+
|
| 30 |
+
if self.running == 1:
|
| 31 |
+
err = uv.uv_check_stop(<uv.uv_check_t*>self._handle)
|
| 32 |
+
self.running = 0
|
| 33 |
+
if err < 0:
|
| 34 |
+
exc = convert_error(err)
|
| 35 |
+
self._fatal_error(exc, True)
|
| 36 |
+
return
|
| 37 |
+
|
| 38 |
+
cdef inline start(self):
|
| 39 |
+
cdef int err
|
| 40 |
+
|
| 41 |
+
self._ensure_alive()
|
| 42 |
+
|
| 43 |
+
if self.running == 0:
|
| 44 |
+
err = uv.uv_check_start(<uv.uv_check_t*>self._handle,
|
| 45 |
+
cb_check_callback)
|
| 46 |
+
if err < 0:
|
| 47 |
+
exc = convert_error(err)
|
| 48 |
+
self._fatal_error(exc, True)
|
| 49 |
+
return
|
| 50 |
+
self.running = 1
|
| 51 |
+
|
| 52 |
+
@staticmethod
|
| 53 |
+
cdef UVCheck new(Loop loop, Handle h):
|
| 54 |
+
cdef UVCheck handle
|
| 55 |
+
handle = UVCheck.__new__(UVCheck)
|
| 56 |
+
handle._init(loop, h)
|
| 57 |
+
return handle
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
cdef void cb_check_callback(
|
| 61 |
+
uv.uv_check_t* handle,
|
| 62 |
+
) noexcept with gil:
|
| 63 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle, "UVCheck callback") == 0:
|
| 64 |
+
return
|
| 65 |
+
|
| 66 |
+
cdef:
|
| 67 |
+
UVCheck check = <UVCheck> handle.data
|
| 68 |
+
Handle h = check.h
|
| 69 |
+
try:
|
| 70 |
+
h._run()
|
| 71 |
+
except BaseException as ex:
|
| 72 |
+
check._error(ex, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pxd
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVFSEvent(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
object callback
|
| 4 |
+
bint running
|
| 5 |
+
|
| 6 |
+
cdef _init(self, Loop loop, object callback, object context)
|
| 7 |
+
cdef _close(self)
|
| 8 |
+
cdef start(self, char* path, int flags)
|
| 9 |
+
cdef stop(self)
|
| 10 |
+
|
| 11 |
+
@staticmethod
|
| 12 |
+
cdef UVFSEvent new(Loop loop, object callback, object context)
|
.venv/lib/python3.11/site-packages/uvloop/handles/fsevent.pyx
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import enum
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class FileSystemEvent(enum.IntEnum):
|
| 5 |
+
RENAME = uv.UV_RENAME
|
| 6 |
+
CHANGE = uv.UV_CHANGE
|
| 7 |
+
RENAME_CHANGE = RENAME | CHANGE
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@cython.no_gc_clear
|
| 11 |
+
cdef class UVFSEvent(UVHandle):
|
| 12 |
+
cdef _init(self, Loop loop, object callback, object context):
|
| 13 |
+
cdef int err
|
| 14 |
+
|
| 15 |
+
self._start_init(loop)
|
| 16 |
+
|
| 17 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(
|
| 18 |
+
sizeof(uv.uv_fs_event_t)
|
| 19 |
+
)
|
| 20 |
+
if self._handle is NULL:
|
| 21 |
+
self._abort_init()
|
| 22 |
+
raise MemoryError()
|
| 23 |
+
|
| 24 |
+
err = uv.uv_fs_event_init(
|
| 25 |
+
self._loop.uvloop, <uv.uv_fs_event_t*>self._handle
|
| 26 |
+
)
|
| 27 |
+
if err < 0:
|
| 28 |
+
self._abort_init()
|
| 29 |
+
raise convert_error(err)
|
| 30 |
+
|
| 31 |
+
self._finish_init()
|
| 32 |
+
|
| 33 |
+
self.running = 0
|
| 34 |
+
self.callback = callback
|
| 35 |
+
if context is None:
|
| 36 |
+
context = Context_CopyCurrent()
|
| 37 |
+
self.context = context
|
| 38 |
+
|
| 39 |
+
cdef start(self, char* path, int flags):
|
| 40 |
+
cdef int err
|
| 41 |
+
|
| 42 |
+
self._ensure_alive()
|
| 43 |
+
|
| 44 |
+
if self.running == 0:
|
| 45 |
+
err = uv.uv_fs_event_start(
|
| 46 |
+
<uv.uv_fs_event_t*>self._handle,
|
| 47 |
+
__uvfsevent_callback,
|
| 48 |
+
path,
|
| 49 |
+
flags,
|
| 50 |
+
)
|
| 51 |
+
if err < 0:
|
| 52 |
+
exc = convert_error(err)
|
| 53 |
+
self._fatal_error(exc, True)
|
| 54 |
+
return
|
| 55 |
+
self.running = 1
|
| 56 |
+
|
| 57 |
+
cdef stop(self):
|
| 58 |
+
cdef int err
|
| 59 |
+
|
| 60 |
+
if not self._is_alive():
|
| 61 |
+
self.running = 0
|
| 62 |
+
return
|
| 63 |
+
|
| 64 |
+
if self.running == 1:
|
| 65 |
+
err = uv.uv_fs_event_stop(<uv.uv_fs_event_t*>self._handle)
|
| 66 |
+
self.running = 0
|
| 67 |
+
if err < 0:
|
| 68 |
+
exc = convert_error(err)
|
| 69 |
+
self._fatal_error(exc, True)
|
| 70 |
+
return
|
| 71 |
+
|
| 72 |
+
cdef _close(self):
|
| 73 |
+
try:
|
| 74 |
+
self.stop()
|
| 75 |
+
finally:
|
| 76 |
+
UVHandle._close(<UVHandle>self)
|
| 77 |
+
|
| 78 |
+
def cancel(self):
|
| 79 |
+
self._close()
|
| 80 |
+
|
| 81 |
+
def cancelled(self):
|
| 82 |
+
return self.running == 0
|
| 83 |
+
|
| 84 |
+
@staticmethod
|
| 85 |
+
cdef UVFSEvent new(Loop loop, object callback, object context):
|
| 86 |
+
cdef UVFSEvent handle
|
| 87 |
+
handle = UVFSEvent.__new__(UVFSEvent)
|
| 88 |
+
handle._init(loop, callback, context)
|
| 89 |
+
return handle
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
cdef void __uvfsevent_callback(
|
| 93 |
+
uv.uv_fs_event_t* handle,
|
| 94 |
+
const char *filename,
|
| 95 |
+
int events,
|
| 96 |
+
int status,
|
| 97 |
+
) noexcept with gil:
|
| 98 |
+
if __ensure_handle_data(
|
| 99 |
+
<uv.uv_handle_t*>handle, "UVFSEvent callback"
|
| 100 |
+
) == 0:
|
| 101 |
+
return
|
| 102 |
+
|
| 103 |
+
cdef:
|
| 104 |
+
UVFSEvent fs_event = <UVFSEvent> handle.data
|
| 105 |
+
Handle h
|
| 106 |
+
|
| 107 |
+
try:
|
| 108 |
+
h = new_Handle(
|
| 109 |
+
fs_event._loop,
|
| 110 |
+
fs_event.callback,
|
| 111 |
+
(filename, FileSystemEvent(events)),
|
| 112 |
+
fs_event.context,
|
| 113 |
+
)
|
| 114 |
+
h._run()
|
| 115 |
+
except BaseException as ex:
|
| 116 |
+
fs_event._error(ex, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/handle.pxd
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVHandle:
|
| 2 |
+
cdef:
|
| 3 |
+
uv.uv_handle_t *_handle
|
| 4 |
+
Loop _loop
|
| 5 |
+
readonly _source_traceback
|
| 6 |
+
bint _closed
|
| 7 |
+
bint _inited
|
| 8 |
+
object context
|
| 9 |
+
|
| 10 |
+
# Added to enable current UDPTransport implementation,
|
| 11 |
+
# which doesn't use libuv handles.
|
| 12 |
+
bint _has_handle
|
| 13 |
+
|
| 14 |
+
# All "inline" methods are final
|
| 15 |
+
|
| 16 |
+
cdef inline _start_init(self, Loop loop)
|
| 17 |
+
cdef inline _abort_init(self)
|
| 18 |
+
cdef inline _finish_init(self)
|
| 19 |
+
|
| 20 |
+
cdef inline bint _is_alive(self)
|
| 21 |
+
cdef inline _ensure_alive(self)
|
| 22 |
+
|
| 23 |
+
cdef _error(self, exc, throw)
|
| 24 |
+
cdef _fatal_error(self, exc, throw, reason=?)
|
| 25 |
+
|
| 26 |
+
cdef _warn_unclosed(self)
|
| 27 |
+
|
| 28 |
+
cdef _free(self)
|
| 29 |
+
cdef _close(self)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
cdef class UVSocketHandle(UVHandle):
|
| 33 |
+
cdef:
|
| 34 |
+
# Points to a Python file-object that should be closed
|
| 35 |
+
# when the transport is closing. Used by pipes. This
|
| 36 |
+
# should probably be refactored somehow.
|
| 37 |
+
object _fileobj
|
| 38 |
+
object __cached_socket
|
| 39 |
+
|
| 40 |
+
# All "inline" methods are final
|
| 41 |
+
|
| 42 |
+
cdef _fileno(self)
|
| 43 |
+
|
| 44 |
+
cdef _new_socket(self)
|
| 45 |
+
cdef inline _get_socket(self)
|
| 46 |
+
cdef inline _attach_fileobj(self, object file)
|
| 47 |
+
|
| 48 |
+
cdef _open(self, int sockfd)
|
.venv/lib/python3.11/site-packages/uvloop/handles/handle.pyx
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVHandle:
|
| 2 |
+
"""A base class for all libuv handles.
|
| 3 |
+
|
| 4 |
+
Automatically manages memory deallocation and closing.
|
| 5 |
+
|
| 6 |
+
Important:
|
| 7 |
+
|
| 8 |
+
1. call "_ensure_alive()" before calling any libuv functions on
|
| 9 |
+
your handles.
|
| 10 |
+
|
| 11 |
+
2. call "__ensure_handle_data" in *all* libuv handle callbacks.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
def __cinit__(self):
|
| 15 |
+
self._closed = 0
|
| 16 |
+
self._inited = 0
|
| 17 |
+
self._has_handle = 1
|
| 18 |
+
self._handle = NULL
|
| 19 |
+
self._loop = None
|
| 20 |
+
self._source_traceback = None
|
| 21 |
+
|
| 22 |
+
def __init__(self):
|
| 23 |
+
raise TypeError(
|
| 24 |
+
'{} is not supposed to be instantiated from Python'.format(
|
| 25 |
+
self.__class__.__name__))
|
| 26 |
+
|
| 27 |
+
def __dealloc__(self):
|
| 28 |
+
if UVLOOP_DEBUG:
|
| 29 |
+
if self._loop is not None:
|
| 30 |
+
if self._inited:
|
| 31 |
+
self._loop._debug_handles_current.subtract([
|
| 32 |
+
self.__class__.__name__])
|
| 33 |
+
else:
|
| 34 |
+
# No "@cython.no_gc_clear" decorator on this UVHandle
|
| 35 |
+
raise RuntimeError(
|
| 36 |
+
'{} without @no_gc_clear; loop was set to None by GC'
|
| 37 |
+
.format(self.__class__.__name__))
|
| 38 |
+
|
| 39 |
+
if self._handle is NULL:
|
| 40 |
+
return
|
| 41 |
+
|
| 42 |
+
# -> When we're at this point, something is wrong <-
|
| 43 |
+
|
| 44 |
+
if self._handle.loop is NULL:
|
| 45 |
+
# The handle wasn't initialized with "uv_{handle}_init"
|
| 46 |
+
self._closed = 1
|
| 47 |
+
self._free()
|
| 48 |
+
raise RuntimeError(
|
| 49 |
+
'{} is open in __dealloc__ with loop set to NULL'
|
| 50 |
+
.format(self.__class__.__name__))
|
| 51 |
+
|
| 52 |
+
if self._closed:
|
| 53 |
+
# So _handle is not NULL and self._closed == 1?
|
| 54 |
+
raise RuntimeError(
|
| 55 |
+
'{}.__dealloc__: _handle is NULL, _closed == 1'.format(
|
| 56 |
+
self.__class__.__name__))
|
| 57 |
+
|
| 58 |
+
# The handle is dealloced while open. Let's try to close it.
|
| 59 |
+
# Situations when this is possible include unhandled exceptions,
|
| 60 |
+
# errors during Handle.__cinit__/__init__ etc.
|
| 61 |
+
if self._inited:
|
| 62 |
+
self._handle.data = NULL
|
| 63 |
+
uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors
|
| 64 |
+
self._handle = NULL
|
| 65 |
+
self._warn_unclosed()
|
| 66 |
+
else:
|
| 67 |
+
# The handle was allocated, but not initialized
|
| 68 |
+
self._closed = 1
|
| 69 |
+
self._free()
|
| 70 |
+
|
| 71 |
+
cdef _free(self):
|
| 72 |
+
if self._handle == NULL:
|
| 73 |
+
return
|
| 74 |
+
|
| 75 |
+
if UVLOOP_DEBUG and self._inited:
|
| 76 |
+
self._loop._debug_uv_handles_freed += 1
|
| 77 |
+
|
| 78 |
+
PyMem_RawFree(self._handle)
|
| 79 |
+
self._handle = NULL
|
| 80 |
+
|
| 81 |
+
cdef _warn_unclosed(self):
|
| 82 |
+
if self._source_traceback is not None:
|
| 83 |
+
try:
|
| 84 |
+
tb = ''.join(tb_format_list(self._source_traceback))
|
| 85 |
+
tb = 'object created at (most recent call last):\n{}'.format(
|
| 86 |
+
tb.rstrip())
|
| 87 |
+
except Exception as ex:
|
| 88 |
+
msg = (
|
| 89 |
+
'unclosed resource {!r}; could not serialize '
|
| 90 |
+
'debug traceback: {}: {}'
|
| 91 |
+
).format(self, type(ex).__name__, ex)
|
| 92 |
+
else:
|
| 93 |
+
msg = 'unclosed resource {!r}; {}'.format(self, tb)
|
| 94 |
+
else:
|
| 95 |
+
msg = 'unclosed resource {!r}'.format(self)
|
| 96 |
+
warnings_warn(msg, ResourceWarning)
|
| 97 |
+
|
| 98 |
+
cdef inline _abort_init(self):
|
| 99 |
+
if self._handle is not NULL:
|
| 100 |
+
self._free()
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
if UVLOOP_DEBUG:
|
| 104 |
+
name = self.__class__.__name__
|
| 105 |
+
if self._inited:
|
| 106 |
+
raise RuntimeError(
|
| 107 |
+
'_abort_init: {}._inited is set'.format(name))
|
| 108 |
+
if self._closed:
|
| 109 |
+
raise RuntimeError(
|
| 110 |
+
'_abort_init: {}._closed is set'.format(name))
|
| 111 |
+
finally:
|
| 112 |
+
self._closed = 1
|
| 113 |
+
|
| 114 |
+
cdef inline _finish_init(self):
|
| 115 |
+
self._inited = 1
|
| 116 |
+
if self._has_handle == 1:
|
| 117 |
+
self._handle.data = <void*>self
|
| 118 |
+
if self._loop._debug:
|
| 119 |
+
self._source_traceback = extract_stack()
|
| 120 |
+
if UVLOOP_DEBUG:
|
| 121 |
+
cls_name = self.__class__.__name__
|
| 122 |
+
self._loop._debug_uv_handles_total += 1
|
| 123 |
+
self._loop._debug_handles_total.update([cls_name])
|
| 124 |
+
self._loop._debug_handles_current.update([cls_name])
|
| 125 |
+
|
| 126 |
+
cdef inline _start_init(self, Loop loop):
|
| 127 |
+
if UVLOOP_DEBUG:
|
| 128 |
+
if self._loop is not None:
|
| 129 |
+
raise RuntimeError(
|
| 130 |
+
'{}._start_init can only be called once'.format(
|
| 131 |
+
self.__class__.__name__))
|
| 132 |
+
|
| 133 |
+
self._loop = loop
|
| 134 |
+
|
| 135 |
+
cdef inline bint _is_alive(self):
|
| 136 |
+
cdef bint res
|
| 137 |
+
res = self._closed != 1 and self._inited == 1
|
| 138 |
+
if UVLOOP_DEBUG:
|
| 139 |
+
if res and self._has_handle == 1:
|
| 140 |
+
name = self.__class__.__name__
|
| 141 |
+
if self._handle is NULL:
|
| 142 |
+
raise RuntimeError(
|
| 143 |
+
'{} is alive, but _handle is NULL'.format(name))
|
| 144 |
+
if self._loop is None:
|
| 145 |
+
raise RuntimeError(
|
| 146 |
+
'{} is alive, but _loop is None'.format(name))
|
| 147 |
+
if self._handle.loop is not self._loop.uvloop:
|
| 148 |
+
raise RuntimeError(
|
| 149 |
+
'{} is alive, but _handle.loop is not '
|
| 150 |
+
'initialized'.format(name))
|
| 151 |
+
if self._handle.data is not <void*>self:
|
| 152 |
+
raise RuntimeError(
|
| 153 |
+
'{} is alive, but _handle.data is not '
|
| 154 |
+
'initialized'.format(name))
|
| 155 |
+
return res
|
| 156 |
+
|
| 157 |
+
cdef inline _ensure_alive(self):
|
| 158 |
+
if not self._is_alive():
|
| 159 |
+
raise RuntimeError(
|
| 160 |
+
'unable to perform operation on {!r}; '
|
| 161 |
+
'the handler is closed'.format(self))
|
| 162 |
+
|
| 163 |
+
cdef _fatal_error(self, exc, throw, reason=None):
|
| 164 |
+
# Fatal error means an error that was returned by the
|
| 165 |
+
# underlying libuv handle function. We usually can't
|
| 166 |
+
# recover from that, hence we just close the handle.
|
| 167 |
+
self._close()
|
| 168 |
+
|
| 169 |
+
if throw or self._loop is None:
|
| 170 |
+
raise exc
|
| 171 |
+
else:
|
| 172 |
+
self._loop._handle_exception(exc)
|
| 173 |
+
|
| 174 |
+
cdef _error(self, exc, throw):
|
| 175 |
+
# A non-fatal error is usually an error that was caught
|
| 176 |
+
# by the handler, but was originated in the client code
|
| 177 |
+
# (not in libuv). In this case we either want to simply
|
| 178 |
+
# raise or log it.
|
| 179 |
+
if throw or self._loop is None:
|
| 180 |
+
raise exc
|
| 181 |
+
else:
|
| 182 |
+
self._loop._handle_exception(exc)
|
| 183 |
+
|
| 184 |
+
cdef _close(self):
|
| 185 |
+
if self._closed == 1:
|
| 186 |
+
return
|
| 187 |
+
|
| 188 |
+
self._closed = 1
|
| 189 |
+
|
| 190 |
+
if self._handle is NULL:
|
| 191 |
+
return
|
| 192 |
+
|
| 193 |
+
if UVLOOP_DEBUG:
|
| 194 |
+
if self._handle.data is NULL:
|
| 195 |
+
raise RuntimeError(
|
| 196 |
+
'{}._close: _handle.data is NULL'.format(
|
| 197 |
+
self.__class__.__name__))
|
| 198 |
+
|
| 199 |
+
if <object>self._handle.data is not self:
|
| 200 |
+
raise RuntimeError(
|
| 201 |
+
'{}._close: _handle.data is not UVHandle/self'.format(
|
| 202 |
+
self.__class__.__name__))
|
| 203 |
+
|
| 204 |
+
if uv.uv_is_closing(self._handle):
|
| 205 |
+
raise RuntimeError(
|
| 206 |
+
'{}._close: uv_is_closing() is true'.format(
|
| 207 |
+
self.__class__.__name__))
|
| 208 |
+
|
| 209 |
+
# We want the handle wrapper (UVHandle) to stay alive until
|
| 210 |
+
# the closing callback fires.
|
| 211 |
+
Py_INCREF(self)
|
| 212 |
+
uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors
|
| 213 |
+
|
| 214 |
+
def __repr__(self):
|
| 215 |
+
return '<{} closed={} {:#x}>'.format(
|
| 216 |
+
self.__class__.__name__,
|
| 217 |
+
self._closed,
|
| 218 |
+
id(self))
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
cdef class UVSocketHandle(UVHandle):
|
| 222 |
+
|
| 223 |
+
def __cinit__(self):
|
| 224 |
+
self._fileobj = None
|
| 225 |
+
self.__cached_socket = None
|
| 226 |
+
|
| 227 |
+
cdef _fileno(self):
|
| 228 |
+
cdef:
|
| 229 |
+
int fd
|
| 230 |
+
int err
|
| 231 |
+
|
| 232 |
+
self._ensure_alive()
|
| 233 |
+
err = uv.uv_fileno(self._handle, <uv.uv_os_fd_t*>&fd)
|
| 234 |
+
if err < 0:
|
| 235 |
+
raise convert_error(err)
|
| 236 |
+
|
| 237 |
+
return fd
|
| 238 |
+
|
| 239 |
+
cdef _new_socket(self):
|
| 240 |
+
raise NotImplementedError
|
| 241 |
+
|
| 242 |
+
cdef inline _get_socket(self):
|
| 243 |
+
if self.__cached_socket is not None:
|
| 244 |
+
return self.__cached_socket
|
| 245 |
+
|
| 246 |
+
if not self._is_alive():
|
| 247 |
+
return None
|
| 248 |
+
|
| 249 |
+
self.__cached_socket = self._new_socket()
|
| 250 |
+
if UVLOOP_DEBUG:
|
| 251 |
+
# We don't "dup" for the "__cached_socket".
|
| 252 |
+
assert self.__cached_socket.fileno() == self._fileno()
|
| 253 |
+
return self.__cached_socket
|
| 254 |
+
|
| 255 |
+
cdef inline _attach_fileobj(self, object file):
|
| 256 |
+
# When we create a TCP/PIPE/etc connection/server based on
|
| 257 |
+
# a Python file object, we need to close the file object when
|
| 258 |
+
# the uv handle is closed.
|
| 259 |
+
socket_inc_io_ref(file)
|
| 260 |
+
self._fileobj = file
|
| 261 |
+
|
| 262 |
+
cdef _close(self):
|
| 263 |
+
if self.__cached_socket is not None:
|
| 264 |
+
(<PseudoSocket>self.__cached_socket)._fd = -1
|
| 265 |
+
|
| 266 |
+
UVHandle._close(self)
|
| 267 |
+
|
| 268 |
+
try:
|
| 269 |
+
# This code will only run for transports created from
|
| 270 |
+
# Python sockets, i.e. with `loop.create_server(sock=sock)` etc.
|
| 271 |
+
if self._fileobj is not None:
|
| 272 |
+
if isinstance(self._fileobj, socket_socket):
|
| 273 |
+
# Detaching the socket object is the ideal solution:
|
| 274 |
+
# * libuv will actually close the FD;
|
| 275 |
+
# * detach() call will reset FD for the Python socket
|
| 276 |
+
# object, which means that it won't be closed 2nd time
|
| 277 |
+
# when the socket object is GCed.
|
| 278 |
+
#
|
| 279 |
+
# No need to call `socket_dec_io_ref()`, as
|
| 280 |
+
# `socket.detach()` ignores `socket._io_refs`.
|
| 281 |
+
self._fileobj.detach()
|
| 282 |
+
else:
|
| 283 |
+
try:
|
| 284 |
+
# `socket.close()` will raise an EBADF because libuv
|
| 285 |
+
# has already closed the underlying FD.
|
| 286 |
+
self._fileobj.close()
|
| 287 |
+
except OSError as ex:
|
| 288 |
+
if ex.errno != errno_EBADF:
|
| 289 |
+
raise
|
| 290 |
+
except Exception as ex:
|
| 291 |
+
self._loop.call_exception_handler({
|
| 292 |
+
'exception': ex,
|
| 293 |
+
'transport': self,
|
| 294 |
+
'message': f'could not close attached file object '
|
| 295 |
+
f'{self._fileobj!r}',
|
| 296 |
+
})
|
| 297 |
+
finally:
|
| 298 |
+
self._fileobj = None
|
| 299 |
+
|
| 300 |
+
cdef _open(self, int sockfd):
|
| 301 |
+
raise NotImplementedError
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
cdef inline bint __ensure_handle_data(uv.uv_handle_t* handle,
|
| 305 |
+
const char* handle_ctx):
|
| 306 |
+
|
| 307 |
+
cdef Loop loop
|
| 308 |
+
|
| 309 |
+
if UVLOOP_DEBUG:
|
| 310 |
+
if handle.loop is NULL:
|
| 311 |
+
raise RuntimeError(
|
| 312 |
+
'handle.loop is NULL in __ensure_handle_data')
|
| 313 |
+
|
| 314 |
+
if handle.loop.data is NULL:
|
| 315 |
+
raise RuntimeError(
|
| 316 |
+
'handle.loop.data is NULL in __ensure_handle_data')
|
| 317 |
+
|
| 318 |
+
if handle.data is NULL:
|
| 319 |
+
loop = <Loop>handle.loop.data
|
| 320 |
+
loop.call_exception_handler({
|
| 321 |
+
'message': '{} called with handle.data == NULL'.format(
|
| 322 |
+
handle_ctx.decode('latin-1'))
|
| 323 |
+
})
|
| 324 |
+
return 0
|
| 325 |
+
|
| 326 |
+
if handle.data is NULL:
|
| 327 |
+
# The underlying UVHandle object was GCed with an open uv_handle_t.
|
| 328 |
+
loop = <Loop>handle.loop.data
|
| 329 |
+
loop.call_exception_handler({
|
| 330 |
+
'message': '{} called after destroying the UVHandle'.format(
|
| 331 |
+
handle_ctx.decode('latin-1'))
|
| 332 |
+
})
|
| 333 |
+
return 0
|
| 334 |
+
|
| 335 |
+
return 1
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
cdef void __uv_close_handle_cb(uv.uv_handle_t* handle) noexcept with gil:
|
| 339 |
+
cdef UVHandle h
|
| 340 |
+
|
| 341 |
+
if handle.data is NULL:
|
| 342 |
+
# The original UVHandle is long dead. Just free the mem of
|
| 343 |
+
# the uv_handle_t* handler.
|
| 344 |
+
|
| 345 |
+
if UVLOOP_DEBUG:
|
| 346 |
+
if handle.loop == NULL or handle.loop.data == NULL:
|
| 347 |
+
raise RuntimeError(
|
| 348 |
+
'__uv_close_handle_cb: handle.loop is invalid')
|
| 349 |
+
(<Loop>handle.loop.data)._debug_uv_handles_freed += 1
|
| 350 |
+
|
| 351 |
+
PyMem_RawFree(handle)
|
| 352 |
+
else:
|
| 353 |
+
h = <UVHandle>handle.data
|
| 354 |
+
try:
|
| 355 |
+
if UVLOOP_DEBUG:
|
| 356 |
+
if not h._has_handle:
|
| 357 |
+
raise RuntimeError(
|
| 358 |
+
'has_handle=0 in __uv_close_handle_cb')
|
| 359 |
+
h._loop._debug_handles_closed.update([
|
| 360 |
+
h.__class__.__name__])
|
| 361 |
+
h._free()
|
| 362 |
+
finally:
|
| 363 |
+
Py_DECREF(h) # Was INCREFed in UVHandle._close
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
cdef void __close_all_handles(Loop loop) noexcept:
|
| 367 |
+
uv.uv_walk(loop.uvloop,
|
| 368 |
+
__uv_walk_close_all_handles_cb,
|
| 369 |
+
<void*>loop) # void
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
cdef void __uv_walk_close_all_handles_cb(
|
| 373 |
+
uv.uv_handle_t* handle,
|
| 374 |
+
void* arg,
|
| 375 |
+
) noexcept with gil:
|
| 376 |
+
|
| 377 |
+
cdef:
|
| 378 |
+
Loop loop = <Loop>arg
|
| 379 |
+
UVHandle h
|
| 380 |
+
|
| 381 |
+
if uv.uv_is_closing(handle):
|
| 382 |
+
# The handle is closed or is closing.
|
| 383 |
+
return
|
| 384 |
+
|
| 385 |
+
if handle.data is NULL:
|
| 386 |
+
# This shouldn't happen. Ever.
|
| 387 |
+
loop.call_exception_handler({
|
| 388 |
+
'message': 'handle.data is NULL in __close_all_handles_cb'
|
| 389 |
+
})
|
| 390 |
+
return
|
| 391 |
+
|
| 392 |
+
h = <UVHandle>handle.data
|
| 393 |
+
if not h._closed:
|
| 394 |
+
h._warn_unclosed()
|
| 395 |
+
h._close()
|
.venv/lib/python3.11/site-packages/uvloop/handles/idle.pxd
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVIdle(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
Handle h
|
| 4 |
+
bint running
|
| 5 |
+
|
| 6 |
+
# All "inline" methods are final
|
| 7 |
+
|
| 8 |
+
cdef _init(self, Loop loop, Handle h)
|
| 9 |
+
|
| 10 |
+
cdef inline stop(self)
|
| 11 |
+
cdef inline start(self)
|
| 12 |
+
|
| 13 |
+
@staticmethod
|
| 14 |
+
cdef UVIdle new(Loop loop, Handle h)
|
.venv/lib/python3.11/site-packages/uvloop/handles/idle.pyx
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVIdle(UVHandle):
|
| 3 |
+
cdef _init(self, Loop loop, Handle h):
|
| 4 |
+
cdef int err
|
| 5 |
+
|
| 6 |
+
self._start_init(loop)
|
| 7 |
+
|
| 8 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_idle_t))
|
| 9 |
+
if self._handle is NULL:
|
| 10 |
+
self._abort_init()
|
| 11 |
+
raise MemoryError()
|
| 12 |
+
|
| 13 |
+
err = uv.uv_idle_init(self._loop.uvloop, <uv.uv_idle_t*>self._handle)
|
| 14 |
+
if err < 0:
|
| 15 |
+
self._abort_init()
|
| 16 |
+
raise convert_error(err)
|
| 17 |
+
|
| 18 |
+
self._finish_init()
|
| 19 |
+
|
| 20 |
+
self.h = h
|
| 21 |
+
self.running = 0
|
| 22 |
+
|
| 23 |
+
cdef inline stop(self):
|
| 24 |
+
cdef int err
|
| 25 |
+
|
| 26 |
+
if not self._is_alive():
|
| 27 |
+
self.running = 0
|
| 28 |
+
return
|
| 29 |
+
|
| 30 |
+
if self.running == 1:
|
| 31 |
+
err = uv.uv_idle_stop(<uv.uv_idle_t*>self._handle)
|
| 32 |
+
self.running = 0
|
| 33 |
+
if err < 0:
|
| 34 |
+
exc = convert_error(err)
|
| 35 |
+
self._fatal_error(exc, True)
|
| 36 |
+
return
|
| 37 |
+
|
| 38 |
+
cdef inline start(self):
|
| 39 |
+
cdef int err
|
| 40 |
+
|
| 41 |
+
self._ensure_alive()
|
| 42 |
+
|
| 43 |
+
if self.running == 0:
|
| 44 |
+
err = uv.uv_idle_start(<uv.uv_idle_t*>self._handle,
|
| 45 |
+
cb_idle_callback)
|
| 46 |
+
if err < 0:
|
| 47 |
+
exc = convert_error(err)
|
| 48 |
+
self._fatal_error(exc, True)
|
| 49 |
+
return
|
| 50 |
+
self.running = 1
|
| 51 |
+
|
| 52 |
+
@staticmethod
|
| 53 |
+
cdef UVIdle new(Loop loop, Handle h):
|
| 54 |
+
cdef UVIdle handle
|
| 55 |
+
handle = UVIdle.__new__(UVIdle)
|
| 56 |
+
handle._init(loop, h)
|
| 57 |
+
return handle
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
cdef void cb_idle_callback(
|
| 61 |
+
uv.uv_idle_t* handle,
|
| 62 |
+
) noexcept with gil:
|
| 63 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle, "UVIdle callback") == 0:
|
| 64 |
+
return
|
| 65 |
+
|
| 66 |
+
cdef:
|
| 67 |
+
UVIdle idle = <UVIdle> handle.data
|
| 68 |
+
Handle h = idle.h
|
| 69 |
+
try:
|
| 70 |
+
h._run()
|
| 71 |
+
except BaseException as ex:
|
| 72 |
+
idle._error(ex, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/pipe.pxd
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UnixServer(UVStreamServer):
|
| 2 |
+
|
| 3 |
+
cdef bind(self, str path)
|
| 4 |
+
|
| 5 |
+
@staticmethod
|
| 6 |
+
cdef UnixServer new(Loop loop, object protocol_factory, Server server,
|
| 7 |
+
object backlog,
|
| 8 |
+
object ssl,
|
| 9 |
+
object ssl_handshake_timeout,
|
| 10 |
+
object ssl_shutdown_timeout)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
cdef class UnixTransport(UVStream):
|
| 14 |
+
|
| 15 |
+
@staticmethod
|
| 16 |
+
cdef UnixTransport new(Loop loop, object protocol, Server server,
|
| 17 |
+
object waiter, object context)
|
| 18 |
+
|
| 19 |
+
cdef connect(self, char* addr)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
cdef class ReadUnixTransport(UVStream):
|
| 23 |
+
|
| 24 |
+
@staticmethod
|
| 25 |
+
cdef ReadUnixTransport new(Loop loop, object protocol, Server server,
|
| 26 |
+
object waiter)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
cdef class WriteUnixTransport(UVStream):
|
| 30 |
+
|
| 31 |
+
@staticmethod
|
| 32 |
+
cdef WriteUnixTransport new(Loop loop, object protocol, Server server,
|
| 33 |
+
object waiter)
|
.venv/lib/python3.11/site-packages/uvloop/handles/pipe.pyx
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef __pipe_init_uv_handle(UVStream handle, Loop loop):
|
| 2 |
+
cdef int err
|
| 3 |
+
|
| 4 |
+
handle._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_pipe_t))
|
| 5 |
+
if handle._handle is NULL:
|
| 6 |
+
handle._abort_init()
|
| 7 |
+
raise MemoryError()
|
| 8 |
+
|
| 9 |
+
# Initialize pipe handle with ipc=0.
|
| 10 |
+
# ipc=1 means that libuv will use recvmsg/sendmsg
|
| 11 |
+
# instead of recv/send.
|
| 12 |
+
err = uv.uv_pipe_init(handle._loop.uvloop,
|
| 13 |
+
<uv.uv_pipe_t*>handle._handle,
|
| 14 |
+
0)
|
| 15 |
+
# UV_HANDLE_READABLE allows calling uv_read_start() on this pipe
|
| 16 |
+
# even if it is O_WRONLY, see also #317, libuv/libuv#2058
|
| 17 |
+
handle._handle.flags |= uv.UV_INTERNAL_HANDLE_READABLE
|
| 18 |
+
if err < 0:
|
| 19 |
+
handle._abort_init()
|
| 20 |
+
raise convert_error(err)
|
| 21 |
+
|
| 22 |
+
handle._finish_init()
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
cdef __pipe_open(UVStream handle, int fd):
|
| 26 |
+
cdef int err
|
| 27 |
+
err = uv.uv_pipe_open(<uv.uv_pipe_t *>handle._handle,
|
| 28 |
+
<uv.uv_os_fd_t>fd)
|
| 29 |
+
if err < 0:
|
| 30 |
+
exc = convert_error(err)
|
| 31 |
+
raise exc
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
cdef __pipe_get_socket(UVSocketHandle handle):
|
| 35 |
+
fileno = handle._fileno()
|
| 36 |
+
return PseudoSocket(uv.AF_UNIX, uv.SOCK_STREAM, 0, fileno)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@cython.no_gc_clear
|
| 40 |
+
cdef class UnixServer(UVStreamServer):
|
| 41 |
+
|
| 42 |
+
@staticmethod
|
| 43 |
+
cdef UnixServer new(Loop loop, object protocol_factory, Server server,
|
| 44 |
+
object backlog,
|
| 45 |
+
object ssl,
|
| 46 |
+
object ssl_handshake_timeout,
|
| 47 |
+
object ssl_shutdown_timeout):
|
| 48 |
+
|
| 49 |
+
cdef UnixServer handle
|
| 50 |
+
handle = UnixServer.__new__(UnixServer)
|
| 51 |
+
handle._init(loop, protocol_factory, server, backlog,
|
| 52 |
+
ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
|
| 53 |
+
__pipe_init_uv_handle(<UVStream>handle, loop)
|
| 54 |
+
return handle
|
| 55 |
+
|
| 56 |
+
cdef _new_socket(self):
|
| 57 |
+
return __pipe_get_socket(<UVSocketHandle>self)
|
| 58 |
+
|
| 59 |
+
cdef _open(self, int sockfd):
|
| 60 |
+
self._ensure_alive()
|
| 61 |
+
__pipe_open(<UVStream>self, sockfd)
|
| 62 |
+
self._mark_as_open()
|
| 63 |
+
|
| 64 |
+
cdef bind(self, str path):
|
| 65 |
+
cdef int err
|
| 66 |
+
self._ensure_alive()
|
| 67 |
+
err = uv.uv_pipe_bind(<uv.uv_pipe_t *>self._handle,
|
| 68 |
+
path.encode())
|
| 69 |
+
if err < 0:
|
| 70 |
+
exc = convert_error(err)
|
| 71 |
+
self._fatal_error(exc, True)
|
| 72 |
+
return
|
| 73 |
+
|
| 74 |
+
self._mark_as_open()
|
| 75 |
+
|
| 76 |
+
cdef UVStream _make_new_transport(self, object protocol, object waiter,
|
| 77 |
+
object context):
|
| 78 |
+
cdef UnixTransport tr
|
| 79 |
+
tr = UnixTransport.new(self._loop, protocol, self._server, waiter,
|
| 80 |
+
context)
|
| 81 |
+
return <UVStream>tr
|
| 82 |
+
|
| 83 |
+
cdef _close(self):
|
| 84 |
+
sock = self._fileobj
|
| 85 |
+
if sock is not None and sock in self._loop._unix_server_sockets:
|
| 86 |
+
path = sock.getsockname()
|
| 87 |
+
else:
|
| 88 |
+
path = None
|
| 89 |
+
|
| 90 |
+
UVStreamServer._close(self)
|
| 91 |
+
|
| 92 |
+
if path is not None:
|
| 93 |
+
prev_ino = self._loop._unix_server_sockets[sock]
|
| 94 |
+
del self._loop._unix_server_sockets[sock]
|
| 95 |
+
try:
|
| 96 |
+
if os_stat(path).st_ino == prev_ino:
|
| 97 |
+
os_unlink(path)
|
| 98 |
+
except FileNotFoundError:
|
| 99 |
+
pass
|
| 100 |
+
except OSError as err:
|
| 101 |
+
aio_logger.error('Unable to clean up listening UNIX socket '
|
| 102 |
+
'%r: %r', path, err)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
@cython.no_gc_clear
|
| 106 |
+
cdef class UnixTransport(UVStream):
|
| 107 |
+
|
| 108 |
+
@staticmethod
|
| 109 |
+
cdef UnixTransport new(Loop loop, object protocol, Server server,
|
| 110 |
+
object waiter, object context):
|
| 111 |
+
|
| 112 |
+
cdef UnixTransport handle
|
| 113 |
+
handle = UnixTransport.__new__(UnixTransport)
|
| 114 |
+
handle._init(loop, protocol, server, waiter, context)
|
| 115 |
+
__pipe_init_uv_handle(<UVStream>handle, loop)
|
| 116 |
+
return handle
|
| 117 |
+
|
| 118 |
+
cdef _new_socket(self):
|
| 119 |
+
return __pipe_get_socket(<UVSocketHandle>self)
|
| 120 |
+
|
| 121 |
+
cdef _open(self, int sockfd):
|
| 122 |
+
__pipe_open(<UVStream>self, sockfd)
|
| 123 |
+
|
| 124 |
+
cdef connect(self, char* addr):
|
| 125 |
+
cdef _PipeConnectRequest req
|
| 126 |
+
req = _PipeConnectRequest(self._loop, self)
|
| 127 |
+
req.connect(addr)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
@cython.no_gc_clear
|
| 131 |
+
cdef class ReadUnixTransport(UVStream):
|
| 132 |
+
|
| 133 |
+
@staticmethod
|
| 134 |
+
cdef ReadUnixTransport new(Loop loop, object protocol, Server server,
|
| 135 |
+
object waiter):
|
| 136 |
+
cdef ReadUnixTransport handle
|
| 137 |
+
handle = ReadUnixTransport.__new__(ReadUnixTransport)
|
| 138 |
+
# This is only used in connect_read_pipe() and subprocess_shell/exec()
|
| 139 |
+
# directly, we could simply copy the current context.
|
| 140 |
+
handle._init(loop, protocol, server, waiter, Context_CopyCurrent())
|
| 141 |
+
__pipe_init_uv_handle(<UVStream>handle, loop)
|
| 142 |
+
return handle
|
| 143 |
+
|
| 144 |
+
cdef _new_socket(self):
|
| 145 |
+
return __pipe_get_socket(<UVSocketHandle>self)
|
| 146 |
+
|
| 147 |
+
cdef _open(self, int sockfd):
|
| 148 |
+
__pipe_open(<UVStream>self, sockfd)
|
| 149 |
+
|
| 150 |
+
def get_write_buffer_limits(self):
|
| 151 |
+
raise NotImplementedError
|
| 152 |
+
|
| 153 |
+
def set_write_buffer_limits(self, high=None, low=None):
|
| 154 |
+
raise NotImplementedError
|
| 155 |
+
|
| 156 |
+
def get_write_buffer_size(self):
|
| 157 |
+
raise NotImplementedError
|
| 158 |
+
|
| 159 |
+
def write(self, data):
|
| 160 |
+
raise NotImplementedError
|
| 161 |
+
|
| 162 |
+
def writelines(self, list_of_data):
|
| 163 |
+
raise NotImplementedError
|
| 164 |
+
|
| 165 |
+
def write_eof(self):
|
| 166 |
+
raise NotImplementedError
|
| 167 |
+
|
| 168 |
+
def can_write_eof(self):
|
| 169 |
+
raise NotImplementedError
|
| 170 |
+
|
| 171 |
+
def abort(self):
|
| 172 |
+
raise NotImplementedError
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
@cython.no_gc_clear
|
| 176 |
+
cdef class WriteUnixTransport(UVStream):
|
| 177 |
+
|
| 178 |
+
@staticmethod
|
| 179 |
+
cdef WriteUnixTransport new(Loop loop, object protocol, Server server,
|
| 180 |
+
object waiter):
|
| 181 |
+
cdef WriteUnixTransport handle
|
| 182 |
+
handle = WriteUnixTransport.__new__(WriteUnixTransport)
|
| 183 |
+
|
| 184 |
+
# We listen for read events on write-end of the pipe. When
|
| 185 |
+
# the read-end is close, the uv_stream_t.read callback will
|
| 186 |
+
# receive an error -- we want to silence that error, and just
|
| 187 |
+
# close the transport.
|
| 188 |
+
handle._close_on_read_error()
|
| 189 |
+
|
| 190 |
+
# This is only used in connect_write_pipe() and subprocess_shell/exec()
|
| 191 |
+
# directly, we could simply copy the current context.
|
| 192 |
+
handle._init(loop, protocol, server, waiter, Context_CopyCurrent())
|
| 193 |
+
__pipe_init_uv_handle(<UVStream>handle, loop)
|
| 194 |
+
return handle
|
| 195 |
+
|
| 196 |
+
cdef _new_socket(self):
|
| 197 |
+
return __pipe_get_socket(<UVSocketHandle>self)
|
| 198 |
+
|
| 199 |
+
cdef _open(self, int sockfd):
|
| 200 |
+
__pipe_open(<UVStream>self, sockfd)
|
| 201 |
+
|
| 202 |
+
def pause_reading(self):
|
| 203 |
+
raise NotImplementedError
|
| 204 |
+
|
| 205 |
+
def resume_reading(self):
|
| 206 |
+
raise NotImplementedError
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
cdef class _PipeConnectRequest(UVRequest):
|
| 210 |
+
cdef:
|
| 211 |
+
UnixTransport transport
|
| 212 |
+
uv.uv_connect_t _req_data
|
| 213 |
+
|
| 214 |
+
def __cinit__(self, loop, transport):
|
| 215 |
+
self.request = <uv.uv_req_t*> &self._req_data
|
| 216 |
+
self.request.data = <void*>self
|
| 217 |
+
self.transport = transport
|
| 218 |
+
|
| 219 |
+
cdef connect(self, char* addr):
|
| 220 |
+
# uv_pipe_connect returns void
|
| 221 |
+
uv.uv_pipe_connect(<uv.uv_connect_t*>self.request,
|
| 222 |
+
<uv.uv_pipe_t*>self.transport._handle,
|
| 223 |
+
addr,
|
| 224 |
+
__pipe_connect_callback)
|
| 225 |
+
|
| 226 |
+
cdef void __pipe_connect_callback(
|
| 227 |
+
uv.uv_connect_t* req,
|
| 228 |
+
int status,
|
| 229 |
+
) noexcept with gil:
|
| 230 |
+
cdef:
|
| 231 |
+
_PipeConnectRequest wrapper
|
| 232 |
+
UnixTransport transport
|
| 233 |
+
|
| 234 |
+
wrapper = <_PipeConnectRequest> req.data
|
| 235 |
+
transport = wrapper.transport
|
| 236 |
+
|
| 237 |
+
if status < 0:
|
| 238 |
+
exc = convert_error(status)
|
| 239 |
+
else:
|
| 240 |
+
exc = None
|
| 241 |
+
|
| 242 |
+
try:
|
| 243 |
+
transport._on_connect(exc)
|
| 244 |
+
except BaseException as ex:
|
| 245 |
+
wrapper.transport._fatal_error(ex, False)
|
| 246 |
+
finally:
|
| 247 |
+
wrapper.on_done()
|
.venv/lib/python3.11/site-packages/uvloop/handles/poll.pxd
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVPoll(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
int fd
|
| 4 |
+
Handle reading_handle
|
| 5 |
+
Handle writing_handle
|
| 6 |
+
|
| 7 |
+
cdef _init(self, Loop loop, int fd)
|
| 8 |
+
cdef _close(self)
|
| 9 |
+
|
| 10 |
+
cdef inline _poll_start(self, int flags)
|
| 11 |
+
cdef inline _poll_stop(self)
|
| 12 |
+
|
| 13 |
+
cdef int is_active(self) noexcept
|
| 14 |
+
|
| 15 |
+
cdef is_reading(self)
|
| 16 |
+
cdef is_writing(self)
|
| 17 |
+
|
| 18 |
+
cdef start_reading(self, Handle callback)
|
| 19 |
+
cdef start_writing(self, Handle callback)
|
| 20 |
+
cdef stop_reading(self)
|
| 21 |
+
cdef stop_writing(self)
|
| 22 |
+
cdef stop(self)
|
| 23 |
+
|
| 24 |
+
@staticmethod
|
| 25 |
+
cdef UVPoll new(Loop loop, int fd)
|
.venv/lib/python3.11/site-packages/uvloop/handles/poll.pyx
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVPoll(UVHandle):
|
| 3 |
+
cdef _init(self, Loop loop, int fd):
|
| 4 |
+
cdef int err
|
| 5 |
+
|
| 6 |
+
self._start_init(loop)
|
| 7 |
+
|
| 8 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_poll_t))
|
| 9 |
+
if self._handle is NULL:
|
| 10 |
+
self._abort_init()
|
| 11 |
+
raise MemoryError()
|
| 12 |
+
|
| 13 |
+
err = uv.uv_poll_init(self._loop.uvloop,
|
| 14 |
+
<uv.uv_poll_t *>self._handle, fd)
|
| 15 |
+
if err < 0:
|
| 16 |
+
self._abort_init()
|
| 17 |
+
raise convert_error(err)
|
| 18 |
+
|
| 19 |
+
self._finish_init()
|
| 20 |
+
|
| 21 |
+
self.fd = fd
|
| 22 |
+
self.reading_handle = None
|
| 23 |
+
self.writing_handle = None
|
| 24 |
+
|
| 25 |
+
@staticmethod
|
| 26 |
+
cdef UVPoll new(Loop loop, int fd):
|
| 27 |
+
cdef UVPoll handle
|
| 28 |
+
handle = UVPoll.__new__(UVPoll)
|
| 29 |
+
handle._init(loop, fd)
|
| 30 |
+
return handle
|
| 31 |
+
|
| 32 |
+
cdef int is_active(self) noexcept:
|
| 33 |
+
return (self.reading_handle is not None or
|
| 34 |
+
self.writing_handle is not None)
|
| 35 |
+
|
| 36 |
+
cdef inline _poll_start(self, int flags):
|
| 37 |
+
cdef int err
|
| 38 |
+
|
| 39 |
+
self._ensure_alive()
|
| 40 |
+
|
| 41 |
+
err = uv.uv_poll_start(
|
| 42 |
+
<uv.uv_poll_t*>self._handle,
|
| 43 |
+
flags,
|
| 44 |
+
__on_uvpoll_event)
|
| 45 |
+
|
| 46 |
+
if err < 0:
|
| 47 |
+
exc = convert_error(err)
|
| 48 |
+
self._fatal_error(exc, True)
|
| 49 |
+
return
|
| 50 |
+
|
| 51 |
+
cdef inline _poll_stop(self):
|
| 52 |
+
cdef int err
|
| 53 |
+
|
| 54 |
+
if not self._is_alive():
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
err = uv.uv_poll_stop(<uv.uv_poll_t*>self._handle)
|
| 58 |
+
if err < 0:
|
| 59 |
+
exc = convert_error(err)
|
| 60 |
+
self._fatal_error(exc, True)
|
| 61 |
+
return
|
| 62 |
+
|
| 63 |
+
cdef:
|
| 64 |
+
int backend_id
|
| 65 |
+
system.epoll_event dummy_event
|
| 66 |
+
|
| 67 |
+
if system.PLATFORM_IS_LINUX:
|
| 68 |
+
# libuv doesn't remove the FD from epoll immediately
|
| 69 |
+
# after uv_poll_stop or uv_poll_close, causing hard
|
| 70 |
+
# to debug issue with dup-ed file descriptors causing
|
| 71 |
+
# CPU burn in epoll/epoll_ctl:
|
| 72 |
+
# https://github.com/MagicStack/uvloop/issues/61
|
| 73 |
+
#
|
| 74 |
+
# It's safe though to manually call epoll_ctl here,
|
| 75 |
+
# after calling uv_poll_stop.
|
| 76 |
+
|
| 77 |
+
backend_id = uv.uv_backend_fd(self._loop.uvloop)
|
| 78 |
+
if backend_id != -1:
|
| 79 |
+
memset(&dummy_event, 0, sizeof(dummy_event))
|
| 80 |
+
system.epoll_ctl(
|
| 81 |
+
backend_id,
|
| 82 |
+
system.EPOLL_CTL_DEL,
|
| 83 |
+
self.fd,
|
| 84 |
+
&dummy_event) # ignore errors
|
| 85 |
+
|
| 86 |
+
cdef is_reading(self):
|
| 87 |
+
return self._is_alive() and self.reading_handle is not None
|
| 88 |
+
|
| 89 |
+
cdef is_writing(self):
|
| 90 |
+
return self._is_alive() and self.writing_handle is not None
|
| 91 |
+
|
| 92 |
+
cdef start_reading(self, Handle callback):
|
| 93 |
+
cdef:
|
| 94 |
+
int mask = 0
|
| 95 |
+
|
| 96 |
+
if self.reading_handle is None:
|
| 97 |
+
# not reading right now, setup the handle
|
| 98 |
+
|
| 99 |
+
mask = uv.UV_READABLE
|
| 100 |
+
if self.writing_handle is not None:
|
| 101 |
+
# are we writing right now?
|
| 102 |
+
mask |= uv.UV_WRITABLE
|
| 103 |
+
|
| 104 |
+
self._poll_start(mask)
|
| 105 |
+
else:
|
| 106 |
+
self.reading_handle._cancel()
|
| 107 |
+
|
| 108 |
+
self.reading_handle = callback
|
| 109 |
+
|
| 110 |
+
cdef start_writing(self, Handle callback):
|
| 111 |
+
cdef:
|
| 112 |
+
int mask = 0
|
| 113 |
+
|
| 114 |
+
if self.writing_handle is None:
|
| 115 |
+
# not writing right now, setup the handle
|
| 116 |
+
|
| 117 |
+
mask = uv.UV_WRITABLE
|
| 118 |
+
if self.reading_handle is not None:
|
| 119 |
+
# are we reading right now?
|
| 120 |
+
mask |= uv.UV_READABLE
|
| 121 |
+
|
| 122 |
+
self._poll_start(mask)
|
| 123 |
+
else:
|
| 124 |
+
self.writing_handle._cancel()
|
| 125 |
+
|
| 126 |
+
self.writing_handle = callback
|
| 127 |
+
|
| 128 |
+
cdef stop_reading(self):
|
| 129 |
+
if self.reading_handle is None:
|
| 130 |
+
return False
|
| 131 |
+
|
| 132 |
+
self.reading_handle._cancel()
|
| 133 |
+
self.reading_handle = None
|
| 134 |
+
|
| 135 |
+
if self.writing_handle is None:
|
| 136 |
+
self.stop()
|
| 137 |
+
else:
|
| 138 |
+
self._poll_start(uv.UV_WRITABLE)
|
| 139 |
+
|
| 140 |
+
return True
|
| 141 |
+
|
| 142 |
+
cdef stop_writing(self):
|
| 143 |
+
if self.writing_handle is None:
|
| 144 |
+
return False
|
| 145 |
+
|
| 146 |
+
self.writing_handle._cancel()
|
| 147 |
+
self.writing_handle = None
|
| 148 |
+
|
| 149 |
+
if self.reading_handle is None:
|
| 150 |
+
self.stop()
|
| 151 |
+
else:
|
| 152 |
+
self._poll_start(uv.UV_READABLE)
|
| 153 |
+
|
| 154 |
+
return True
|
| 155 |
+
|
| 156 |
+
cdef stop(self):
|
| 157 |
+
if self.reading_handle is not None:
|
| 158 |
+
self.reading_handle._cancel()
|
| 159 |
+
self.reading_handle = None
|
| 160 |
+
|
| 161 |
+
if self.writing_handle is not None:
|
| 162 |
+
self.writing_handle._cancel()
|
| 163 |
+
self.writing_handle = None
|
| 164 |
+
|
| 165 |
+
self._poll_stop()
|
| 166 |
+
|
| 167 |
+
cdef _close(self):
|
| 168 |
+
if self.is_active():
|
| 169 |
+
self.stop()
|
| 170 |
+
|
| 171 |
+
UVHandle._close(<UVHandle>self)
|
| 172 |
+
|
| 173 |
+
cdef _fatal_error(self, exc, throw, reason=None):
|
| 174 |
+
try:
|
| 175 |
+
if self.reading_handle is not None:
|
| 176 |
+
try:
|
| 177 |
+
self.reading_handle._run()
|
| 178 |
+
except BaseException as ex:
|
| 179 |
+
self._loop._handle_exception(ex)
|
| 180 |
+
self.reading_handle = None
|
| 181 |
+
|
| 182 |
+
if self.writing_handle is not None:
|
| 183 |
+
try:
|
| 184 |
+
self.writing_handle._run()
|
| 185 |
+
except BaseException as ex:
|
| 186 |
+
self._loop._handle_exception(ex)
|
| 187 |
+
self.writing_handle = None
|
| 188 |
+
|
| 189 |
+
finally:
|
| 190 |
+
self._close()
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
cdef void __on_uvpoll_event(
|
| 194 |
+
uv.uv_poll_t* handle,
|
| 195 |
+
int status,
|
| 196 |
+
int events,
|
| 197 |
+
) noexcept with gil:
|
| 198 |
+
|
| 199 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle, "UVPoll callback") == 0:
|
| 200 |
+
return
|
| 201 |
+
|
| 202 |
+
cdef:
|
| 203 |
+
UVPoll poll = <UVPoll> handle.data
|
| 204 |
+
|
| 205 |
+
if status < 0:
|
| 206 |
+
exc = convert_error(status)
|
| 207 |
+
poll._fatal_error(exc, False)
|
| 208 |
+
return
|
| 209 |
+
|
| 210 |
+
if ((events & (uv.UV_READABLE | uv.UV_DISCONNECT)) and
|
| 211 |
+
poll.reading_handle is not None):
|
| 212 |
+
|
| 213 |
+
try:
|
| 214 |
+
if UVLOOP_DEBUG:
|
| 215 |
+
poll._loop._poll_read_events_total += 1
|
| 216 |
+
poll.reading_handle._run()
|
| 217 |
+
except BaseException as ex:
|
| 218 |
+
if UVLOOP_DEBUG:
|
| 219 |
+
poll._loop._poll_read_cb_errors_total += 1
|
| 220 |
+
poll._error(ex, False)
|
| 221 |
+
# continue code execution
|
| 222 |
+
|
| 223 |
+
if ((events & (uv.UV_WRITABLE | uv.UV_DISCONNECT)) and
|
| 224 |
+
poll.writing_handle is not None):
|
| 225 |
+
|
| 226 |
+
try:
|
| 227 |
+
if UVLOOP_DEBUG:
|
| 228 |
+
poll._loop._poll_write_events_total += 1
|
| 229 |
+
poll.writing_handle._run()
|
| 230 |
+
except BaseException as ex:
|
| 231 |
+
if UVLOOP_DEBUG:
|
| 232 |
+
poll._loop._poll_write_cb_errors_total += 1
|
| 233 |
+
poll._error(ex, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/process.pxd
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVProcess(UVHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
object _returncode
|
| 4 |
+
object _pid
|
| 5 |
+
|
| 6 |
+
object _errpipe_read
|
| 7 |
+
object _errpipe_write
|
| 8 |
+
object _preexec_fn
|
| 9 |
+
bint _restore_signals
|
| 10 |
+
|
| 11 |
+
list _fds_to_close
|
| 12 |
+
|
| 13 |
+
# Attributes used to compose uv_process_options_t:
|
| 14 |
+
uv.uv_process_options_t options
|
| 15 |
+
uv.uv_stdio_container_t[3] iocnt
|
| 16 |
+
list __env
|
| 17 |
+
char **uv_opt_env
|
| 18 |
+
list __args
|
| 19 |
+
char **uv_opt_args
|
| 20 |
+
char *uv_opt_file
|
| 21 |
+
bytes __cwd
|
| 22 |
+
|
| 23 |
+
cdef _close_process_handle(self)
|
| 24 |
+
|
| 25 |
+
cdef _init(self, Loop loop, list args, dict env, cwd,
|
| 26 |
+
start_new_session,
|
| 27 |
+
_stdin, _stdout, _stderr, pass_fds,
|
| 28 |
+
debug_flags, preexec_fn, restore_signals)
|
| 29 |
+
|
| 30 |
+
cdef _after_fork(self)
|
| 31 |
+
|
| 32 |
+
cdef char** __to_cstring_array(self, list arr)
|
| 33 |
+
cdef _init_args(self, list args)
|
| 34 |
+
cdef _init_env(self, dict env)
|
| 35 |
+
cdef _init_files(self, _stdin, _stdout, _stderr)
|
| 36 |
+
cdef _init_options(self, list args, dict env, cwd, start_new_session,
|
| 37 |
+
_stdin, _stdout, _stderr, bint force_fork)
|
| 38 |
+
|
| 39 |
+
cdef _close_after_spawn(self, int fd)
|
| 40 |
+
|
| 41 |
+
cdef _on_exit(self, int64_t exit_status, int term_signal)
|
| 42 |
+
cdef _kill(self, int signum)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
cdef class UVProcessTransport(UVProcess):
|
| 46 |
+
cdef:
|
| 47 |
+
list _exit_waiters
|
| 48 |
+
list _init_futs
|
| 49 |
+
bint _stdio_ready
|
| 50 |
+
list _pending_calls
|
| 51 |
+
object _protocol
|
| 52 |
+
bint _finished
|
| 53 |
+
|
| 54 |
+
WriteUnixTransport _stdin
|
| 55 |
+
ReadUnixTransport _stdout
|
| 56 |
+
ReadUnixTransport _stderr
|
| 57 |
+
|
| 58 |
+
object stdin_proto
|
| 59 |
+
object stdout_proto
|
| 60 |
+
object stderr_proto
|
| 61 |
+
|
| 62 |
+
cdef _file_redirect_stdio(self, int fd)
|
| 63 |
+
cdef _file_devnull(self)
|
| 64 |
+
cdef _file_inpipe(self)
|
| 65 |
+
cdef _file_outpipe(self)
|
| 66 |
+
|
| 67 |
+
cdef _check_proc(self)
|
| 68 |
+
cdef _pipe_connection_lost(self, int fd, exc)
|
| 69 |
+
cdef _pipe_data_received(self, int fd, data)
|
| 70 |
+
|
| 71 |
+
cdef _call_connection_made(self, waiter)
|
| 72 |
+
cdef _try_finish(self)
|
| 73 |
+
|
| 74 |
+
@staticmethod
|
| 75 |
+
cdef UVProcessTransport new(Loop loop, protocol, args, env, cwd,
|
| 76 |
+
start_new_session,
|
| 77 |
+
_stdin, _stdout, _stderr, pass_fds,
|
| 78 |
+
waiter,
|
| 79 |
+
debug_flags,
|
| 80 |
+
preexec_fn, restore_signals)
|
.venv/lib/python3.11/site-packages/uvloop/handles/process.pyx
ADDED
|
@@ -0,0 +1,792 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVProcess(UVHandle):
|
| 3 |
+
"""Abstract class; wrapper over uv_process_t handle."""
|
| 4 |
+
|
| 5 |
+
def __cinit__(self):
|
| 6 |
+
self.uv_opt_env = NULL
|
| 7 |
+
self.uv_opt_args = NULL
|
| 8 |
+
self._returncode = None
|
| 9 |
+
self._pid = None
|
| 10 |
+
self._fds_to_close = list()
|
| 11 |
+
self._preexec_fn = None
|
| 12 |
+
self._restore_signals = True
|
| 13 |
+
self.context = Context_CopyCurrent()
|
| 14 |
+
|
| 15 |
+
cdef _close_process_handle(self):
|
| 16 |
+
# XXX: This is a workaround for a libuv bug:
|
| 17 |
+
# - https://github.com/libuv/libuv/issues/1933
|
| 18 |
+
# - https://github.com/libuv/libuv/pull/551
|
| 19 |
+
if self._handle is NULL:
|
| 20 |
+
return
|
| 21 |
+
self._handle.data = NULL
|
| 22 |
+
uv.uv_close(self._handle, __uv_close_process_handle_cb)
|
| 23 |
+
self._handle = NULL # close callback will free() the memory
|
| 24 |
+
|
| 25 |
+
cdef _init(self, Loop loop, list args, dict env,
|
| 26 |
+
cwd, start_new_session,
|
| 27 |
+
_stdin, _stdout, _stderr, # std* can be defined as macros in C
|
| 28 |
+
pass_fds, debug_flags, preexec_fn, restore_signals):
|
| 29 |
+
|
| 30 |
+
global __forking
|
| 31 |
+
global __forking_loop
|
| 32 |
+
global __forkHandler
|
| 33 |
+
|
| 34 |
+
cdef int err
|
| 35 |
+
|
| 36 |
+
self._start_init(loop)
|
| 37 |
+
|
| 38 |
+
self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(
|
| 39 |
+
sizeof(uv.uv_process_t))
|
| 40 |
+
if self._handle is NULL:
|
| 41 |
+
self._abort_init()
|
| 42 |
+
raise MemoryError()
|
| 43 |
+
|
| 44 |
+
# Too early to call _finish_init, but still a lot of work to do.
|
| 45 |
+
# Let's set handle.data to NULL, so in case something goes wrong,
|
| 46 |
+
# callbacks have a chance to avoid casting *something* into UVHandle.
|
| 47 |
+
self._handle.data = NULL
|
| 48 |
+
|
| 49 |
+
force_fork = False
|
| 50 |
+
if system.PLATFORM_IS_APPLE and not (
|
| 51 |
+
preexec_fn is None
|
| 52 |
+
and not pass_fds
|
| 53 |
+
):
|
| 54 |
+
# see _execute_child() in CPython/subprocess.py
|
| 55 |
+
force_fork = True
|
| 56 |
+
|
| 57 |
+
try:
|
| 58 |
+
self._init_options(args, env, cwd, start_new_session,
|
| 59 |
+
_stdin, _stdout, _stderr, force_fork)
|
| 60 |
+
|
| 61 |
+
restore_inheritable = set()
|
| 62 |
+
if pass_fds:
|
| 63 |
+
for fd in pass_fds:
|
| 64 |
+
if not os_get_inheritable(fd):
|
| 65 |
+
restore_inheritable.add(fd)
|
| 66 |
+
os_set_inheritable(fd, True)
|
| 67 |
+
except Exception:
|
| 68 |
+
self._abort_init()
|
| 69 |
+
raise
|
| 70 |
+
|
| 71 |
+
if __forking or loop.active_process_handler is not None:
|
| 72 |
+
# Our pthread_atfork handlers won't work correctly when
|
| 73 |
+
# another loop is forking in another thread (even though
|
| 74 |
+
# GIL should help us to avoid that.)
|
| 75 |
+
self._abort_init()
|
| 76 |
+
raise RuntimeError(
|
| 77 |
+
'Racing with another loop to spawn a process.')
|
| 78 |
+
|
| 79 |
+
self._errpipe_read, self._errpipe_write = os_pipe()
|
| 80 |
+
fds_to_close = self._fds_to_close
|
| 81 |
+
self._fds_to_close = None
|
| 82 |
+
fds_to_close.append(self._errpipe_read)
|
| 83 |
+
# add the write pipe last so we can close it early
|
| 84 |
+
fds_to_close.append(self._errpipe_write)
|
| 85 |
+
try:
|
| 86 |
+
os_set_inheritable(self._errpipe_write, True)
|
| 87 |
+
|
| 88 |
+
self._preexec_fn = preexec_fn
|
| 89 |
+
self._restore_signals = restore_signals
|
| 90 |
+
|
| 91 |
+
loop.active_process_handler = self
|
| 92 |
+
__forking = 1
|
| 93 |
+
__forking_loop = loop
|
| 94 |
+
system.setForkHandler(<system.OnForkHandler>&__get_fork_handler)
|
| 95 |
+
|
| 96 |
+
PyOS_BeforeFork()
|
| 97 |
+
|
| 98 |
+
err = uv.uv_spawn(loop.uvloop,
|
| 99 |
+
<uv.uv_process_t*>self._handle,
|
| 100 |
+
&self.options)
|
| 101 |
+
|
| 102 |
+
__forking = 0
|
| 103 |
+
__forking_loop = None
|
| 104 |
+
system.resetForkHandler()
|
| 105 |
+
loop.active_process_handler = None
|
| 106 |
+
|
| 107 |
+
PyOS_AfterFork_Parent()
|
| 108 |
+
|
| 109 |
+
if err < 0:
|
| 110 |
+
self._close_process_handle()
|
| 111 |
+
self._abort_init()
|
| 112 |
+
raise convert_error(err)
|
| 113 |
+
|
| 114 |
+
self._finish_init()
|
| 115 |
+
|
| 116 |
+
# close the write pipe early
|
| 117 |
+
os_close(fds_to_close.pop())
|
| 118 |
+
|
| 119 |
+
if preexec_fn is not None:
|
| 120 |
+
errpipe_data = bytearray()
|
| 121 |
+
while True:
|
| 122 |
+
# XXX: This is a blocking code that has to be
|
| 123 |
+
# rewritten (using loop.connect_read_pipe() or
|
| 124 |
+
# otherwise.)
|
| 125 |
+
part = os_read(self._errpipe_read, 50000)
|
| 126 |
+
errpipe_data += part
|
| 127 |
+
if not part or len(errpipe_data) > 50000:
|
| 128 |
+
break
|
| 129 |
+
|
| 130 |
+
finally:
|
| 131 |
+
while fds_to_close:
|
| 132 |
+
os_close(fds_to_close.pop())
|
| 133 |
+
|
| 134 |
+
for fd in restore_inheritable:
|
| 135 |
+
os_set_inheritable(fd, False)
|
| 136 |
+
|
| 137 |
+
# asyncio caches the PID in BaseSubprocessTransport,
|
| 138 |
+
# so that the transport knows what the PID was even
|
| 139 |
+
# after the process is finished.
|
| 140 |
+
self._pid = (<uv.uv_process_t*>self._handle).pid
|
| 141 |
+
|
| 142 |
+
# Track the process handle (create a strong ref to it)
|
| 143 |
+
# to guarantee that __dealloc__ doesn't happen in an
|
| 144 |
+
# uncontrolled fashion. We want to wait until the process
|
| 145 |
+
# exits and libuv calls __uvprocess_on_exit_callback,
|
| 146 |
+
# which will call `UVProcess._close()`, which will, in turn,
|
| 147 |
+
# untrack this handle.
|
| 148 |
+
self._loop._track_process(self)
|
| 149 |
+
|
| 150 |
+
if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK:
|
| 151 |
+
time_sleep(1)
|
| 152 |
+
|
| 153 |
+
if preexec_fn is not None and errpipe_data:
|
| 154 |
+
# preexec_fn has raised an exception. The child
|
| 155 |
+
# process must be dead now.
|
| 156 |
+
try:
|
| 157 |
+
exc_name, exc_msg = errpipe_data.split(b':', 1)
|
| 158 |
+
exc_name = exc_name.decode()
|
| 159 |
+
exc_msg = exc_msg.decode()
|
| 160 |
+
except Exception:
|
| 161 |
+
self._close()
|
| 162 |
+
raise subprocess_SubprocessError(
|
| 163 |
+
'Bad exception data from child: {!r}'.format(
|
| 164 |
+
errpipe_data))
|
| 165 |
+
exc_cls = getattr(__builtins__, exc_name,
|
| 166 |
+
subprocess_SubprocessError)
|
| 167 |
+
|
| 168 |
+
exc = subprocess_SubprocessError(
|
| 169 |
+
'Exception occurred in preexec_fn.')
|
| 170 |
+
exc.__cause__ = exc_cls(exc_msg)
|
| 171 |
+
self._close()
|
| 172 |
+
raise exc
|
| 173 |
+
|
| 174 |
+
cdef _after_fork(self):
|
| 175 |
+
# See CPython/_posixsubprocess.c for details
|
| 176 |
+
cdef int err
|
| 177 |
+
|
| 178 |
+
if self._restore_signals:
|
| 179 |
+
_Py_RestoreSignals()
|
| 180 |
+
|
| 181 |
+
PyOS_AfterFork_Child()
|
| 182 |
+
|
| 183 |
+
err = uv.uv_loop_fork(self._loop.uvloop)
|
| 184 |
+
if err < 0:
|
| 185 |
+
raise convert_error(err)
|
| 186 |
+
|
| 187 |
+
if self._preexec_fn is not None:
|
| 188 |
+
try:
|
| 189 |
+
gc_disable()
|
| 190 |
+
self._preexec_fn()
|
| 191 |
+
except BaseException as ex:
|
| 192 |
+
try:
|
| 193 |
+
with open(self._errpipe_write, 'wb') as f:
|
| 194 |
+
f.write(str(ex.__class__.__name__).encode())
|
| 195 |
+
f.write(b':')
|
| 196 |
+
f.write(str(ex.args[0]).encode())
|
| 197 |
+
finally:
|
| 198 |
+
system._exit(255)
|
| 199 |
+
return
|
| 200 |
+
else:
|
| 201 |
+
os_close(self._errpipe_write)
|
| 202 |
+
else:
|
| 203 |
+
os_close(self._errpipe_write)
|
| 204 |
+
|
| 205 |
+
cdef _close_after_spawn(self, int fd):
|
| 206 |
+
if self._fds_to_close is None:
|
| 207 |
+
raise RuntimeError(
|
| 208 |
+
'UVProcess._close_after_spawn called after uv_spawn')
|
| 209 |
+
self._fds_to_close.append(fd)
|
| 210 |
+
|
| 211 |
+
def __dealloc__(self):
|
| 212 |
+
if self.uv_opt_env is not NULL:
|
| 213 |
+
PyMem_RawFree(self.uv_opt_env)
|
| 214 |
+
self.uv_opt_env = NULL
|
| 215 |
+
|
| 216 |
+
if self.uv_opt_args is not NULL:
|
| 217 |
+
PyMem_RawFree(self.uv_opt_args)
|
| 218 |
+
self.uv_opt_args = NULL
|
| 219 |
+
|
| 220 |
+
cdef char** __to_cstring_array(self, list arr):
|
| 221 |
+
cdef:
|
| 222 |
+
int i
|
| 223 |
+
ssize_t arr_len = len(arr)
|
| 224 |
+
bytes el
|
| 225 |
+
|
| 226 |
+
char **ret
|
| 227 |
+
|
| 228 |
+
ret = <char **>PyMem_RawMalloc((arr_len + 1) * sizeof(char *))
|
| 229 |
+
if ret is NULL:
|
| 230 |
+
raise MemoryError()
|
| 231 |
+
|
| 232 |
+
for i in range(arr_len):
|
| 233 |
+
el = arr[i]
|
| 234 |
+
# NB: PyBytes_AsString doesn't copy the data;
|
| 235 |
+
# we have to be careful when the "arr" is GCed,
|
| 236 |
+
# and it shouldn't be ever mutated.
|
| 237 |
+
ret[i] = PyBytes_AsString(el)
|
| 238 |
+
|
| 239 |
+
ret[arr_len] = NULL
|
| 240 |
+
return ret
|
| 241 |
+
|
| 242 |
+
cdef _init_options(self, list args, dict env, cwd, start_new_session,
|
| 243 |
+
_stdin, _stdout, _stderr, bint force_fork):
|
| 244 |
+
|
| 245 |
+
memset(&self.options, 0, sizeof(uv.uv_process_options_t))
|
| 246 |
+
|
| 247 |
+
self._init_env(env)
|
| 248 |
+
self.options.env = self.uv_opt_env
|
| 249 |
+
|
| 250 |
+
self._init_args(args)
|
| 251 |
+
self.options.file = self.uv_opt_file
|
| 252 |
+
self.options.args = self.uv_opt_args
|
| 253 |
+
|
| 254 |
+
if start_new_session:
|
| 255 |
+
self.options.flags |= uv.UV_PROCESS_DETACHED
|
| 256 |
+
|
| 257 |
+
if force_fork:
|
| 258 |
+
# This is a hack to work around the change in libuv 1.44:
|
| 259 |
+
# > macos: use posix_spawn instead of fork
|
| 260 |
+
# where Python subprocess options like preexec_fn are
|
| 261 |
+
# crippled. CPython only uses posix_spawn under a pretty
|
| 262 |
+
# strict list of conditions (see subprocess.py), and falls
|
| 263 |
+
# back to using fork() otherwise. We'd like to simulate such
|
| 264 |
+
# behavior with libuv, but unfortunately libuv doesn't
|
| 265 |
+
# provide explicit API to choose such implementation detail.
|
| 266 |
+
# Based on current (libuv 1.46) behavior, setting
|
| 267 |
+
# UV_PROCESS_SETUID or UV_PROCESS_SETGID would reliably make
|
| 268 |
+
# libuv fallback to use fork, so let's just use it for now.
|
| 269 |
+
self.options.flags |= uv.UV_PROCESS_SETUID
|
| 270 |
+
self.options.uid = uv.getuid()
|
| 271 |
+
|
| 272 |
+
if cwd is not None:
|
| 273 |
+
cwd = os_fspath(cwd)
|
| 274 |
+
|
| 275 |
+
if isinstance(cwd, str):
|
| 276 |
+
cwd = PyUnicode_EncodeFSDefault(cwd)
|
| 277 |
+
if not isinstance(cwd, bytes):
|
| 278 |
+
raise ValueError('cwd must be a str or bytes object')
|
| 279 |
+
|
| 280 |
+
self.__cwd = cwd
|
| 281 |
+
self.options.cwd = PyBytes_AsString(self.__cwd)
|
| 282 |
+
|
| 283 |
+
self.options.exit_cb = &__uvprocess_on_exit_callback
|
| 284 |
+
|
| 285 |
+
self._init_files(_stdin, _stdout, _stderr)
|
| 286 |
+
|
| 287 |
+
cdef _init_args(self, list args):
|
| 288 |
+
cdef:
|
| 289 |
+
bytes path
|
| 290 |
+
int an = len(args)
|
| 291 |
+
|
| 292 |
+
if an < 1:
|
| 293 |
+
raise ValueError('cannot spawn a process: args are empty')
|
| 294 |
+
|
| 295 |
+
self.__args = args.copy()
|
| 296 |
+
for i in range(an):
|
| 297 |
+
arg = os_fspath(args[i])
|
| 298 |
+
if isinstance(arg, str):
|
| 299 |
+
self.__args[i] = PyUnicode_EncodeFSDefault(arg)
|
| 300 |
+
elif not isinstance(arg, bytes):
|
| 301 |
+
raise TypeError('all args must be str or bytes')
|
| 302 |
+
|
| 303 |
+
path = self.__args[0]
|
| 304 |
+
self.uv_opt_file = PyBytes_AsString(path)
|
| 305 |
+
self.uv_opt_args = self.__to_cstring_array(self.__args)
|
| 306 |
+
|
| 307 |
+
cdef _init_env(self, dict env):
|
| 308 |
+
if env is not None:
|
| 309 |
+
self.__env = list()
|
| 310 |
+
for key in env:
|
| 311 |
+
val = env[key]
|
| 312 |
+
|
| 313 |
+
if isinstance(key, str):
|
| 314 |
+
key = PyUnicode_EncodeFSDefault(key)
|
| 315 |
+
elif not isinstance(key, bytes):
|
| 316 |
+
raise TypeError(
|
| 317 |
+
'all environment vars must be bytes or str')
|
| 318 |
+
|
| 319 |
+
if isinstance(val, str):
|
| 320 |
+
val = PyUnicode_EncodeFSDefault(val)
|
| 321 |
+
elif not isinstance(val, bytes):
|
| 322 |
+
raise TypeError(
|
| 323 |
+
'all environment values must be bytes or str')
|
| 324 |
+
|
| 325 |
+
self.__env.append(key + b'=' + val)
|
| 326 |
+
|
| 327 |
+
self.uv_opt_env = self.__to_cstring_array(self.__env)
|
| 328 |
+
else:
|
| 329 |
+
self.__env = None
|
| 330 |
+
|
| 331 |
+
cdef _init_files(self, _stdin, _stdout, _stderr):
|
| 332 |
+
self.options.stdio_count = 0
|
| 333 |
+
|
| 334 |
+
cdef _kill(self, int signum):
|
| 335 |
+
cdef int err
|
| 336 |
+
self._ensure_alive()
|
| 337 |
+
err = uv.uv_process_kill(<uv.uv_process_t*>self._handle, signum)
|
| 338 |
+
if err < 0:
|
| 339 |
+
raise convert_error(err)
|
| 340 |
+
|
| 341 |
+
cdef _on_exit(self, int64_t exit_status, int term_signal):
|
| 342 |
+
if term_signal:
|
| 343 |
+
# From Python docs:
|
| 344 |
+
# A negative value -N indicates that the child was
|
| 345 |
+
# terminated by signal N (POSIX only).
|
| 346 |
+
self._returncode = -term_signal
|
| 347 |
+
else:
|
| 348 |
+
self._returncode = exit_status
|
| 349 |
+
|
| 350 |
+
self._close()
|
| 351 |
+
|
| 352 |
+
cdef _close(self):
|
| 353 |
+
try:
|
| 354 |
+
if self._loop is not None:
|
| 355 |
+
self._loop._untrack_process(self)
|
| 356 |
+
finally:
|
| 357 |
+
UVHandle._close(self)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
DEF _CALL_PIPE_DATA_RECEIVED = 0
|
| 361 |
+
DEF _CALL_PIPE_CONNECTION_LOST = 1
|
| 362 |
+
DEF _CALL_PROCESS_EXITED = 2
|
| 363 |
+
DEF _CALL_CONNECTION_LOST = 3
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
@cython.no_gc_clear
|
| 367 |
+
cdef class UVProcessTransport(UVProcess):
|
| 368 |
+
def __cinit__(self):
|
| 369 |
+
self._exit_waiters = []
|
| 370 |
+
self._protocol = None
|
| 371 |
+
|
| 372 |
+
self._init_futs = []
|
| 373 |
+
self._pending_calls = []
|
| 374 |
+
self._stdio_ready = 0
|
| 375 |
+
|
| 376 |
+
self._stdin = self._stdout = self._stderr = None
|
| 377 |
+
self.stdin_proto = self.stdout_proto = self.stderr_proto = None
|
| 378 |
+
|
| 379 |
+
self._finished = 0
|
| 380 |
+
|
| 381 |
+
cdef _on_exit(self, int64_t exit_status, int term_signal):
|
| 382 |
+
UVProcess._on_exit(self, exit_status, term_signal)
|
| 383 |
+
|
| 384 |
+
if self._stdio_ready:
|
| 385 |
+
self._loop.call_soon(self._protocol.process_exited,
|
| 386 |
+
context=self.context)
|
| 387 |
+
else:
|
| 388 |
+
self._pending_calls.append((_CALL_PROCESS_EXITED, None, None))
|
| 389 |
+
|
| 390 |
+
self._try_finish()
|
| 391 |
+
|
| 392 |
+
for waiter in self._exit_waiters:
|
| 393 |
+
if not waiter.cancelled():
|
| 394 |
+
waiter.set_result(self._returncode)
|
| 395 |
+
self._exit_waiters.clear()
|
| 396 |
+
|
| 397 |
+
self._close()
|
| 398 |
+
|
| 399 |
+
cdef _check_proc(self):
|
| 400 |
+
if not self._is_alive() or self._returncode is not None:
|
| 401 |
+
raise ProcessLookupError()
|
| 402 |
+
|
| 403 |
+
cdef _pipe_connection_lost(self, int fd, exc):
|
| 404 |
+
if self._stdio_ready:
|
| 405 |
+
self._loop.call_soon(self._protocol.pipe_connection_lost, fd, exc,
|
| 406 |
+
context=self.context)
|
| 407 |
+
self._try_finish()
|
| 408 |
+
else:
|
| 409 |
+
self._pending_calls.append((_CALL_PIPE_CONNECTION_LOST, fd, exc))
|
| 410 |
+
|
| 411 |
+
cdef _pipe_data_received(self, int fd, data):
|
| 412 |
+
if self._stdio_ready:
|
| 413 |
+
self._loop.call_soon(self._protocol.pipe_data_received, fd, data,
|
| 414 |
+
context=self.context)
|
| 415 |
+
else:
|
| 416 |
+
self._pending_calls.append((_CALL_PIPE_DATA_RECEIVED, fd, data))
|
| 417 |
+
|
| 418 |
+
cdef _file_redirect_stdio(self, int fd):
|
| 419 |
+
fd = os_dup(fd)
|
| 420 |
+
os_set_inheritable(fd, True)
|
| 421 |
+
self._close_after_spawn(fd)
|
| 422 |
+
return fd
|
| 423 |
+
|
| 424 |
+
cdef _file_devnull(self):
|
| 425 |
+
dn = os_open(os_devnull, os_O_RDWR)
|
| 426 |
+
os_set_inheritable(dn, True)
|
| 427 |
+
self._close_after_spawn(dn)
|
| 428 |
+
return dn
|
| 429 |
+
|
| 430 |
+
cdef _file_outpipe(self):
|
| 431 |
+
r, w = __socketpair()
|
| 432 |
+
os_set_inheritable(w, True)
|
| 433 |
+
self._close_after_spawn(w)
|
| 434 |
+
return r, w
|
| 435 |
+
|
| 436 |
+
cdef _file_inpipe(self):
|
| 437 |
+
r, w = __socketpair()
|
| 438 |
+
os_set_inheritable(r, True)
|
| 439 |
+
self._close_after_spawn(r)
|
| 440 |
+
return r, w
|
| 441 |
+
|
| 442 |
+
cdef _init_files(self, _stdin, _stdout, _stderr):
|
| 443 |
+
cdef uv.uv_stdio_container_t *iocnt
|
| 444 |
+
|
| 445 |
+
UVProcess._init_files(self, _stdin, _stdout, _stderr)
|
| 446 |
+
|
| 447 |
+
io = [None, None, None]
|
| 448 |
+
|
| 449 |
+
self.options.stdio_count = 3
|
| 450 |
+
self.options.stdio = self.iocnt
|
| 451 |
+
|
| 452 |
+
if _stdin is not None:
|
| 453 |
+
if _stdin == subprocess_PIPE:
|
| 454 |
+
r, w = self._file_inpipe()
|
| 455 |
+
io[0] = r
|
| 456 |
+
|
| 457 |
+
self.stdin_proto = WriteSubprocessPipeProto(self, 0)
|
| 458 |
+
waiter = self._loop._new_future()
|
| 459 |
+
self._stdin = WriteUnixTransport.new(
|
| 460 |
+
self._loop, self.stdin_proto, None, waiter)
|
| 461 |
+
self._init_futs.append(waiter)
|
| 462 |
+
self._stdin._open(w)
|
| 463 |
+
self._stdin._init_protocol()
|
| 464 |
+
elif _stdin == subprocess_DEVNULL:
|
| 465 |
+
io[0] = self._file_devnull()
|
| 466 |
+
elif _stdout == subprocess_STDOUT:
|
| 467 |
+
raise ValueError(
|
| 468 |
+
'subprocess.STDOUT is supported only by stderr parameter')
|
| 469 |
+
else:
|
| 470 |
+
io[0] = self._file_redirect_stdio(_stdin)
|
| 471 |
+
else:
|
| 472 |
+
io[0] = self._file_redirect_stdio(0)
|
| 473 |
+
|
| 474 |
+
if _stdout is not None:
|
| 475 |
+
if _stdout == subprocess_PIPE:
|
| 476 |
+
# We can't use UV_CREATE_PIPE here, since 'stderr' might be
|
| 477 |
+
# set to 'subprocess.STDOUT', and there is no way to
|
| 478 |
+
# emulate that functionality with libuv high-level
|
| 479 |
+
# streams API. Therefore, we create pipes for stdout and
|
| 480 |
+
# stderr manually.
|
| 481 |
+
|
| 482 |
+
r, w = self._file_outpipe()
|
| 483 |
+
io[1] = w
|
| 484 |
+
|
| 485 |
+
self.stdout_proto = ReadSubprocessPipeProto(self, 1)
|
| 486 |
+
waiter = self._loop._new_future()
|
| 487 |
+
self._stdout = ReadUnixTransport.new(
|
| 488 |
+
self._loop, self.stdout_proto, None, waiter)
|
| 489 |
+
self._init_futs.append(waiter)
|
| 490 |
+
self._stdout._open(r)
|
| 491 |
+
self._stdout._init_protocol()
|
| 492 |
+
elif _stdout == subprocess_DEVNULL:
|
| 493 |
+
io[1] = self._file_devnull()
|
| 494 |
+
elif _stdout == subprocess_STDOUT:
|
| 495 |
+
raise ValueError(
|
| 496 |
+
'subprocess.STDOUT is supported only by stderr parameter')
|
| 497 |
+
else:
|
| 498 |
+
io[1] = self._file_redirect_stdio(_stdout)
|
| 499 |
+
else:
|
| 500 |
+
io[1] = self._file_redirect_stdio(1)
|
| 501 |
+
|
| 502 |
+
if _stderr is not None:
|
| 503 |
+
if _stderr == subprocess_PIPE:
|
| 504 |
+
r, w = self._file_outpipe()
|
| 505 |
+
io[2] = w
|
| 506 |
+
|
| 507 |
+
self.stderr_proto = ReadSubprocessPipeProto(self, 2)
|
| 508 |
+
waiter = self._loop._new_future()
|
| 509 |
+
self._stderr = ReadUnixTransport.new(
|
| 510 |
+
self._loop, self.stderr_proto, None, waiter)
|
| 511 |
+
self._init_futs.append(waiter)
|
| 512 |
+
self._stderr._open(r)
|
| 513 |
+
self._stderr._init_protocol()
|
| 514 |
+
elif _stderr == subprocess_STDOUT:
|
| 515 |
+
if io[1] is None:
|
| 516 |
+
# shouldn't ever happen
|
| 517 |
+
raise RuntimeError('cannot apply subprocess.STDOUT')
|
| 518 |
+
|
| 519 |
+
io[2] = self._file_redirect_stdio(io[1])
|
| 520 |
+
elif _stderr == subprocess_DEVNULL:
|
| 521 |
+
io[2] = self._file_devnull()
|
| 522 |
+
else:
|
| 523 |
+
io[2] = self._file_redirect_stdio(_stderr)
|
| 524 |
+
else:
|
| 525 |
+
io[2] = self._file_redirect_stdio(2)
|
| 526 |
+
|
| 527 |
+
assert len(io) == 3
|
| 528 |
+
for idx in range(3):
|
| 529 |
+
iocnt = &self.iocnt[idx]
|
| 530 |
+
if io[idx] is not None:
|
| 531 |
+
iocnt.flags = uv.UV_INHERIT_FD
|
| 532 |
+
iocnt.data.fd = io[idx]
|
| 533 |
+
else:
|
| 534 |
+
iocnt.flags = uv.UV_IGNORE
|
| 535 |
+
|
| 536 |
+
cdef _call_connection_made(self, waiter):
|
| 537 |
+
try:
|
| 538 |
+
# we're always called in the right context, so just call the user's
|
| 539 |
+
self._protocol.connection_made(self)
|
| 540 |
+
except (KeyboardInterrupt, SystemExit):
|
| 541 |
+
raise
|
| 542 |
+
except BaseException as ex:
|
| 543 |
+
if waiter is not None and not waiter.cancelled():
|
| 544 |
+
waiter.set_exception(ex)
|
| 545 |
+
else:
|
| 546 |
+
raise
|
| 547 |
+
else:
|
| 548 |
+
if waiter is not None and not waiter.cancelled():
|
| 549 |
+
waiter.set_result(True)
|
| 550 |
+
|
| 551 |
+
self._stdio_ready = 1
|
| 552 |
+
if self._pending_calls:
|
| 553 |
+
pending_calls = self._pending_calls.copy()
|
| 554 |
+
self._pending_calls.clear()
|
| 555 |
+
for (type, fd, arg) in pending_calls:
|
| 556 |
+
if type == _CALL_PIPE_CONNECTION_LOST:
|
| 557 |
+
self._pipe_connection_lost(fd, arg)
|
| 558 |
+
elif type == _CALL_PIPE_DATA_RECEIVED:
|
| 559 |
+
self._pipe_data_received(fd, arg)
|
| 560 |
+
elif type == _CALL_PROCESS_EXITED:
|
| 561 |
+
self._loop.call_soon(self._protocol.process_exited)
|
| 562 |
+
elif type == _CALL_CONNECTION_LOST:
|
| 563 |
+
self._loop.call_soon(self._protocol.connection_lost, None)
|
| 564 |
+
|
| 565 |
+
cdef _try_finish(self):
|
| 566 |
+
if self._returncode is None or self._finished:
|
| 567 |
+
return
|
| 568 |
+
|
| 569 |
+
if ((self.stdin_proto is None or self.stdin_proto.disconnected) and
|
| 570 |
+
(self.stdout_proto is None or
|
| 571 |
+
self.stdout_proto.disconnected) and
|
| 572 |
+
(self.stderr_proto is None or
|
| 573 |
+
self.stderr_proto.disconnected)):
|
| 574 |
+
|
| 575 |
+
self._finished = 1
|
| 576 |
+
|
| 577 |
+
if self._stdio_ready:
|
| 578 |
+
# copy self.context for simplicity
|
| 579 |
+
self._loop.call_soon(self._protocol.connection_lost, None,
|
| 580 |
+
context=self.context)
|
| 581 |
+
else:
|
| 582 |
+
self._pending_calls.append((_CALL_CONNECTION_LOST, None, None))
|
| 583 |
+
|
| 584 |
+
def __stdio_inited(self, waiter, stdio_fut):
|
| 585 |
+
exc = stdio_fut.exception()
|
| 586 |
+
if exc is not None:
|
| 587 |
+
if waiter is None:
|
| 588 |
+
raise exc
|
| 589 |
+
else:
|
| 590 |
+
waiter.set_exception(exc)
|
| 591 |
+
else:
|
| 592 |
+
self._loop._call_soon_handle(
|
| 593 |
+
new_MethodHandle1(self._loop,
|
| 594 |
+
"UVProcessTransport._call_connection_made",
|
| 595 |
+
<method1_t>self._call_connection_made,
|
| 596 |
+
None, # means to copy the current context
|
| 597 |
+
self, waiter))
|
| 598 |
+
|
| 599 |
+
@staticmethod
|
| 600 |
+
cdef UVProcessTransport new(Loop loop, protocol, args, env,
|
| 601 |
+
cwd, start_new_session,
|
| 602 |
+
_stdin, _stdout, _stderr, pass_fds,
|
| 603 |
+
waiter,
|
| 604 |
+
debug_flags,
|
| 605 |
+
preexec_fn,
|
| 606 |
+
restore_signals):
|
| 607 |
+
|
| 608 |
+
cdef UVProcessTransport handle
|
| 609 |
+
handle = UVProcessTransport.__new__(UVProcessTransport)
|
| 610 |
+
handle._protocol = protocol
|
| 611 |
+
handle._init(loop, args, env, cwd, start_new_session,
|
| 612 |
+
__process_convert_fileno(_stdin),
|
| 613 |
+
__process_convert_fileno(_stdout),
|
| 614 |
+
__process_convert_fileno(_stderr),
|
| 615 |
+
pass_fds,
|
| 616 |
+
debug_flags,
|
| 617 |
+
preexec_fn,
|
| 618 |
+
restore_signals)
|
| 619 |
+
|
| 620 |
+
if handle._init_futs:
|
| 621 |
+
handle._stdio_ready = 0
|
| 622 |
+
init_fut = aio_gather(*handle._init_futs)
|
| 623 |
+
# add_done_callback will copy the current context and run the
|
| 624 |
+
# callback within the context
|
| 625 |
+
init_fut.add_done_callback(
|
| 626 |
+
ft_partial(handle.__stdio_inited, waiter))
|
| 627 |
+
else:
|
| 628 |
+
handle._stdio_ready = 1
|
| 629 |
+
loop._call_soon_handle(
|
| 630 |
+
new_MethodHandle1(loop,
|
| 631 |
+
"UVProcessTransport._call_connection_made",
|
| 632 |
+
<method1_t>handle._call_connection_made,
|
| 633 |
+
None, # means to copy the current context
|
| 634 |
+
handle, waiter))
|
| 635 |
+
|
| 636 |
+
return handle
|
| 637 |
+
|
| 638 |
+
def get_protocol(self):
|
| 639 |
+
return self._protocol
|
| 640 |
+
|
| 641 |
+
def set_protocol(self, protocol):
|
| 642 |
+
self._protocol = protocol
|
| 643 |
+
|
| 644 |
+
def get_pid(self):
|
| 645 |
+
return self._pid
|
| 646 |
+
|
| 647 |
+
def get_returncode(self):
|
| 648 |
+
return self._returncode
|
| 649 |
+
|
| 650 |
+
def get_pipe_transport(self, fd):
|
| 651 |
+
if fd == 0:
|
| 652 |
+
return self._stdin
|
| 653 |
+
elif fd == 1:
|
| 654 |
+
return self._stdout
|
| 655 |
+
elif fd == 2:
|
| 656 |
+
return self._stderr
|
| 657 |
+
|
| 658 |
+
def terminate(self):
|
| 659 |
+
self._check_proc()
|
| 660 |
+
self._kill(uv.SIGTERM)
|
| 661 |
+
|
| 662 |
+
def kill(self):
|
| 663 |
+
self._check_proc()
|
| 664 |
+
self._kill(uv.SIGKILL)
|
| 665 |
+
|
| 666 |
+
def send_signal(self, int signal):
|
| 667 |
+
self._check_proc()
|
| 668 |
+
self._kill(signal)
|
| 669 |
+
|
| 670 |
+
def is_closing(self):
|
| 671 |
+
return self._closed
|
| 672 |
+
|
| 673 |
+
def close(self):
|
| 674 |
+
if self._returncode is None:
|
| 675 |
+
self._kill(uv.SIGKILL)
|
| 676 |
+
|
| 677 |
+
if self._stdin is not None:
|
| 678 |
+
self._stdin.close()
|
| 679 |
+
if self._stdout is not None:
|
| 680 |
+
self._stdout.close()
|
| 681 |
+
if self._stderr is not None:
|
| 682 |
+
self._stderr.close()
|
| 683 |
+
|
| 684 |
+
if self._returncode is not None:
|
| 685 |
+
# The process is dead, just close the UV handle.
|
| 686 |
+
#
|
| 687 |
+
# (If "self._returncode is None", the process should have been
|
| 688 |
+
# killed already and we're just waiting for a SIGCHLD; after
|
| 689 |
+
# which the transport will be GC'ed and the uvhandle will be
|
| 690 |
+
# closed in UVHandle.__dealloc__.)
|
| 691 |
+
self._close()
|
| 692 |
+
|
| 693 |
+
def get_extra_info(self, name, default=None):
|
| 694 |
+
return default
|
| 695 |
+
|
| 696 |
+
def _wait(self):
|
| 697 |
+
fut = self._loop._new_future()
|
| 698 |
+
if self._returncode is not None:
|
| 699 |
+
fut.set_result(self._returncode)
|
| 700 |
+
return fut
|
| 701 |
+
|
| 702 |
+
self._exit_waiters.append(fut)
|
| 703 |
+
return fut
|
| 704 |
+
|
| 705 |
+
|
| 706 |
+
class WriteSubprocessPipeProto(aio_BaseProtocol):
|
| 707 |
+
|
| 708 |
+
def __init__(self, proc, fd):
|
| 709 |
+
if UVLOOP_DEBUG:
|
| 710 |
+
if type(proc) is not UVProcessTransport:
|
| 711 |
+
raise TypeError
|
| 712 |
+
if not isinstance(fd, int):
|
| 713 |
+
raise TypeError
|
| 714 |
+
self.proc = proc
|
| 715 |
+
self.fd = fd
|
| 716 |
+
self.pipe = None
|
| 717 |
+
self.disconnected = False
|
| 718 |
+
|
| 719 |
+
def connection_made(self, transport):
|
| 720 |
+
self.pipe = transport
|
| 721 |
+
|
| 722 |
+
def __repr__(self):
|
| 723 |
+
return ('<%s fd=%s pipe=%r>'
|
| 724 |
+
% (self.__class__.__name__, self.fd, self.pipe))
|
| 725 |
+
|
| 726 |
+
def connection_lost(self, exc):
|
| 727 |
+
self.disconnected = True
|
| 728 |
+
(<UVProcessTransport>self.proc)._pipe_connection_lost(self.fd, exc)
|
| 729 |
+
self.proc = None
|
| 730 |
+
|
| 731 |
+
def pause_writing(self):
|
| 732 |
+
(<UVProcessTransport>self.proc)._protocol.pause_writing()
|
| 733 |
+
|
| 734 |
+
def resume_writing(self):
|
| 735 |
+
(<UVProcessTransport>self.proc)._protocol.resume_writing()
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
|
| 739 |
+
aio_Protocol):
|
| 740 |
+
|
| 741 |
+
def data_received(self, data):
|
| 742 |
+
(<UVProcessTransport>self.proc)._pipe_data_received(self.fd, data)
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
cdef __process_convert_fileno(object obj):
|
| 746 |
+
if obj is None or isinstance(obj, int):
|
| 747 |
+
return obj
|
| 748 |
+
|
| 749 |
+
fileno = obj.fileno()
|
| 750 |
+
if not isinstance(fileno, int):
|
| 751 |
+
raise TypeError(
|
| 752 |
+
'{!r}.fileno() returned non-integer'.format(obj))
|
| 753 |
+
return fileno
|
| 754 |
+
|
| 755 |
+
|
| 756 |
+
cdef void __uvprocess_on_exit_callback(
|
| 757 |
+
uv.uv_process_t *handle,
|
| 758 |
+
int64_t exit_status,
|
| 759 |
+
int term_signal,
|
| 760 |
+
) noexcept with gil:
|
| 761 |
+
|
| 762 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle,
|
| 763 |
+
"UVProcess exit callback") == 0:
|
| 764 |
+
return
|
| 765 |
+
|
| 766 |
+
cdef UVProcess proc = <UVProcess> handle.data
|
| 767 |
+
try:
|
| 768 |
+
proc._on_exit(exit_status, term_signal)
|
| 769 |
+
except BaseException as ex:
|
| 770 |
+
proc._error(ex, False)
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
cdef __socketpair():
|
| 774 |
+
cdef:
|
| 775 |
+
int fds[2]
|
| 776 |
+
int err
|
| 777 |
+
|
| 778 |
+
err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds)
|
| 779 |
+
if err:
|
| 780 |
+
exc = convert_error(-err)
|
| 781 |
+
raise exc
|
| 782 |
+
|
| 783 |
+
os_set_inheritable(fds[0], False)
|
| 784 |
+
os_set_inheritable(fds[1], False)
|
| 785 |
+
|
| 786 |
+
return fds[0], fds[1]
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
cdef void __uv_close_process_handle_cb(
|
| 790 |
+
uv.uv_handle_t* handle
|
| 791 |
+
) noexcept with gil:
|
| 792 |
+
PyMem_RawFree(handle)
|
.venv/lib/python3.11/site-packages/uvloop/handles/stream.pxd
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVStream(UVBaseTransport):
|
| 2 |
+
cdef:
|
| 3 |
+
uv.uv_shutdown_t _shutdown_req
|
| 4 |
+
bint __shutting_down
|
| 5 |
+
bint __reading
|
| 6 |
+
bint __read_error_close
|
| 7 |
+
|
| 8 |
+
bint __buffered
|
| 9 |
+
object _protocol_get_buffer
|
| 10 |
+
object _protocol_buffer_updated
|
| 11 |
+
|
| 12 |
+
bint _eof
|
| 13 |
+
list _buffer
|
| 14 |
+
size_t _buffer_size
|
| 15 |
+
|
| 16 |
+
Py_buffer _read_pybuf
|
| 17 |
+
bint _read_pybuf_acquired
|
| 18 |
+
|
| 19 |
+
# All "inline" methods are final
|
| 20 |
+
|
| 21 |
+
cdef inline _init(self, Loop loop, object protocol, Server server,
|
| 22 |
+
object waiter, object context)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
cdef inline _shutdown(self)
|
| 26 |
+
cdef inline _accept(self, UVStream server)
|
| 27 |
+
|
| 28 |
+
cdef inline _close_on_read_error(self)
|
| 29 |
+
|
| 30 |
+
cdef inline __reading_started(self)
|
| 31 |
+
cdef inline __reading_stopped(self)
|
| 32 |
+
|
| 33 |
+
# The user API write() and writelines() firstly call _buffer_write() to
|
| 34 |
+
# buffer up user data chunks, potentially multiple times in writelines(),
|
| 35 |
+
# and then call _initiate_write() to start writing either immediately or in
|
| 36 |
+
# the next iteration (loop._queue_write()).
|
| 37 |
+
cdef inline _buffer_write(self, object data)
|
| 38 |
+
cdef inline _initiate_write(self)
|
| 39 |
+
|
| 40 |
+
# _exec_write() is the method that does the actual send, and _try_write()
|
| 41 |
+
# is a fast-path used in _exec_write() to send a single chunk.
|
| 42 |
+
cdef inline _exec_write(self)
|
| 43 |
+
cdef inline _try_write(self, object data)
|
| 44 |
+
|
| 45 |
+
cdef _close(self)
|
| 46 |
+
|
| 47 |
+
cdef inline _on_accept(self)
|
| 48 |
+
cdef inline _on_eof(self)
|
| 49 |
+
cdef inline _on_write(self)
|
| 50 |
+
cdef inline _on_connect(self, object exc)
|
.venv/lib/python3.11/site-packages/uvloop/handles/stream.pyx
ADDED
|
@@ -0,0 +1,1019 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef extern from *:
|
| 2 |
+
'''
|
| 3 |
+
enum {__PREALLOCED_BUFS = 4};
|
| 4 |
+
'''
|
| 5 |
+
const bint __PREALLOCED_BUFS
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@cython.no_gc_clear
|
| 9 |
+
@cython.freelist(DEFAULT_FREELIST_SIZE)
|
| 10 |
+
cdef class _StreamWriteContext:
|
| 11 |
+
# used to hold additional write request information for uv_write
|
| 12 |
+
|
| 13 |
+
cdef:
|
| 14 |
+
uv.uv_write_t req
|
| 15 |
+
|
| 16 |
+
list buffers
|
| 17 |
+
|
| 18 |
+
uv.uv_buf_t uv_bufs_sml[__PREALLOCED_BUFS]
|
| 19 |
+
Py_buffer py_bufs_sml[__PREALLOCED_BUFS]
|
| 20 |
+
bint py_bufs_sml_inuse
|
| 21 |
+
|
| 22 |
+
uv.uv_buf_t* uv_bufs
|
| 23 |
+
Py_buffer* py_bufs
|
| 24 |
+
size_t py_bufs_len
|
| 25 |
+
|
| 26 |
+
uv.uv_buf_t* uv_bufs_start
|
| 27 |
+
size_t uv_bufs_len
|
| 28 |
+
|
| 29 |
+
UVStream stream
|
| 30 |
+
|
| 31 |
+
bint closed
|
| 32 |
+
|
| 33 |
+
cdef free_bufs(self):
|
| 34 |
+
cdef size_t i
|
| 35 |
+
|
| 36 |
+
if self.uv_bufs is not NULL:
|
| 37 |
+
PyMem_RawFree(self.uv_bufs)
|
| 38 |
+
self.uv_bufs = NULL
|
| 39 |
+
if UVLOOP_DEBUG:
|
| 40 |
+
if self.py_bufs_sml_inuse:
|
| 41 |
+
raise RuntimeError(
|
| 42 |
+
'_StreamWriteContext.close: uv_bufs != NULL and '
|
| 43 |
+
'py_bufs_sml_inuse is True')
|
| 44 |
+
|
| 45 |
+
if self.py_bufs is not NULL:
|
| 46 |
+
for i from 0 <= i < self.py_bufs_len:
|
| 47 |
+
PyBuffer_Release(&self.py_bufs[i])
|
| 48 |
+
PyMem_RawFree(self.py_bufs)
|
| 49 |
+
self.py_bufs = NULL
|
| 50 |
+
if UVLOOP_DEBUG:
|
| 51 |
+
if self.py_bufs_sml_inuse:
|
| 52 |
+
raise RuntimeError(
|
| 53 |
+
'_StreamWriteContext.close: py_bufs != NULL and '
|
| 54 |
+
'py_bufs_sml_inuse is True')
|
| 55 |
+
|
| 56 |
+
if self.py_bufs_sml_inuse:
|
| 57 |
+
for i from 0 <= i < self.py_bufs_len:
|
| 58 |
+
PyBuffer_Release(&self.py_bufs_sml[i])
|
| 59 |
+
self.py_bufs_sml_inuse = 0
|
| 60 |
+
|
| 61 |
+
self.py_bufs_len = 0
|
| 62 |
+
self.buffers = None
|
| 63 |
+
|
| 64 |
+
cdef close(self):
|
| 65 |
+
if self.closed:
|
| 66 |
+
return
|
| 67 |
+
self.closed = 1
|
| 68 |
+
self.free_bufs()
|
| 69 |
+
Py_DECREF(self)
|
| 70 |
+
|
| 71 |
+
cdef advance_uv_buf(self, size_t sent):
|
| 72 |
+
# Advance the pointer to first uv_buf and the
|
| 73 |
+
# pointer to first byte in that buffer.
|
| 74 |
+
#
|
| 75 |
+
# We do this after a "uv_try_write" call, which
|
| 76 |
+
# sometimes sends only a portion of data.
|
| 77 |
+
# We then call "advance_uv_buf" on the write
|
| 78 |
+
# context, and reuse it in a "uv_write" call.
|
| 79 |
+
|
| 80 |
+
cdef:
|
| 81 |
+
uv.uv_buf_t* buf
|
| 82 |
+
size_t idx
|
| 83 |
+
|
| 84 |
+
for idx from 0 <= idx < self.uv_bufs_len:
|
| 85 |
+
buf = &self.uv_bufs_start[idx]
|
| 86 |
+
if buf.len > sent:
|
| 87 |
+
buf.len -= sent
|
| 88 |
+
buf.base = buf.base + sent
|
| 89 |
+
self.uv_bufs_start = buf
|
| 90 |
+
self.uv_bufs_len -= idx
|
| 91 |
+
return
|
| 92 |
+
else:
|
| 93 |
+
sent -= self.uv_bufs_start[idx].len
|
| 94 |
+
|
| 95 |
+
if UVLOOP_DEBUG:
|
| 96 |
+
if sent < 0:
|
| 97 |
+
raise RuntimeError('fatal: sent < 0 in advance_uv_buf')
|
| 98 |
+
|
| 99 |
+
raise RuntimeError('fatal: Could not advance _StreamWriteContext')
|
| 100 |
+
|
| 101 |
+
@staticmethod
|
| 102 |
+
cdef _StreamWriteContext new(UVStream stream, list buffers):
|
| 103 |
+
cdef:
|
| 104 |
+
_StreamWriteContext ctx
|
| 105 |
+
int uv_bufs_idx = 0
|
| 106 |
+
size_t py_bufs_len = 0
|
| 107 |
+
int i
|
| 108 |
+
|
| 109 |
+
Py_buffer* p_pybufs
|
| 110 |
+
uv.uv_buf_t* p_uvbufs
|
| 111 |
+
|
| 112 |
+
ctx = _StreamWriteContext.__new__(_StreamWriteContext)
|
| 113 |
+
ctx.stream = None
|
| 114 |
+
ctx.closed = 1
|
| 115 |
+
ctx.py_bufs_len = 0
|
| 116 |
+
ctx.py_bufs_sml_inuse = 0
|
| 117 |
+
ctx.uv_bufs = NULL
|
| 118 |
+
ctx.py_bufs = NULL
|
| 119 |
+
ctx.buffers = buffers
|
| 120 |
+
ctx.stream = stream
|
| 121 |
+
|
| 122 |
+
if len(buffers) <= __PREALLOCED_BUFS:
|
| 123 |
+
# We've got a small number of buffers to write, don't
|
| 124 |
+
# need to use malloc.
|
| 125 |
+
ctx.py_bufs_sml_inuse = 1
|
| 126 |
+
p_pybufs = <Py_buffer*>&ctx.py_bufs_sml
|
| 127 |
+
p_uvbufs = <uv.uv_buf_t*>&ctx.uv_bufs_sml
|
| 128 |
+
|
| 129 |
+
else:
|
| 130 |
+
for buf in buffers:
|
| 131 |
+
if UVLOOP_DEBUG:
|
| 132 |
+
if not isinstance(buf, (bytes, bytearray, memoryview)):
|
| 133 |
+
raise RuntimeError(
|
| 134 |
+
'invalid data in writebuf: an instance of '
|
| 135 |
+
'bytes, bytearray or memoryview was expected, '
|
| 136 |
+
'got {}'.format(type(buf)))
|
| 137 |
+
|
| 138 |
+
if not PyBytes_CheckExact(buf):
|
| 139 |
+
py_bufs_len += 1
|
| 140 |
+
|
| 141 |
+
if py_bufs_len > 0:
|
| 142 |
+
ctx.py_bufs = <Py_buffer*>PyMem_RawMalloc(
|
| 143 |
+
py_bufs_len * sizeof(Py_buffer))
|
| 144 |
+
if ctx.py_bufs is NULL:
|
| 145 |
+
raise MemoryError()
|
| 146 |
+
|
| 147 |
+
ctx.uv_bufs = <uv.uv_buf_t*>PyMem_RawMalloc(
|
| 148 |
+
len(buffers) * sizeof(uv.uv_buf_t))
|
| 149 |
+
if ctx.uv_bufs is NULL:
|
| 150 |
+
raise MemoryError()
|
| 151 |
+
|
| 152 |
+
p_pybufs = ctx.py_bufs
|
| 153 |
+
p_uvbufs = ctx.uv_bufs
|
| 154 |
+
|
| 155 |
+
py_bufs_len = 0
|
| 156 |
+
for buf in buffers:
|
| 157 |
+
if PyBytes_CheckExact(buf):
|
| 158 |
+
# We can only use this hack for bytes since it's
|
| 159 |
+
# immutable. For everything else it is only safe to
|
| 160 |
+
# use buffer protocol.
|
| 161 |
+
p_uvbufs[uv_bufs_idx].base = PyBytes_AS_STRING(buf)
|
| 162 |
+
p_uvbufs[uv_bufs_idx].len = Py_SIZE(buf)
|
| 163 |
+
|
| 164 |
+
else:
|
| 165 |
+
try:
|
| 166 |
+
PyObject_GetBuffer(
|
| 167 |
+
buf, &p_pybufs[py_bufs_len], PyBUF_SIMPLE)
|
| 168 |
+
except Exception:
|
| 169 |
+
# This shouldn't ever happen, as `UVStream._buffer_write`
|
| 170 |
+
# casts non-bytes objects to `memoryviews`.
|
| 171 |
+
ctx.py_bufs_len = py_bufs_len
|
| 172 |
+
ctx.free_bufs()
|
| 173 |
+
raise
|
| 174 |
+
|
| 175 |
+
p_uvbufs[uv_bufs_idx].base = <char*>p_pybufs[py_bufs_len].buf
|
| 176 |
+
p_uvbufs[uv_bufs_idx].len = p_pybufs[py_bufs_len].len
|
| 177 |
+
|
| 178 |
+
py_bufs_len += 1
|
| 179 |
+
|
| 180 |
+
uv_bufs_idx += 1
|
| 181 |
+
|
| 182 |
+
ctx.uv_bufs_start = p_uvbufs
|
| 183 |
+
ctx.uv_bufs_len = uv_bufs_idx
|
| 184 |
+
|
| 185 |
+
ctx.py_bufs_len = py_bufs_len
|
| 186 |
+
ctx.req.data = <void*> ctx
|
| 187 |
+
|
| 188 |
+
if UVLOOP_DEBUG:
|
| 189 |
+
stream._loop._debug_stream_write_ctx_total += 1
|
| 190 |
+
stream._loop._debug_stream_write_ctx_cnt += 1
|
| 191 |
+
|
| 192 |
+
# Do incref after everything else is done.
|
| 193 |
+
# Under no circumstances we want `ctx` to be GCed while
|
| 194 |
+
# libuv is still working with `ctx.uv_bufs`.
|
| 195 |
+
Py_INCREF(ctx)
|
| 196 |
+
ctx.closed = 0
|
| 197 |
+
return ctx
|
| 198 |
+
|
| 199 |
+
def __dealloc__(self):
|
| 200 |
+
if not self.closed:
|
| 201 |
+
# Because we do an INCREF in _StreamWriteContext.new,
|
| 202 |
+
# __dealloc__ shouldn't ever happen with `self.closed == 1`
|
| 203 |
+
raise RuntimeError(
|
| 204 |
+
'open _StreamWriteContext is being deallocated')
|
| 205 |
+
|
| 206 |
+
if UVLOOP_DEBUG:
|
| 207 |
+
if self.stream is not None:
|
| 208 |
+
self.stream._loop._debug_stream_write_ctx_cnt -= 1
|
| 209 |
+
self.stream = None
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
@cython.no_gc_clear
|
| 213 |
+
cdef class UVStream(UVBaseTransport):
|
| 214 |
+
|
| 215 |
+
def __cinit__(self):
|
| 216 |
+
self.__shutting_down = 0
|
| 217 |
+
self.__reading = 0
|
| 218 |
+
self.__read_error_close = 0
|
| 219 |
+
self.__buffered = 0
|
| 220 |
+
self._eof = 0
|
| 221 |
+
self._buffer = []
|
| 222 |
+
self._buffer_size = 0
|
| 223 |
+
|
| 224 |
+
self._protocol_get_buffer = None
|
| 225 |
+
self._protocol_buffer_updated = None
|
| 226 |
+
|
| 227 |
+
self._read_pybuf_acquired = False
|
| 228 |
+
|
| 229 |
+
cdef _set_protocol(self, object protocol):
|
| 230 |
+
if protocol is None:
|
| 231 |
+
raise TypeError('protocol is required')
|
| 232 |
+
|
| 233 |
+
UVBaseTransport._set_protocol(self, protocol)
|
| 234 |
+
|
| 235 |
+
if (hasattr(protocol, 'get_buffer') and
|
| 236 |
+
not isinstance(protocol, aio_Protocol)):
|
| 237 |
+
try:
|
| 238 |
+
self._protocol_get_buffer = protocol.get_buffer
|
| 239 |
+
self._protocol_buffer_updated = protocol.buffer_updated
|
| 240 |
+
self.__buffered = 1
|
| 241 |
+
except AttributeError:
|
| 242 |
+
pass
|
| 243 |
+
else:
|
| 244 |
+
self.__buffered = 0
|
| 245 |
+
|
| 246 |
+
cdef _clear_protocol(self):
|
| 247 |
+
UVBaseTransport._clear_protocol(self)
|
| 248 |
+
self._protocol_get_buffer = None
|
| 249 |
+
self._protocol_buffer_updated = None
|
| 250 |
+
self.__buffered = 0
|
| 251 |
+
|
| 252 |
+
cdef inline _shutdown(self):
|
| 253 |
+
cdef int err
|
| 254 |
+
|
| 255 |
+
if self.__shutting_down:
|
| 256 |
+
return
|
| 257 |
+
self.__shutting_down = 1
|
| 258 |
+
|
| 259 |
+
self._ensure_alive()
|
| 260 |
+
|
| 261 |
+
self._shutdown_req.data = <void*> self
|
| 262 |
+
err = uv.uv_shutdown(&self._shutdown_req,
|
| 263 |
+
<uv.uv_stream_t*> self._handle,
|
| 264 |
+
__uv_stream_on_shutdown)
|
| 265 |
+
if err < 0:
|
| 266 |
+
exc = convert_error(err)
|
| 267 |
+
self._fatal_error(exc, True)
|
| 268 |
+
return
|
| 269 |
+
|
| 270 |
+
cdef inline _accept(self, UVStream server):
|
| 271 |
+
cdef int err
|
| 272 |
+
self._ensure_alive()
|
| 273 |
+
|
| 274 |
+
err = uv.uv_accept(<uv.uv_stream_t*>server._handle,
|
| 275 |
+
<uv.uv_stream_t*>self._handle)
|
| 276 |
+
if err < 0:
|
| 277 |
+
exc = convert_error(err)
|
| 278 |
+
self._fatal_error(exc, True)
|
| 279 |
+
return
|
| 280 |
+
|
| 281 |
+
self._on_accept()
|
| 282 |
+
|
| 283 |
+
cdef inline _close_on_read_error(self):
|
| 284 |
+
self.__read_error_close = 1
|
| 285 |
+
|
| 286 |
+
cdef bint _is_reading(self):
|
| 287 |
+
return self.__reading
|
| 288 |
+
|
| 289 |
+
cdef _start_reading(self):
|
| 290 |
+
cdef int err
|
| 291 |
+
|
| 292 |
+
if self._closing:
|
| 293 |
+
return
|
| 294 |
+
|
| 295 |
+
self._ensure_alive()
|
| 296 |
+
|
| 297 |
+
if self.__reading:
|
| 298 |
+
return
|
| 299 |
+
|
| 300 |
+
if self.__buffered:
|
| 301 |
+
err = uv.uv_read_start(<uv.uv_stream_t*>self._handle,
|
| 302 |
+
__uv_stream_buffered_alloc,
|
| 303 |
+
__uv_stream_buffered_on_read)
|
| 304 |
+
else:
|
| 305 |
+
err = uv.uv_read_start(<uv.uv_stream_t*>self._handle,
|
| 306 |
+
__loop_alloc_buffer,
|
| 307 |
+
__uv_stream_on_read)
|
| 308 |
+
if err < 0:
|
| 309 |
+
exc = convert_error(err)
|
| 310 |
+
self._fatal_error(exc, True)
|
| 311 |
+
return
|
| 312 |
+
else:
|
| 313 |
+
# UVStream must live until the read callback is called
|
| 314 |
+
self.__reading_started()
|
| 315 |
+
|
| 316 |
+
cdef inline __reading_started(self):
|
| 317 |
+
if self.__reading:
|
| 318 |
+
return
|
| 319 |
+
self.__reading = 1
|
| 320 |
+
Py_INCREF(self)
|
| 321 |
+
|
| 322 |
+
cdef inline __reading_stopped(self):
|
| 323 |
+
if not self.__reading:
|
| 324 |
+
return
|
| 325 |
+
self.__reading = 0
|
| 326 |
+
Py_DECREF(self)
|
| 327 |
+
|
| 328 |
+
cdef _stop_reading(self):
|
| 329 |
+
cdef int err
|
| 330 |
+
|
| 331 |
+
if not self.__reading:
|
| 332 |
+
return
|
| 333 |
+
|
| 334 |
+
self._ensure_alive()
|
| 335 |
+
|
| 336 |
+
# From libuv docs:
|
| 337 |
+
# This function is idempotent and may be safely
|
| 338 |
+
# called on a stopped stream.
|
| 339 |
+
err = uv.uv_read_stop(<uv.uv_stream_t*>self._handle)
|
| 340 |
+
if err < 0:
|
| 341 |
+
exc = convert_error(err)
|
| 342 |
+
self._fatal_error(exc, True)
|
| 343 |
+
return
|
| 344 |
+
else:
|
| 345 |
+
self.__reading_stopped()
|
| 346 |
+
|
| 347 |
+
cdef inline _try_write(self, object data):
|
| 348 |
+
cdef:
|
| 349 |
+
ssize_t written
|
| 350 |
+
bint used_buf = 0
|
| 351 |
+
Py_buffer py_buf
|
| 352 |
+
void* buf
|
| 353 |
+
size_t blen
|
| 354 |
+
int saved_errno
|
| 355 |
+
int fd
|
| 356 |
+
|
| 357 |
+
if (<uv.uv_stream_t*>self._handle).write_queue_size != 0:
|
| 358 |
+
raise RuntimeError(
|
| 359 |
+
'UVStream._try_write called with data in uv buffers')
|
| 360 |
+
|
| 361 |
+
if PyBytes_CheckExact(data):
|
| 362 |
+
# We can only use this hack for bytes since it's
|
| 363 |
+
# immutable. For everything else it is only safe to
|
| 364 |
+
# use buffer protocol.
|
| 365 |
+
buf = <void*>PyBytes_AS_STRING(data)
|
| 366 |
+
blen = Py_SIZE(data)
|
| 367 |
+
else:
|
| 368 |
+
PyObject_GetBuffer(data, &py_buf, PyBUF_SIMPLE)
|
| 369 |
+
used_buf = 1
|
| 370 |
+
buf = py_buf.buf
|
| 371 |
+
blen = py_buf.len
|
| 372 |
+
|
| 373 |
+
if blen == 0:
|
| 374 |
+
# Empty data, do nothing.
|
| 375 |
+
return 0
|
| 376 |
+
|
| 377 |
+
fd = self._fileno()
|
| 378 |
+
# Use `unistd.h/write` directly, it's faster than
|
| 379 |
+
# uv_try_write -- less layers of code. The error
|
| 380 |
+
# checking logic is copied from libuv.
|
| 381 |
+
written = system.write(fd, buf, blen)
|
| 382 |
+
while written == -1 and (
|
| 383 |
+
errno.errno == errno.EINTR or
|
| 384 |
+
(system.PLATFORM_IS_APPLE and
|
| 385 |
+
errno.errno == errno.EPROTOTYPE)):
|
| 386 |
+
# From libuv code (unix/stream.c):
|
| 387 |
+
# Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
|
| 388 |
+
# EPROTOTYPE can be returned while trying to write to a socket
|
| 389 |
+
# that is shutting down. If we retry the write, we should get
|
| 390 |
+
# the expected EPIPE instead.
|
| 391 |
+
written = system.write(fd, buf, blen)
|
| 392 |
+
saved_errno = errno.errno
|
| 393 |
+
|
| 394 |
+
if used_buf:
|
| 395 |
+
PyBuffer_Release(&py_buf)
|
| 396 |
+
|
| 397 |
+
if written < 0:
|
| 398 |
+
if saved_errno == errno.EAGAIN or \
|
| 399 |
+
saved_errno == system.EWOULDBLOCK:
|
| 400 |
+
return -1
|
| 401 |
+
else:
|
| 402 |
+
exc = convert_error(-saved_errno)
|
| 403 |
+
self._fatal_error(exc, True)
|
| 404 |
+
return
|
| 405 |
+
|
| 406 |
+
if UVLOOP_DEBUG:
|
| 407 |
+
self._loop._debug_stream_write_tries += 1
|
| 408 |
+
|
| 409 |
+
if <size_t>written == blen:
|
| 410 |
+
return 0
|
| 411 |
+
|
| 412 |
+
return written
|
| 413 |
+
|
| 414 |
+
cdef inline _buffer_write(self, object data):
|
| 415 |
+
cdef int dlen
|
| 416 |
+
|
| 417 |
+
if not PyBytes_CheckExact(data):
|
| 418 |
+
data = memoryview(data).cast('b')
|
| 419 |
+
|
| 420 |
+
dlen = len(data)
|
| 421 |
+
if not dlen:
|
| 422 |
+
return
|
| 423 |
+
|
| 424 |
+
self._buffer_size += dlen
|
| 425 |
+
self._buffer.append(data)
|
| 426 |
+
|
| 427 |
+
cdef inline _initiate_write(self):
|
| 428 |
+
if (not self._protocol_paused and
|
| 429 |
+
(<uv.uv_stream_t*>self._handle).write_queue_size == 0 and
|
| 430 |
+
self._buffer_size > self._high_water):
|
| 431 |
+
# Fast-path. If:
|
| 432 |
+
# - the protocol isn't yet paused,
|
| 433 |
+
# - there is no data in libuv buffers for this stream,
|
| 434 |
+
# - the protocol will be paused if we continue to buffer data
|
| 435 |
+
#
|
| 436 |
+
# Then:
|
| 437 |
+
# - Try to write all buffered data right now.
|
| 438 |
+
all_sent = self._exec_write()
|
| 439 |
+
if UVLOOP_DEBUG:
|
| 440 |
+
if self._buffer_size != 0 or self._buffer != []:
|
| 441 |
+
raise RuntimeError(
|
| 442 |
+
'_buffer_size is not 0 after a successful _exec_write')
|
| 443 |
+
|
| 444 |
+
# There is no need to call `_queue_write` anymore,
|
| 445 |
+
# as `uv_write` should be called already.
|
| 446 |
+
|
| 447 |
+
if not all_sent:
|
| 448 |
+
# If not all of the data was sent successfully,
|
| 449 |
+
# we might need to pause the protocol.
|
| 450 |
+
self._maybe_pause_protocol()
|
| 451 |
+
|
| 452 |
+
elif self._buffer_size > 0:
|
| 453 |
+
self._maybe_pause_protocol()
|
| 454 |
+
self._loop._queue_write(self)
|
| 455 |
+
|
| 456 |
+
cdef inline _exec_write(self):
|
| 457 |
+
cdef:
|
| 458 |
+
int err
|
| 459 |
+
int buf_len
|
| 460 |
+
_StreamWriteContext ctx = None
|
| 461 |
+
|
| 462 |
+
if self._closed:
|
| 463 |
+
# If the handle is closed, just return, it's too
|
| 464 |
+
# late to do anything.
|
| 465 |
+
return
|
| 466 |
+
|
| 467 |
+
buf_len = len(self._buffer)
|
| 468 |
+
if not buf_len:
|
| 469 |
+
return
|
| 470 |
+
|
| 471 |
+
if (<uv.uv_stream_t*>self._handle).write_queue_size == 0:
|
| 472 |
+
# libuv internal write buffers for this stream are empty.
|
| 473 |
+
if buf_len == 1:
|
| 474 |
+
# If we only have one piece of data to send, let's
|
| 475 |
+
# use our fast implementation of try_write.
|
| 476 |
+
data = self._buffer[0]
|
| 477 |
+
sent = self._try_write(data)
|
| 478 |
+
|
| 479 |
+
if sent is None:
|
| 480 |
+
# A `self._fatal_error` was called.
|
| 481 |
+
# It might not raise an exception under some
|
| 482 |
+
# conditions.
|
| 483 |
+
self._buffer_size = 0
|
| 484 |
+
self._buffer.clear()
|
| 485 |
+
if not self._closing:
|
| 486 |
+
# This should never happen.
|
| 487 |
+
raise RuntimeError(
|
| 488 |
+
'stream is open after UVStream._try_write '
|
| 489 |
+
'returned None')
|
| 490 |
+
return
|
| 491 |
+
|
| 492 |
+
if sent == 0:
|
| 493 |
+
# All data was successfully written.
|
| 494 |
+
self._buffer_size = 0
|
| 495 |
+
self._buffer.clear()
|
| 496 |
+
# on_write will call "maybe_resume_protocol".
|
| 497 |
+
self._on_write()
|
| 498 |
+
return True
|
| 499 |
+
|
| 500 |
+
if sent > 0:
|
| 501 |
+
if UVLOOP_DEBUG:
|
| 502 |
+
if sent == len(data):
|
| 503 |
+
raise RuntimeError(
|
| 504 |
+
'_try_write sent all data and returned '
|
| 505 |
+
'non-zero')
|
| 506 |
+
|
| 507 |
+
if PyBytes_CheckExact(data):
|
| 508 |
+
# Cast bytes to memoryview to avoid copying
|
| 509 |
+
# data that wasn't sent.
|
| 510 |
+
data = memoryview(data)
|
| 511 |
+
data = data[sent:]
|
| 512 |
+
|
| 513 |
+
self._buffer_size -= sent
|
| 514 |
+
self._buffer[0] = data
|
| 515 |
+
|
| 516 |
+
# At this point it's either data was sent partially,
|
| 517 |
+
# or an EAGAIN has happened.
|
| 518 |
+
|
| 519 |
+
else:
|
| 520 |
+
ctx = _StreamWriteContext.new(self, self._buffer)
|
| 521 |
+
|
| 522 |
+
err = uv.uv_try_write(<uv.uv_stream_t*>self._handle,
|
| 523 |
+
ctx.uv_bufs_start,
|
| 524 |
+
ctx.uv_bufs_len)
|
| 525 |
+
|
| 526 |
+
if err > 0:
|
| 527 |
+
# Some data was successfully sent.
|
| 528 |
+
|
| 529 |
+
if <size_t>err == self._buffer_size:
|
| 530 |
+
# Everything was sent.
|
| 531 |
+
ctx.close()
|
| 532 |
+
self._buffer.clear()
|
| 533 |
+
self._buffer_size = 0
|
| 534 |
+
# on_write will call "maybe_resume_protocol".
|
| 535 |
+
self._on_write()
|
| 536 |
+
return True
|
| 537 |
+
|
| 538 |
+
try:
|
| 539 |
+
# Advance pointers to uv_bufs in `ctx`,
|
| 540 |
+
# we will reuse it soon for a uv_write
|
| 541 |
+
# call.
|
| 542 |
+
ctx.advance_uv_buf(<ssize_t>err)
|
| 543 |
+
except Exception as ex: # This should never happen.
|
| 544 |
+
# Let's try to close the `ctx` anyways.
|
| 545 |
+
ctx.close()
|
| 546 |
+
self._fatal_error(ex, True)
|
| 547 |
+
self._buffer.clear()
|
| 548 |
+
self._buffer_size = 0
|
| 549 |
+
return
|
| 550 |
+
|
| 551 |
+
elif err != uv.UV_EAGAIN:
|
| 552 |
+
ctx.close()
|
| 553 |
+
exc = convert_error(err)
|
| 554 |
+
self._fatal_error(exc, True)
|
| 555 |
+
self._buffer.clear()
|
| 556 |
+
self._buffer_size = 0
|
| 557 |
+
return
|
| 558 |
+
|
| 559 |
+
# fall through
|
| 560 |
+
|
| 561 |
+
if ctx is None:
|
| 562 |
+
ctx = _StreamWriteContext.new(self, self._buffer)
|
| 563 |
+
|
| 564 |
+
err = uv.uv_write(&ctx.req,
|
| 565 |
+
<uv.uv_stream_t*>self._handle,
|
| 566 |
+
ctx.uv_bufs_start,
|
| 567 |
+
ctx.uv_bufs_len,
|
| 568 |
+
__uv_stream_on_write)
|
| 569 |
+
|
| 570 |
+
self._buffer_size = 0
|
| 571 |
+
# Can't use `_buffer.clear()` here: `ctx` holds a reference to
|
| 572 |
+
# the `_buffer`.
|
| 573 |
+
self._buffer = []
|
| 574 |
+
|
| 575 |
+
if err < 0:
|
| 576 |
+
# close write context
|
| 577 |
+
ctx.close()
|
| 578 |
+
|
| 579 |
+
exc = convert_error(err)
|
| 580 |
+
self._fatal_error(exc, True)
|
| 581 |
+
return
|
| 582 |
+
|
| 583 |
+
self._maybe_resume_protocol()
|
| 584 |
+
|
| 585 |
+
cdef size_t _get_write_buffer_size(self):
|
| 586 |
+
if self._handle is NULL:
|
| 587 |
+
return 0
|
| 588 |
+
return ((<uv.uv_stream_t*>self._handle).write_queue_size +
|
| 589 |
+
self._buffer_size)
|
| 590 |
+
|
| 591 |
+
cdef _close(self):
|
| 592 |
+
try:
|
| 593 |
+
if self._read_pybuf_acquired:
|
| 594 |
+
# Should never happen. libuv always calls uv_alloc/uv_read
|
| 595 |
+
# in pairs.
|
| 596 |
+
self._loop.call_exception_handler({
|
| 597 |
+
'transport': self,
|
| 598 |
+
'message': 'XXX: an allocated buffer in transport._close()'
|
| 599 |
+
})
|
| 600 |
+
self._read_pybuf_acquired = 0
|
| 601 |
+
PyBuffer_Release(&self._read_pybuf)
|
| 602 |
+
|
| 603 |
+
self._stop_reading()
|
| 604 |
+
finally:
|
| 605 |
+
UVSocketHandle._close(<UVHandle>self)
|
| 606 |
+
|
| 607 |
+
cdef inline _on_accept(self):
|
| 608 |
+
# Ultimately called by __uv_stream_on_listen.
|
| 609 |
+
self._init_protocol()
|
| 610 |
+
|
| 611 |
+
cdef inline _on_eof(self):
|
| 612 |
+
# Any exception raised here will be caught in
|
| 613 |
+
# __uv_stream_on_read.
|
| 614 |
+
|
| 615 |
+
try:
|
| 616 |
+
meth = self._protocol.eof_received
|
| 617 |
+
except AttributeError:
|
| 618 |
+
keep_open = False
|
| 619 |
+
else:
|
| 620 |
+
keep_open = run_in_context(self.context, meth)
|
| 621 |
+
|
| 622 |
+
if keep_open:
|
| 623 |
+
# We're keeping the connection open so the
|
| 624 |
+
# protocol can write more, but we still can't
|
| 625 |
+
# receive more, so remove the reader callback.
|
| 626 |
+
self._stop_reading()
|
| 627 |
+
else:
|
| 628 |
+
self.close()
|
| 629 |
+
|
| 630 |
+
cdef inline _on_write(self):
|
| 631 |
+
self._maybe_resume_protocol()
|
| 632 |
+
if not self._get_write_buffer_size():
|
| 633 |
+
if self._closing:
|
| 634 |
+
self._schedule_call_connection_lost(None)
|
| 635 |
+
elif self._eof:
|
| 636 |
+
self._shutdown()
|
| 637 |
+
|
| 638 |
+
cdef inline _init(self, Loop loop, object protocol, Server server,
|
| 639 |
+
object waiter, object context):
|
| 640 |
+
self.context = context
|
| 641 |
+
self._set_protocol(protocol)
|
| 642 |
+
self._start_init(loop)
|
| 643 |
+
|
| 644 |
+
if server is not None:
|
| 645 |
+
self._set_server(server)
|
| 646 |
+
|
| 647 |
+
if waiter is not None:
|
| 648 |
+
self._set_waiter(waiter)
|
| 649 |
+
|
| 650 |
+
cdef inline _on_connect(self, object exc):
|
| 651 |
+
# Called from __tcp_connect_callback (tcp.pyx) and
|
| 652 |
+
# __pipe_connect_callback (pipe.pyx).
|
| 653 |
+
if exc is None:
|
| 654 |
+
self._init_protocol()
|
| 655 |
+
else:
|
| 656 |
+
if self._waiter is None:
|
| 657 |
+
self._fatal_error(exc, False, "connect failed")
|
| 658 |
+
elif self._waiter.cancelled():
|
| 659 |
+
# Connect call was cancelled; just close the transport
|
| 660 |
+
# silently.
|
| 661 |
+
self._close()
|
| 662 |
+
elif self._waiter.done():
|
| 663 |
+
self._fatal_error(exc, False, "connect failed")
|
| 664 |
+
else:
|
| 665 |
+
self._waiter.set_exception(exc)
|
| 666 |
+
self._close()
|
| 667 |
+
|
| 668 |
+
# === Public API ===
|
| 669 |
+
|
| 670 |
+
def __repr__(self):
|
| 671 |
+
return '<{} closed={} reading={} {:#x}>'.format(
|
| 672 |
+
self.__class__.__name__,
|
| 673 |
+
self._closed,
|
| 674 |
+
self.__reading,
|
| 675 |
+
id(self))
|
| 676 |
+
|
| 677 |
+
def write(self, object buf):
|
| 678 |
+
self._ensure_alive()
|
| 679 |
+
|
| 680 |
+
if self._eof:
|
| 681 |
+
raise RuntimeError('Cannot call write() after write_eof()')
|
| 682 |
+
if not buf:
|
| 683 |
+
return
|
| 684 |
+
if self._conn_lost:
|
| 685 |
+
self._conn_lost += 1
|
| 686 |
+
return
|
| 687 |
+
self._buffer_write(buf)
|
| 688 |
+
self._initiate_write()
|
| 689 |
+
|
| 690 |
+
def writelines(self, bufs):
|
| 691 |
+
self._ensure_alive()
|
| 692 |
+
|
| 693 |
+
if self._eof:
|
| 694 |
+
raise RuntimeError('Cannot call writelines() after write_eof()')
|
| 695 |
+
if self._conn_lost:
|
| 696 |
+
self._conn_lost += 1
|
| 697 |
+
return
|
| 698 |
+
for buf in bufs:
|
| 699 |
+
self._buffer_write(buf)
|
| 700 |
+
self._initiate_write()
|
| 701 |
+
|
| 702 |
+
def write_eof(self):
|
| 703 |
+
self._ensure_alive()
|
| 704 |
+
|
| 705 |
+
if self._eof:
|
| 706 |
+
return
|
| 707 |
+
|
| 708 |
+
self._eof = 1
|
| 709 |
+
if not self._get_write_buffer_size():
|
| 710 |
+
self._shutdown()
|
| 711 |
+
|
| 712 |
+
def can_write_eof(self):
|
| 713 |
+
return True
|
| 714 |
+
|
| 715 |
+
def is_reading(self):
|
| 716 |
+
return self._is_reading()
|
| 717 |
+
|
| 718 |
+
def pause_reading(self):
|
| 719 |
+
if self._closing or not self._is_reading():
|
| 720 |
+
return
|
| 721 |
+
self._stop_reading()
|
| 722 |
+
|
| 723 |
+
def resume_reading(self):
|
| 724 |
+
if self._is_reading() or self._closing:
|
| 725 |
+
return
|
| 726 |
+
self._start_reading()
|
| 727 |
+
|
| 728 |
+
|
| 729 |
+
cdef void __uv_stream_on_shutdown(uv.uv_shutdown_t* req,
|
| 730 |
+
int status) noexcept with gil:
|
| 731 |
+
|
| 732 |
+
# callback for uv_shutdown
|
| 733 |
+
|
| 734 |
+
if req.data is NULL:
|
| 735 |
+
aio_logger.error(
|
| 736 |
+
'UVStream.shutdown callback called with NULL req.data, status=%r',
|
| 737 |
+
status)
|
| 738 |
+
return
|
| 739 |
+
|
| 740 |
+
cdef UVStream stream = <UVStream> req.data
|
| 741 |
+
|
| 742 |
+
if status < 0 and status != uv.UV_ECANCELED:
|
| 743 |
+
# From libuv source code:
|
| 744 |
+
# The ECANCELED error code is a lie, the shutdown(2) syscall is a
|
| 745 |
+
# fait accompli at this point. Maybe we should revisit this in
|
| 746 |
+
# v0.11. A possible reason for leaving it unchanged is that it
|
| 747 |
+
# informs the callee that the handle has been destroyed.
|
| 748 |
+
|
| 749 |
+
if UVLOOP_DEBUG:
|
| 750 |
+
stream._loop._debug_stream_shutdown_errors_total += 1
|
| 751 |
+
|
| 752 |
+
exc = convert_error(status)
|
| 753 |
+
stream._fatal_error(
|
| 754 |
+
exc, False, "error status in uv_stream_t.shutdown callback")
|
| 755 |
+
return
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
cdef inline bint __uv_stream_on_read_common(
|
| 759 |
+
UVStream sc,
|
| 760 |
+
Loop loop,
|
| 761 |
+
ssize_t nread,
|
| 762 |
+
):
|
| 763 |
+
if sc._closed:
|
| 764 |
+
# The stream was closed, there is no reason to
|
| 765 |
+
# do any work now.
|
| 766 |
+
sc.__reading_stopped() # Just in case.
|
| 767 |
+
return True
|
| 768 |
+
|
| 769 |
+
if nread == uv.UV_EOF:
|
| 770 |
+
# From libuv docs:
|
| 771 |
+
# The callee is responsible for stopping closing the stream
|
| 772 |
+
# when an error happens by calling uv_read_stop() or uv_close().
|
| 773 |
+
# Trying to read from the stream again is undefined.
|
| 774 |
+
try:
|
| 775 |
+
if UVLOOP_DEBUG:
|
| 776 |
+
loop._debug_stream_read_eof_total += 1
|
| 777 |
+
|
| 778 |
+
sc._stop_reading()
|
| 779 |
+
sc._on_eof()
|
| 780 |
+
except BaseException as ex:
|
| 781 |
+
if UVLOOP_DEBUG:
|
| 782 |
+
loop._debug_stream_read_eof_cb_errors_total += 1
|
| 783 |
+
|
| 784 |
+
sc._fatal_error(ex, False)
|
| 785 |
+
finally:
|
| 786 |
+
return True
|
| 787 |
+
|
| 788 |
+
if nread == 0:
|
| 789 |
+
# From libuv docs:
|
| 790 |
+
# nread might be 0, which does not indicate an error or EOF.
|
| 791 |
+
# This is equivalent to EAGAIN or EWOULDBLOCK under read(2).
|
| 792 |
+
return True
|
| 793 |
+
|
| 794 |
+
if nread < 0:
|
| 795 |
+
# From libuv docs:
|
| 796 |
+
# The callee is responsible for stopping closing the stream
|
| 797 |
+
# when an error happens by calling uv_read_stop() or uv_close().
|
| 798 |
+
# Trying to read from the stream again is undefined.
|
| 799 |
+
#
|
| 800 |
+
# Therefore, we're closing the stream. Since "UVHandle._close()"
|
| 801 |
+
# doesn't raise exceptions unless uvloop is built with DEBUG=1,
|
| 802 |
+
# we don't need try...finally here.
|
| 803 |
+
|
| 804 |
+
if UVLOOP_DEBUG:
|
| 805 |
+
loop._debug_stream_read_errors_total += 1
|
| 806 |
+
|
| 807 |
+
if sc.__read_error_close:
|
| 808 |
+
# Used for getting notified when a pipe is closed.
|
| 809 |
+
# See WriteUnixTransport for the explanation.
|
| 810 |
+
sc._on_eof()
|
| 811 |
+
return True
|
| 812 |
+
|
| 813 |
+
exc = convert_error(nread)
|
| 814 |
+
sc._fatal_error(
|
| 815 |
+
exc, False, "error status in uv_stream_t.read callback")
|
| 816 |
+
return True
|
| 817 |
+
|
| 818 |
+
return False
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
cdef inline void __uv_stream_on_read_impl(
|
| 822 |
+
uv.uv_stream_t* stream,
|
| 823 |
+
ssize_t nread,
|
| 824 |
+
const uv.uv_buf_t* buf,
|
| 825 |
+
):
|
| 826 |
+
cdef:
|
| 827 |
+
UVStream sc = <UVStream>stream.data
|
| 828 |
+
Loop loop = sc._loop
|
| 829 |
+
|
| 830 |
+
# It's OK to free the buffer early, since nothing will
|
| 831 |
+
# be able to touch it until this method is done.
|
| 832 |
+
__loop_free_buffer(loop)
|
| 833 |
+
|
| 834 |
+
if __uv_stream_on_read_common(sc, loop, nread):
|
| 835 |
+
return
|
| 836 |
+
|
| 837 |
+
try:
|
| 838 |
+
if UVLOOP_DEBUG:
|
| 839 |
+
loop._debug_stream_read_cb_total += 1
|
| 840 |
+
|
| 841 |
+
run_in_context1(
|
| 842 |
+
sc.context,
|
| 843 |
+
sc._protocol_data_received,
|
| 844 |
+
loop._recv_buffer[:nread],
|
| 845 |
+
)
|
| 846 |
+
except BaseException as exc:
|
| 847 |
+
if UVLOOP_DEBUG:
|
| 848 |
+
loop._debug_stream_read_cb_errors_total += 1
|
| 849 |
+
|
| 850 |
+
sc._fatal_error(exc, False)
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
cdef inline void __uv_stream_on_write_impl(
|
| 854 |
+
uv.uv_write_t* req,
|
| 855 |
+
int status,
|
| 856 |
+
):
|
| 857 |
+
cdef:
|
| 858 |
+
_StreamWriteContext ctx = <_StreamWriteContext> req.data
|
| 859 |
+
UVStream stream = <UVStream>ctx.stream
|
| 860 |
+
|
| 861 |
+
ctx.close()
|
| 862 |
+
|
| 863 |
+
if stream._closed:
|
| 864 |
+
# The stream was closed, there is nothing to do.
|
| 865 |
+
# Even if there is an error, like EPIPE, there
|
| 866 |
+
# is no reason to report it.
|
| 867 |
+
return
|
| 868 |
+
|
| 869 |
+
if status < 0:
|
| 870 |
+
if UVLOOP_DEBUG:
|
| 871 |
+
stream._loop._debug_stream_write_errors_total += 1
|
| 872 |
+
|
| 873 |
+
exc = convert_error(status)
|
| 874 |
+
stream._fatal_error(
|
| 875 |
+
exc, False, "error status in uv_stream_t.write callback")
|
| 876 |
+
return
|
| 877 |
+
|
| 878 |
+
try:
|
| 879 |
+
stream._on_write()
|
| 880 |
+
except BaseException as exc:
|
| 881 |
+
if UVLOOP_DEBUG:
|
| 882 |
+
stream._loop._debug_stream_write_cb_errors_total += 1
|
| 883 |
+
|
| 884 |
+
stream._fatal_error(exc, False)
|
| 885 |
+
|
| 886 |
+
|
| 887 |
+
cdef void __uv_stream_on_read(
|
| 888 |
+
uv.uv_stream_t* stream,
|
| 889 |
+
ssize_t nread,
|
| 890 |
+
const uv.uv_buf_t* buf,
|
| 891 |
+
) noexcept with gil:
|
| 892 |
+
|
| 893 |
+
if __ensure_handle_data(<uv.uv_handle_t*>stream,
|
| 894 |
+
"UVStream read callback") == 0:
|
| 895 |
+
return
|
| 896 |
+
|
| 897 |
+
# Don't need try-finally, __uv_stream_on_read_impl is void
|
| 898 |
+
__uv_stream_on_read_impl(stream, nread, buf)
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
cdef void __uv_stream_on_write(
|
| 902 |
+
uv.uv_write_t* req,
|
| 903 |
+
int status,
|
| 904 |
+
) noexcept with gil:
|
| 905 |
+
|
| 906 |
+
if UVLOOP_DEBUG:
|
| 907 |
+
if req.data is NULL:
|
| 908 |
+
aio_logger.error(
|
| 909 |
+
'UVStream.write callback called with NULL req.data, status=%r',
|
| 910 |
+
status)
|
| 911 |
+
return
|
| 912 |
+
|
| 913 |
+
# Don't need try-finally, __uv_stream_on_write_impl is void
|
| 914 |
+
__uv_stream_on_write_impl(req, status)
|
| 915 |
+
|
| 916 |
+
|
| 917 |
+
cdef void __uv_stream_buffered_alloc(
|
| 918 |
+
uv.uv_handle_t* stream,
|
| 919 |
+
size_t suggested_size,
|
| 920 |
+
uv.uv_buf_t* uvbuf,
|
| 921 |
+
) noexcept with gil:
|
| 922 |
+
|
| 923 |
+
if __ensure_handle_data(<uv.uv_handle_t*>stream,
|
| 924 |
+
"UVStream alloc buffer callback") == 0:
|
| 925 |
+
return
|
| 926 |
+
|
| 927 |
+
cdef:
|
| 928 |
+
UVStream sc = <UVStream>stream.data
|
| 929 |
+
Loop loop = sc._loop
|
| 930 |
+
Py_buffer* pybuf = &sc._read_pybuf
|
| 931 |
+
int got_buf = 0
|
| 932 |
+
|
| 933 |
+
if sc._read_pybuf_acquired:
|
| 934 |
+
uvbuf.len = 0
|
| 935 |
+
uvbuf.base = NULL
|
| 936 |
+
return
|
| 937 |
+
|
| 938 |
+
sc._read_pybuf_acquired = 0
|
| 939 |
+
try:
|
| 940 |
+
buf = run_in_context1(
|
| 941 |
+
sc.context,
|
| 942 |
+
sc._protocol_get_buffer,
|
| 943 |
+
suggested_size,
|
| 944 |
+
)
|
| 945 |
+
PyObject_GetBuffer(buf, pybuf, PyBUF_WRITABLE)
|
| 946 |
+
got_buf = 1
|
| 947 |
+
except BaseException as exc:
|
| 948 |
+
# Can't call 'sc._fatal_error' or 'sc._close', libuv will SF.
|
| 949 |
+
# We'll do it later in __uv_stream_buffered_on_read when we
|
| 950 |
+
# receive UV_ENOBUFS.
|
| 951 |
+
uvbuf.len = 0
|
| 952 |
+
uvbuf.base = NULL
|
| 953 |
+
return
|
| 954 |
+
|
| 955 |
+
if not pybuf.len:
|
| 956 |
+
uvbuf.len = 0
|
| 957 |
+
uvbuf.base = NULL
|
| 958 |
+
if got_buf:
|
| 959 |
+
PyBuffer_Release(pybuf)
|
| 960 |
+
return
|
| 961 |
+
|
| 962 |
+
sc._read_pybuf_acquired = 1
|
| 963 |
+
uvbuf.base = <char*>pybuf.buf
|
| 964 |
+
uvbuf.len = pybuf.len
|
| 965 |
+
|
| 966 |
+
|
| 967 |
+
cdef void __uv_stream_buffered_on_read(
|
| 968 |
+
uv.uv_stream_t* stream,
|
| 969 |
+
ssize_t nread,
|
| 970 |
+
const uv.uv_buf_t* buf,
|
| 971 |
+
) noexcept with gil:
|
| 972 |
+
|
| 973 |
+
if __ensure_handle_data(<uv.uv_handle_t*>stream,
|
| 974 |
+
"UVStream buffered read callback") == 0:
|
| 975 |
+
return
|
| 976 |
+
|
| 977 |
+
cdef:
|
| 978 |
+
UVStream sc = <UVStream>stream.data
|
| 979 |
+
Loop loop = sc._loop
|
| 980 |
+
Py_buffer* pybuf = &sc._read_pybuf
|
| 981 |
+
|
| 982 |
+
if nread == uv.UV_ENOBUFS:
|
| 983 |
+
sc._fatal_error(
|
| 984 |
+
RuntimeError(
|
| 985 |
+
'unhandled error (or an empty buffer) in get_buffer()'),
|
| 986 |
+
False)
|
| 987 |
+
return
|
| 988 |
+
|
| 989 |
+
try:
|
| 990 |
+
if nread > 0 and not sc._read_pybuf_acquired:
|
| 991 |
+
# From libuv docs:
|
| 992 |
+
# nread is > 0 if there is data available or < 0 on error. When
|
| 993 |
+
# we’ve reached EOF, nread will be set to UV_EOF. When
|
| 994 |
+
# nread < 0, the buf parameter might not point to a valid
|
| 995 |
+
# buffer; in that case buf.len and buf.base are both set to 0.
|
| 996 |
+
raise RuntimeError(
|
| 997 |
+
f'no python buffer is allocated in on_read; nread={nread}')
|
| 998 |
+
|
| 999 |
+
if nread == 0:
|
| 1000 |
+
# From libuv docs:
|
| 1001 |
+
# nread might be 0, which does not indicate an error or EOF.
|
| 1002 |
+
# This is equivalent to EAGAIN or EWOULDBLOCK under read(2).
|
| 1003 |
+
return
|
| 1004 |
+
|
| 1005 |
+
if __uv_stream_on_read_common(sc, loop, nread):
|
| 1006 |
+
return
|
| 1007 |
+
|
| 1008 |
+
if UVLOOP_DEBUG:
|
| 1009 |
+
loop._debug_stream_read_cb_total += 1
|
| 1010 |
+
|
| 1011 |
+
run_in_context1(sc.context, sc._protocol_buffer_updated, nread)
|
| 1012 |
+
except BaseException as exc:
|
| 1013 |
+
if UVLOOP_DEBUG:
|
| 1014 |
+
loop._debug_stream_read_cb_errors_total += 1
|
| 1015 |
+
|
| 1016 |
+
sc._fatal_error(exc, False)
|
| 1017 |
+
finally:
|
| 1018 |
+
sc._read_pybuf_acquired = 0
|
| 1019 |
+
PyBuffer_Release(pybuf)
|
.venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pxd
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class UVStreamServer(UVSocketHandle):
|
| 2 |
+
cdef:
|
| 3 |
+
int backlog
|
| 4 |
+
object ssl
|
| 5 |
+
object ssl_handshake_timeout
|
| 6 |
+
object ssl_shutdown_timeout
|
| 7 |
+
object protocol_factory
|
| 8 |
+
bint opened
|
| 9 |
+
Server _server
|
| 10 |
+
|
| 11 |
+
# All "inline" methods are final
|
| 12 |
+
|
| 13 |
+
cdef inline _init(self, Loop loop, object protocol_factory,
|
| 14 |
+
Server server,
|
| 15 |
+
object backlog,
|
| 16 |
+
object ssl,
|
| 17 |
+
object ssl_handshake_timeout,
|
| 18 |
+
object ssl_shutdown_timeout)
|
| 19 |
+
|
| 20 |
+
cdef inline _mark_as_open(self)
|
| 21 |
+
|
| 22 |
+
cdef inline listen(self)
|
| 23 |
+
cdef inline _on_listen(self)
|
| 24 |
+
|
| 25 |
+
cdef UVStream _make_new_transport(self, object protocol, object waiter,
|
| 26 |
+
object context)
|
.venv/lib/python3.11/site-packages/uvloop/handles/streamserver.pyx
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@cython.no_gc_clear
|
| 2 |
+
cdef class UVStreamServer(UVSocketHandle):
|
| 3 |
+
|
| 4 |
+
def __cinit__(self):
|
| 5 |
+
self.opened = 0
|
| 6 |
+
self._server = None
|
| 7 |
+
self.ssl = None
|
| 8 |
+
self.ssl_handshake_timeout = None
|
| 9 |
+
self.ssl_shutdown_timeout = None
|
| 10 |
+
self.protocol_factory = None
|
| 11 |
+
|
| 12 |
+
cdef inline _init(self, Loop loop, object protocol_factory,
|
| 13 |
+
Server server,
|
| 14 |
+
object backlog,
|
| 15 |
+
object ssl,
|
| 16 |
+
object ssl_handshake_timeout,
|
| 17 |
+
object ssl_shutdown_timeout):
|
| 18 |
+
|
| 19 |
+
if not isinstance(backlog, int):
|
| 20 |
+
# Don't allow floats
|
| 21 |
+
raise TypeError('integer argument expected, got {}'.format(
|
| 22 |
+
type(backlog).__name__))
|
| 23 |
+
|
| 24 |
+
if ssl is not None:
|
| 25 |
+
if not isinstance(ssl, ssl_SSLContext):
|
| 26 |
+
raise TypeError(
|
| 27 |
+
'ssl is expected to be None or an instance of '
|
| 28 |
+
'ssl.SSLContext, got {!r}'.format(ssl))
|
| 29 |
+
else:
|
| 30 |
+
if ssl_handshake_timeout is not None:
|
| 31 |
+
raise ValueError(
|
| 32 |
+
'ssl_handshake_timeout is only meaningful with ssl')
|
| 33 |
+
if ssl_shutdown_timeout is not None:
|
| 34 |
+
raise ValueError(
|
| 35 |
+
'ssl_shutdown_timeout is only meaningful with ssl')
|
| 36 |
+
|
| 37 |
+
self.backlog = backlog
|
| 38 |
+
self.ssl = ssl
|
| 39 |
+
self.ssl_handshake_timeout = ssl_handshake_timeout
|
| 40 |
+
self.ssl_shutdown_timeout = ssl_shutdown_timeout
|
| 41 |
+
|
| 42 |
+
self._start_init(loop)
|
| 43 |
+
self.protocol_factory = protocol_factory
|
| 44 |
+
self._server = server
|
| 45 |
+
|
| 46 |
+
cdef inline listen(self):
|
| 47 |
+
cdef int err
|
| 48 |
+
self._ensure_alive()
|
| 49 |
+
|
| 50 |
+
if self.protocol_factory is None:
|
| 51 |
+
raise RuntimeError('unable to listen(); no protocol_factory')
|
| 52 |
+
|
| 53 |
+
if self.opened != 1:
|
| 54 |
+
raise RuntimeError('unopened TCPServer')
|
| 55 |
+
|
| 56 |
+
self.context = Context_CopyCurrent()
|
| 57 |
+
|
| 58 |
+
err = uv.uv_listen(<uv.uv_stream_t*> self._handle,
|
| 59 |
+
self.backlog,
|
| 60 |
+
__uv_streamserver_on_listen)
|
| 61 |
+
if err < 0:
|
| 62 |
+
exc = convert_error(err)
|
| 63 |
+
self._fatal_error(exc, True)
|
| 64 |
+
return
|
| 65 |
+
|
| 66 |
+
cdef inline _on_listen(self):
|
| 67 |
+
cdef UVStream client
|
| 68 |
+
|
| 69 |
+
protocol = run_in_context(self.context, self.protocol_factory)
|
| 70 |
+
|
| 71 |
+
if self.ssl is None:
|
| 72 |
+
client = self._make_new_transport(protocol, None, self.context)
|
| 73 |
+
|
| 74 |
+
else:
|
| 75 |
+
waiter = self._loop._new_future()
|
| 76 |
+
|
| 77 |
+
ssl_protocol = SSLProtocol(
|
| 78 |
+
self._loop, protocol, self.ssl,
|
| 79 |
+
waiter,
|
| 80 |
+
server_side=True,
|
| 81 |
+
server_hostname=None,
|
| 82 |
+
ssl_handshake_timeout=self.ssl_handshake_timeout,
|
| 83 |
+
ssl_shutdown_timeout=self.ssl_shutdown_timeout)
|
| 84 |
+
|
| 85 |
+
client = self._make_new_transport(ssl_protocol, None, self.context)
|
| 86 |
+
|
| 87 |
+
waiter.add_done_callback(
|
| 88 |
+
ft_partial(self.__on_ssl_connected, client))
|
| 89 |
+
|
| 90 |
+
client._accept(<UVStream>self)
|
| 91 |
+
|
| 92 |
+
cdef _fatal_error(self, exc, throw, reason=None):
|
| 93 |
+
# Overload UVHandle._fatal_error
|
| 94 |
+
|
| 95 |
+
self._close()
|
| 96 |
+
|
| 97 |
+
if not isinstance(exc, OSError):
|
| 98 |
+
|
| 99 |
+
if throw or self._loop is None:
|
| 100 |
+
raise exc
|
| 101 |
+
|
| 102 |
+
msg = f'Fatal error on server {self.__class__.__name__}'
|
| 103 |
+
if reason is not None:
|
| 104 |
+
msg = f'{msg} ({reason})'
|
| 105 |
+
|
| 106 |
+
self._loop.call_exception_handler({
|
| 107 |
+
'message': msg,
|
| 108 |
+
'exception': exc,
|
| 109 |
+
})
|
| 110 |
+
|
| 111 |
+
cdef inline _mark_as_open(self):
|
| 112 |
+
self.opened = 1
|
| 113 |
+
|
| 114 |
+
cdef UVStream _make_new_transport(self, object protocol, object waiter,
|
| 115 |
+
object context):
|
| 116 |
+
raise NotImplementedError
|
| 117 |
+
|
| 118 |
+
def __on_ssl_connected(self, transport, fut):
|
| 119 |
+
exc = fut.exception()
|
| 120 |
+
if exc is not None:
|
| 121 |
+
transport._force_close(exc)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
cdef void __uv_streamserver_on_listen(
|
| 125 |
+
uv.uv_stream_t* handle,
|
| 126 |
+
int status,
|
| 127 |
+
) noexcept with gil:
|
| 128 |
+
|
| 129 |
+
# callback for uv_listen
|
| 130 |
+
|
| 131 |
+
if __ensure_handle_data(<uv.uv_handle_t*>handle,
|
| 132 |
+
"UVStream listen callback") == 0:
|
| 133 |
+
return
|
| 134 |
+
|
| 135 |
+
cdef:
|
| 136 |
+
UVStreamServer stream = <UVStreamServer> handle.data
|
| 137 |
+
|
| 138 |
+
if status < 0:
|
| 139 |
+
if UVLOOP_DEBUG:
|
| 140 |
+
stream._loop._debug_stream_listen_errors_total += 1
|
| 141 |
+
|
| 142 |
+
exc = convert_error(status)
|
| 143 |
+
stream._fatal_error(
|
| 144 |
+
exc, False, "error status in uv_stream_t.listen callback")
|
| 145 |
+
return
|
| 146 |
+
|
| 147 |
+
try:
|
| 148 |
+
stream._on_listen()
|
| 149 |
+
except BaseException as exc:
|
| 150 |
+
stream._error(exc, False)
|
.venv/lib/python3.11/site-packages/uvloop/handles/tcp.pxd
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef class TCPServer(UVStreamServer):
|
| 2 |
+
cdef bind(self, system.sockaddr* addr, unsigned int flags=*)
|
| 3 |
+
|
| 4 |
+
@staticmethod
|
| 5 |
+
cdef TCPServer new(Loop loop, object protocol_factory, Server server,
|
| 6 |
+
unsigned int flags,
|
| 7 |
+
object backlog,
|
| 8 |
+
object ssl,
|
| 9 |
+
object ssl_handshake_timeout,
|
| 10 |
+
object ssl_shutdown_timeout)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
cdef class TCPTransport(UVStream):
|
| 14 |
+
cdef:
|
| 15 |
+
bint __peername_set
|
| 16 |
+
bint __sockname_set
|
| 17 |
+
system.sockaddr_storage __peername
|
| 18 |
+
system.sockaddr_storage __sockname
|
| 19 |
+
|
| 20 |
+
cdef bind(self, system.sockaddr* addr, unsigned int flags=*)
|
| 21 |
+
cdef connect(self, system.sockaddr* addr)
|
| 22 |
+
cdef _set_nodelay(self)
|
| 23 |
+
|
| 24 |
+
@staticmethod
|
| 25 |
+
cdef TCPTransport new(Loop loop, object protocol, Server server,
|
| 26 |
+
object waiter, object context)
|
.venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cdef __tcp_init_uv_handle(UVStream handle, Loop loop, unsigned int flags):
|
| 2 |
+
cdef int err
|
| 3 |
+
|
| 4 |
+
handle._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_tcp_t))
|
| 5 |
+
if handle._handle is NULL:
|
| 6 |
+
handle._abort_init()
|
| 7 |
+
raise MemoryError()
|
| 8 |
+
|
| 9 |
+
err = uv.uv_tcp_init_ex(handle._loop.uvloop,
|
| 10 |
+
<uv.uv_tcp_t*>handle._handle,
|
| 11 |
+
flags)
|
| 12 |
+
if err < 0:
|
| 13 |
+
handle._abort_init()
|
| 14 |
+
raise convert_error(err)
|
| 15 |
+
|
| 16 |
+
handle._finish_init()
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
cdef __tcp_bind(UVStream handle, system.sockaddr* addr, unsigned int flags):
|
| 20 |
+
cdef int err
|
| 21 |
+
err = uv.uv_tcp_bind(<uv.uv_tcp_t *>handle._handle,
|
| 22 |
+
addr, flags)
|
| 23 |
+
if err < 0:
|
| 24 |
+
exc = convert_error(err)
|
| 25 |
+
raise exc
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
cdef __tcp_open(UVStream handle, int sockfd):
|
| 29 |
+
cdef int err
|
| 30 |
+
err = uv.uv_tcp_open(<uv.uv_tcp_t *>handle._handle,
|
| 31 |
+
<uv.uv_os_sock_t>sockfd)
|
| 32 |
+
if err < 0:
|
| 33 |
+
exc = convert_error(err)
|
| 34 |
+
raise exc
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
cdef __tcp_get_socket(UVSocketHandle handle):
|
| 38 |
+
cdef:
|
| 39 |
+
int buf_len = sizeof(system.sockaddr_storage)
|
| 40 |
+
int fileno
|
| 41 |
+
int err
|
| 42 |
+
system.sockaddr_storage buf
|
| 43 |
+
|
| 44 |
+
fileno = handle._fileno()
|
| 45 |
+
|
| 46 |
+
err = uv.uv_tcp_getsockname(<uv.uv_tcp_t*>handle._handle,
|
| 47 |
+
<system.sockaddr*>&buf,
|
| 48 |
+
&buf_len)
|
| 49 |
+
if err < 0:
|
| 50 |
+
raise convert_error(err)
|
| 51 |
+
|
| 52 |
+
return PseudoSocket(buf.ss_family, uv.SOCK_STREAM, 0, fileno)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
@cython.no_gc_clear
|
| 56 |
+
cdef class TCPServer(UVStreamServer):
|
| 57 |
+
|
| 58 |
+
@staticmethod
|
| 59 |
+
cdef TCPServer new(Loop loop, object protocol_factory, Server server,
|
| 60 |
+
unsigned int flags,
|
| 61 |
+
object backlog,
|
| 62 |
+
object ssl,
|
| 63 |
+
object ssl_handshake_timeout,
|
| 64 |
+
object ssl_shutdown_timeout):
|
| 65 |
+
|
| 66 |
+
cdef TCPServer handle
|
| 67 |
+
handle = TCPServer.__new__(TCPServer)
|
| 68 |
+
handle._init(loop, protocol_factory, server, backlog,
|
| 69 |
+
ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
|
| 70 |
+
__tcp_init_uv_handle(<UVStream>handle, loop, flags)
|
| 71 |
+
return handle
|
| 72 |
+
|
| 73 |
+
cdef _new_socket(self):
|
| 74 |
+
return __tcp_get_socket(<UVSocketHandle>self)
|
| 75 |
+
|
| 76 |
+
cdef _open(self, int sockfd):
|
| 77 |
+
self._ensure_alive()
|
| 78 |
+
try:
|
| 79 |
+
__tcp_open(<UVStream>self, sockfd)
|
| 80 |
+
except Exception as exc:
|
| 81 |
+
self._fatal_error(exc, True)
|
| 82 |
+
else:
|
| 83 |
+
self._mark_as_open()
|
| 84 |
+
|
| 85 |
+
cdef bind(self, system.sockaddr* addr, unsigned int flags=0):
|
| 86 |
+
self._ensure_alive()
|
| 87 |
+
try:
|
| 88 |
+
__tcp_bind(<UVStream>self, addr, flags)
|
| 89 |
+
except Exception as exc:
|
| 90 |
+
self._fatal_error(exc, True)
|
| 91 |
+
else:
|
| 92 |
+
self._mark_as_open()
|
| 93 |
+
|
| 94 |
+
cdef UVStream _make_new_transport(self, object protocol, object waiter,
|
| 95 |
+
object context):
|
| 96 |
+
cdef TCPTransport tr
|
| 97 |
+
tr = TCPTransport.new(self._loop, protocol, self._server, waiter,
|
| 98 |
+
context)
|
| 99 |
+
return <UVStream>tr
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@cython.no_gc_clear
|
| 103 |
+
cdef class TCPTransport(UVStream):
|
| 104 |
+
|
| 105 |
+
@staticmethod
|
| 106 |
+
cdef TCPTransport new(Loop loop, object protocol, Server server,
|
| 107 |
+
object waiter, object context):
|
| 108 |
+
|
| 109 |
+
cdef TCPTransport handle
|
| 110 |
+
handle = TCPTransport.__new__(TCPTransport)
|
| 111 |
+
handle._init(loop, protocol, server, waiter, context)
|
| 112 |
+
__tcp_init_uv_handle(<UVStream>handle, loop, uv.AF_UNSPEC)
|
| 113 |
+
handle.__peername_set = 0
|
| 114 |
+
handle.__sockname_set = 0
|
| 115 |
+
handle._set_nodelay()
|
| 116 |
+
return handle
|
| 117 |
+
|
| 118 |
+
cdef _set_nodelay(self):
|
| 119 |
+
cdef int err
|
| 120 |
+
self._ensure_alive()
|
| 121 |
+
err = uv.uv_tcp_nodelay(<uv.uv_tcp_t*>self._handle, 1)
|
| 122 |
+
if err < 0:
|
| 123 |
+
raise convert_error(err)
|
| 124 |
+
|
| 125 |
+
cdef _call_connection_made(self):
|
| 126 |
+
# asyncio saves peername & sockname when transports are instantiated,
|
| 127 |
+
# so that they're accessible even after the transport is closed.
|
| 128 |
+
# We are doing the same thing here, except that we create Python
|
| 129 |
+
# objects lazily, on request in get_extra_info()
|
| 130 |
+
|
| 131 |
+
cdef:
|
| 132 |
+
int err
|
| 133 |
+
int buf_len
|
| 134 |
+
|
| 135 |
+
buf_len = sizeof(system.sockaddr_storage)
|
| 136 |
+
err = uv.uv_tcp_getsockname(<uv.uv_tcp_t*>self._handle,
|
| 137 |
+
<system.sockaddr*>&self.__sockname,
|
| 138 |
+
&buf_len)
|
| 139 |
+
if err >= 0:
|
| 140 |
+
# Ignore errors, this is an optional thing.
|
| 141 |
+
# If something serious is going on, the transport
|
| 142 |
+
# will crash later (in roughly the same way how
|
| 143 |
+
# an asyncio transport would.)
|
| 144 |
+
self.__sockname_set = 1
|
| 145 |
+
|
| 146 |
+
buf_len = sizeof(system.sockaddr_storage)
|
| 147 |
+
err = uv.uv_tcp_getpeername(<uv.uv_tcp_t*>self._handle,
|
| 148 |
+
<system.sockaddr*>&self.__peername,
|
| 149 |
+
&buf_len)
|
| 150 |
+
if err >= 0:
|
| 151 |
+
# Same as few lines above -- we don't really care
|
| 152 |
+
# about error case here.
|
| 153 |
+
self.__peername_set = 1
|
| 154 |
+
|
| 155 |
+
UVBaseTransport._call_connection_made(self)
|
| 156 |
+
|
| 157 |
+
def get_extra_info(self, name, default=None):
|
| 158 |
+
if name == 'sockname':
|
| 159 |
+
if self.__sockname_set:
|
| 160 |
+
return __convert_sockaddr_to_pyaddr(
|
| 161 |
+
<system.sockaddr*>&self.__sockname)
|
| 162 |
+
elif name == 'peername':
|
| 163 |
+
if self.__peername_set:
|
| 164 |
+
return __convert_sockaddr_to_pyaddr(
|
| 165 |
+
<system.sockaddr*>&self.__peername)
|
| 166 |
+
return super().get_extra_info(name, default)
|
| 167 |
+
|
| 168 |
+
cdef _new_socket(self):
|
| 169 |
+
return __tcp_get_socket(<UVSocketHandle>self)
|
| 170 |
+
|
| 171 |
+
cdef bind(self, system.sockaddr* addr, unsigned int flags=0):
|
| 172 |
+
self._ensure_alive()
|
| 173 |
+
__tcp_bind(<UVStream>self, addr, flags)
|
| 174 |
+
|
| 175 |
+
cdef _open(self, int sockfd):
|
| 176 |
+
self._ensure_alive()
|
| 177 |
+
__tcp_open(<UVStream>self, sockfd)
|
| 178 |
+
|
| 179 |
+
cdef connect(self, system.sockaddr* addr):
|
| 180 |
+
cdef _TCPConnectRequest req
|
| 181 |
+
req = _TCPConnectRequest(self._loop, self)
|
| 182 |
+
req.connect(addr)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
cdef class _TCPConnectRequest(UVRequest):
|
| 186 |
+
cdef:
|
| 187 |
+
TCPTransport transport
|
| 188 |
+
uv.uv_connect_t _req_data
|
| 189 |
+
|
| 190 |
+
def __cinit__(self, loop, transport):
|
| 191 |
+
self.request = <uv.uv_req_t*>&self._req_data
|
| 192 |
+
self.request.data = <void*>self
|
| 193 |
+
self.transport = transport
|
| 194 |
+
|
| 195 |
+
cdef connect(self, system.sockaddr* addr):
|
| 196 |
+
cdef int err
|
| 197 |
+
err = uv.uv_tcp_connect(<uv.uv_connect_t*>self.request,
|
| 198 |
+
<uv.uv_tcp_t*>self.transport._handle,
|
| 199 |
+
addr,
|
| 200 |
+
__tcp_connect_callback)
|
| 201 |
+
if err < 0:
|
| 202 |
+
exc = convert_error(err)
|
| 203 |
+
self.on_done()
|
| 204 |
+
raise exc
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
cdef void __tcp_connect_callback(
|
| 208 |
+
uv.uv_connect_t* req,
|
| 209 |
+
int status,
|
| 210 |
+
) noexcept with gil:
|
| 211 |
+
cdef:
|
| 212 |
+
_TCPConnectRequest wrapper
|
| 213 |
+
TCPTransport transport
|
| 214 |
+
|
| 215 |
+
wrapper = <_TCPConnectRequest> req.data
|
| 216 |
+
transport = wrapper.transport
|
| 217 |
+
|
| 218 |
+
if status < 0:
|
| 219 |
+
exc = convert_error(status)
|
| 220 |
+
else:
|
| 221 |
+
exc = None
|
| 222 |
+
|
| 223 |
+
try:
|
| 224 |
+
transport._on_connect(exc)
|
| 225 |
+
except BaseException as ex:
|
| 226 |
+
wrapper.transport._fatal_error(ex, False)
|
| 227 |
+
finally:
|
| 228 |
+
wrapper.on_done()
|