Doosik commited on
Commit
e1baff6
·
verified ·
1 Parent(s): afaee9e

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .claude/settings.local.json +7 -0
  2. .gitattributes +2 -0
  3. .gitignore +11 -0
  4. .python-version +1 -0
  5. .venv/.gitignore +1 -0
  6. .venv/CACHEDIR.TAG +1 -0
  7. .venv/Lib/site-packages/_distutils_hack/__init__.py +239 -0
  8. .venv/Lib/site-packages/_distutils_hack/override.py +1 -0
  9. .venv/Lib/site-packages/_virtualenv.pth +3 -0
  10. .venv/Lib/site-packages/_virtualenv.py +101 -0
  11. .venv/Lib/site-packages/_yaml/__init__.py +33 -0
  12. .venv/Lib/site-packages/anyio-4.14.1.dist-info/INSTALLER +1 -0
  13. .venv/Lib/site-packages/anyio-4.14.1.dist-info/METADATA +107 -0
  14. .venv/Lib/site-packages/anyio-4.14.1.dist-info/RECORD +54 -0
  15. .venv/Lib/site-packages/anyio-4.14.1.dist-info/REQUESTED +0 -0
  16. .venv/Lib/site-packages/anyio-4.14.1.dist-info/WHEEL +5 -0
  17. .venv/Lib/site-packages/anyio-4.14.1.dist-info/entry_points.txt +2 -0
  18. .venv/Lib/site-packages/anyio-4.14.1.dist-info/licenses/LICENSE +20 -0
  19. .venv/Lib/site-packages/anyio-4.14.1.dist-info/scm_file_list.json +119 -0
  20. .venv/Lib/site-packages/anyio-4.14.1.dist-info/scm_version.json +8 -0
  21. .venv/Lib/site-packages/anyio-4.14.1.dist-info/top_level.txt +1 -0
  22. .venv/Lib/site-packages/anyio/__init__.py +115 -0
  23. .venv/Lib/site-packages/anyio/_backends/__init__.py +0 -0
  24. .venv/Lib/site-packages/anyio/_backends/_asyncio.py +0 -0
  25. .venv/Lib/site-packages/anyio/_backends/_trio.py +1456 -0
  26. .venv/Lib/site-packages/anyio/_core/__init__.py +0 -0
  27. .venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py +167 -0
  28. .venv/Lib/site-packages/anyio/_core/_contextmanagers.py +200 -0
  29. .venv/Lib/site-packages/anyio/_core/_eventloop.py +240 -0
  30. .venv/Lib/site-packages/anyio/_core/_exceptions.py +177 -0
  31. .venv/Lib/site-packages/anyio/_core/_fileio.py +960 -0
  32. .venv/Lib/site-packages/anyio/_core/_resources.py +18 -0
  33. .venv/Lib/site-packages/anyio/_core/_signals.py +29 -0
  34. .venv/Lib/site-packages/anyio/_core/_sockets.py +1011 -0
  35. .venv/Lib/site-packages/anyio/_core/_streams.py +52 -0
  36. .venv/Lib/site-packages/anyio/_core/_subprocesses.py +196 -0
  37. .venv/Lib/site-packages/anyio/_core/_synchronization.py +772 -0
  38. .venv/Lib/site-packages/anyio/_core/_tasks.py +415 -0
  39. .venv/Lib/site-packages/anyio/_core/_tempfile.py +613 -0
  40. .venv/Lib/site-packages/anyio/_core/_testing.py +82 -0
  41. .venv/Lib/site-packages/anyio/_core/_typedattr.py +81 -0
  42. .venv/Lib/site-packages/anyio/abc/__init__.py +58 -0
  43. .venv/Lib/site-packages/anyio/abc/_eventloop.py +410 -0
  44. .venv/Lib/site-packages/anyio/abc/_resources.py +33 -0
  45. .venv/Lib/site-packages/anyio/abc/_sockets.py +399 -0
  46. .venv/Lib/site-packages/anyio/abc/_streams.py +233 -0
  47. .venv/Lib/site-packages/anyio/abc/_subprocesses.py +79 -0
  48. .venv/Lib/site-packages/anyio/abc/_tasks.py +209 -0
  49. .venv/Lib/site-packages/anyio/abc/_testing.py +73 -0
  50. .venv/Lib/site-packages/anyio/from_thread.py +582 -0
.claude/settings.local.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(.venv/Scripts/python.exe -c ' *)"
5
+ ]
6
+ }
7
+ }
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ .venv/Lib/site-packages/functorch/_C.cp313-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
37
+ .venv/Lib/site-packages/hf_xet/hf_xet.pyd filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ PROMPT.md
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.13
.venv/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *
.venv/CACHEDIR.TAG ADDED
@@ -0,0 +1 @@
 
 
1
+ Signature: 8a477f597d28d172789f06886806bc55
.venv/Lib/site-packages/_distutils_hack/__init__.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # don't import any costly modules
2
+ import os
3
+ import sys
4
+
5
+ report_url = (
6
+ "https://github.com/pypa/setuptools/issues/new?template=distutils-deprecation.yml"
7
+ )
8
+
9
+
10
+ def warn_distutils_present():
11
+ if 'distutils' not in sys.modules:
12
+ return
13
+ import warnings
14
+
15
+ warnings.warn(
16
+ "Distutils was imported before Setuptools, but importing Setuptools "
17
+ "also replaces the `distutils` module in `sys.modules`. This may lead "
18
+ "to undesirable behaviors or errors. To avoid these issues, avoid "
19
+ "using distutils directly, ensure that setuptools is installed in the "
20
+ "traditional way (e.g. not an editable install), and/or make sure "
21
+ "that setuptools is always imported before distutils."
22
+ )
23
+
24
+
25
+ def clear_distutils():
26
+ if 'distutils' not in sys.modules:
27
+ return
28
+ import warnings
29
+
30
+ warnings.warn(
31
+ "Setuptools is replacing distutils. Support for replacing "
32
+ "an already imported distutils is deprecated. In the future, "
33
+ "this condition will fail. "
34
+ f"Register concerns at {report_url}"
35
+ )
36
+ mods = [
37
+ name
38
+ for name in sys.modules
39
+ if name == "distutils" or name.startswith("distutils.")
40
+ ]
41
+ for name in mods:
42
+ del sys.modules[name]
43
+
44
+
45
+ def enabled():
46
+ """
47
+ Allow selection of distutils by environment variable.
48
+ """
49
+ which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
50
+ if which == 'stdlib':
51
+ import warnings
52
+
53
+ warnings.warn(
54
+ "Reliance on distutils from stdlib is deprecated. Users "
55
+ "must rely on setuptools to provide the distutils module. "
56
+ "Avoid importing distutils or import setuptools first, "
57
+ "and avoid setting SETUPTOOLS_USE_DISTUTILS=stdlib. "
58
+ f"Register concerns at {report_url}"
59
+ )
60
+ return which == 'local'
61
+
62
+
63
+ def ensure_local_distutils():
64
+ import importlib
65
+
66
+ clear_distutils()
67
+
68
+ # With the DistutilsMetaFinder in place,
69
+ # perform an import to cause distutils to be
70
+ # loaded from setuptools._distutils. Ref #2906.
71
+ with shim():
72
+ importlib.import_module('distutils')
73
+
74
+ # check that submodules load as expected
75
+ core = importlib.import_module('distutils.core')
76
+ assert '_distutils' in core.__file__, core.__file__
77
+ assert 'setuptools._distutils.log' not in sys.modules
78
+
79
+
80
+ def do_override():
81
+ """
82
+ Ensure that the local copy of distutils is preferred over stdlib.
83
+
84
+ See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
85
+ for more motivation.
86
+ """
87
+ if enabled():
88
+ warn_distutils_present()
89
+ ensure_local_distutils()
90
+
91
+
92
+ class _TrivialRe:
93
+ def __init__(self, *patterns) -> None:
94
+ self._patterns = patterns
95
+
96
+ def match(self, string):
97
+ return all(pat in string for pat in self._patterns)
98
+
99
+
100
+ class DistutilsMetaFinder:
101
+ def find_spec(self, fullname, path, target=None):
102
+ # optimization: only consider top level modules and those
103
+ # found in the CPython test suite.
104
+ if path is not None and not fullname.startswith('test.'):
105
+ return None
106
+
107
+ method_name = 'spec_for_{fullname}'.format(**locals())
108
+ method = getattr(self, method_name, lambda: None)
109
+ return method()
110
+
111
+ def spec_for_distutils(self):
112
+ if self.is_cpython():
113
+ return None
114
+
115
+ import importlib
116
+ import importlib.abc
117
+ import importlib.util
118
+
119
+ try:
120
+ mod = importlib.import_module('setuptools._distutils')
121
+ except Exception:
122
+ # There are a couple of cases where setuptools._distutils
123
+ # may not be present:
124
+ # - An older Setuptools without a local distutils is
125
+ # taking precedence. Ref #2957.
126
+ # - Path manipulation during sitecustomize removes
127
+ # setuptools from the path but only after the hook
128
+ # has been loaded. Ref #2980.
129
+ # In either case, fall back to stdlib behavior.
130
+ return None
131
+
132
+ class DistutilsLoader(importlib.abc.Loader):
133
+ def create_module(self, spec):
134
+ mod.__name__ = 'distutils'
135
+ return mod
136
+
137
+ def exec_module(self, module):
138
+ pass
139
+
140
+ return importlib.util.spec_from_loader(
141
+ 'distutils', DistutilsLoader(), origin=mod.__file__
142
+ )
143
+
144
+ @staticmethod
145
+ def is_cpython():
146
+ """
147
+ Suppress supplying distutils for CPython (build and tests).
148
+ Ref #2965 and #3007.
149
+ """
150
+ return os.path.isfile('pybuilddir.txt')
151
+
152
+ def spec_for_pip(self):
153
+ """
154
+ Ensure stdlib distutils when running under pip.
155
+ See pypa/pip#8761 for rationale.
156
+ """
157
+ if sys.version_info >= (3, 12) or self.pip_imported_during_build():
158
+ return
159
+ clear_distutils()
160
+ self.spec_for_distutils = lambda: None
161
+
162
+ @classmethod
163
+ def pip_imported_during_build(cls):
164
+ """
165
+ Detect if pip is being imported in a build script. Ref #2355.
166
+ """
167
+ import traceback
168
+
169
+ return any(
170
+ cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
171
+ )
172
+
173
+ @staticmethod
174
+ def frame_file_is_setup(frame):
175
+ """
176
+ Return True if the indicated frame suggests a setup.py file.
177
+ """
178
+ # some frames may not have __file__ (#2940)
179
+ return frame.f_globals.get('__file__', '').endswith('setup.py')
180
+
181
+ def spec_for_sensitive_tests(self):
182
+ """
183
+ Ensure stdlib distutils when running select tests under CPython.
184
+
185
+ python/cpython#91169
186
+ """
187
+ clear_distutils()
188
+ self.spec_for_distutils = lambda: None
189
+
190
+ sensitive_tests = (
191
+ [
192
+ 'test.test_distutils',
193
+ 'test.test_peg_generator',
194
+ 'test.test_importlib',
195
+ ]
196
+ if sys.version_info < (3, 10)
197
+ else [
198
+ 'test.test_distutils',
199
+ ]
200
+ )
201
+
202
+
203
+ for name in DistutilsMetaFinder.sensitive_tests:
204
+ setattr(
205
+ DistutilsMetaFinder,
206
+ f'spec_for_{name}',
207
+ DistutilsMetaFinder.spec_for_sensitive_tests,
208
+ )
209
+
210
+
211
+ DISTUTILS_FINDER = DistutilsMetaFinder()
212
+
213
+
214
+ def add_shim():
215
+ DISTUTILS_FINDER in sys.meta_path or insert_shim()
216
+
217
+
218
+ class shim:
219
+ def __enter__(self) -> None:
220
+ insert_shim()
221
+
222
+ def __exit__(self, exc: object, value: object, tb: object) -> None:
223
+ _remove_shim()
224
+
225
+
226
+ def insert_shim():
227
+ sys.meta_path.insert(0, DISTUTILS_FINDER)
228
+
229
+
230
+ def _remove_shim():
231
+ try:
232
+ sys.meta_path.remove(DISTUTILS_FINDER)
233
+ except ValueError:
234
+ pass
235
+
236
+
237
+ if sys.version_info < (3, 12):
238
+ # DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632)
239
+ remove_shim = _remove_shim
.venv/Lib/site-packages/_distutils_hack/override.py ADDED
@@ -0,0 +1 @@
 
 
1
+ __import__('_distutils_hack').do_override()
.venv/Lib/site-packages/_virtualenv.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:69ac3d8f27e679c81b94ab30b3b56e9cd138219b1ba94a1fa3606d5a76a1433d
3
+ size 18
.venv/Lib/site-packages/_virtualenv.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Patches that are applied at runtime to the virtual environment."""
2
+
3
+ import os
4
+ import sys
5
+
6
+ VIRTUALENV_PATCH_FILE = os.path.join(__file__)
7
+
8
+
9
+ def patch_dist(dist):
10
+ """
11
+ Distutils allows user to configure some arguments via a configuration file:
12
+ https://docs.python.org/3.11/install/index.html#distutils-configuration-files.
13
+
14
+ Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.
15
+ """ # noqa: D205
16
+ # we cannot allow some install config as that would get packages installed outside of the virtual environment
17
+ old_parse_config_files = dist.Distribution.parse_config_files
18
+
19
+ def parse_config_files(self, *args, **kwargs):
20
+ result = old_parse_config_files(self, *args, **kwargs)
21
+ install = self.get_option_dict("install")
22
+
23
+ if "prefix" in install: # the prefix governs where to install the libraries
24
+ install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
25
+ for base in ("purelib", "platlib", "headers", "scripts", "data"):
26
+ key = f"install_{base}"
27
+ if key in install: # do not allow global configs to hijack venv paths
28
+ install.pop(key, None)
29
+ return result
30
+
31
+ dist.Distribution.parse_config_files = parse_config_files
32
+
33
+
34
+ # Import hook that patches some modules to ignore configuration values that break package installation in case
35
+ # of virtual environments.
36
+ _DISTUTILS_PATCH = "distutils.dist", "setuptools.dist"
37
+ # https://docs.python.org/3/library/importlib.html#setting-up-an-importer
38
+
39
+
40
+ class _Finder:
41
+ """A meta path finder that allows patching the imported distutils modules."""
42
+
43
+ fullname = None
44
+
45
+ # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup,
46
+ # because there are gevent-based applications that need to be first to import threading by themselves.
47
+ # See https://github.com/pypa/virtualenv/issues/1895 for details.
48
+ lock = [] # noqa: RUF012
49
+
50
+ def find_spec(self, fullname, path, target=None): # noqa: ARG002
51
+ if fullname in _DISTUTILS_PATCH and self.fullname is None:
52
+ # initialize lock[0] lazily
53
+ if len(self.lock) == 0:
54
+ import threading
55
+
56
+ lock = threading.Lock()
57
+ # there is possibility that two threads T1 and T2 are simultaneously running into find_spec,
58
+ # observing .lock as empty, and further going into hereby initialization. However due to the GIL,
59
+ # list.append() operation is atomic and this way only one of the threads will "win" to put the lock
60
+ # - that every thread will use - into .lock[0].
61
+ # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
62
+ self.lock.append(lock)
63
+
64
+ from functools import partial
65
+ from importlib.util import find_spec
66
+
67
+ with self.lock[0]:
68
+ self.fullname = fullname
69
+ try:
70
+ spec = find_spec(fullname, path)
71
+ if spec is not None:
72
+ # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work
73
+ is_new_api = hasattr(spec.loader, "exec_module")
74
+ func_name = "exec_module" if is_new_api else "load_module"
75
+ old = getattr(spec.loader, func_name)
76
+ func = self.exec_module if is_new_api else self.load_module
77
+ if old is not func:
78
+ try: # noqa: SIM105
79
+ setattr(spec.loader, func_name, partial(func, old))
80
+ except AttributeError:
81
+ pass # C-Extension loaders are r/o such as zipimporter with <3.7
82
+ return spec
83
+ finally:
84
+ self.fullname = None
85
+ return None
86
+
87
+ @staticmethod
88
+ def exec_module(old, module):
89
+ old(module)
90
+ if module.__name__ in _DISTUTILS_PATCH:
91
+ patch_dist(module)
92
+
93
+ @staticmethod
94
+ def load_module(old, name):
95
+ module = old(name)
96
+ if module.__name__ in _DISTUTILS_PATCH:
97
+ patch_dist(module)
98
+ return module
99
+
100
+
101
+ sys.meta_path.insert(0, _Finder())
.venv/Lib/site-packages/_yaml/__init__.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is a stub package designed to roughly emulate the _yaml
2
+ # extension module, which previously existed as a standalone module
3
+ # and has been moved into the `yaml` package namespace.
4
+ # It does not perfectly mimic its old counterpart, but should get
5
+ # close enough for anyone who's relying on it even when they shouldn't.
6
+ import yaml
7
+
8
+ # in some circumstances, the yaml module we imoprted may be from a different version, so we need
9
+ # to tread carefully when poking at it here (it may not have the attributes we expect)
10
+ if not getattr(yaml, '__with_libyaml__', False):
11
+ from sys import version_info
12
+
13
+ exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
14
+ raise exc("No module named '_yaml'")
15
+ else:
16
+ from yaml._yaml import *
17
+ import warnings
18
+ warnings.warn(
19
+ 'The _yaml extension module is now located at yaml._yaml'
20
+ ' and its location is subject to change. To use the'
21
+ ' LibYAML-based parser and emitter, import from `yaml`:'
22
+ ' `from yaml import CLoader as Loader, CDumper as Dumper`.',
23
+ DeprecationWarning
24
+ )
25
+ del warnings
26
+ # Don't `del yaml` here because yaml is actually an existing
27
+ # namespace member of _yaml.
28
+
29
+ __name__ = '_yaml'
30
+ # If the module is top-level (i.e. not a part of any specific package)
31
+ # then the attribute should be set to ''.
32
+ # https://docs.python.org/3.8/library/types.html
33
+ __package__ = ''
.venv/Lib/site-packages/anyio-4.14.1.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ uv
.venv/Lib/site-packages/anyio-4.14.1.dist-info/METADATA ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: anyio
3
+ Version: 4.14.1
4
+ Summary: High-level concurrency and networking framework on top of asyncio or Trio
5
+ Author-email: Alex Grönholm <alex.gronholm@nextday.fi>
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/
8
+ Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html
9
+ Project-URL: Source code, https://github.com/agronholm/anyio
10
+ Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Framework :: AnyIO
14
+ Classifier: Typing :: Typed
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3.15
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/x-rst
25
+ License-File: LICENSE
26
+ Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11"
27
+ Requires-Dist: idna>=2.8
28
+ Requires-Dist: typing_extensions>=4.5; python_version < "3.13"
29
+ Provides-Extra: trio
30
+ Requires-Dist: trio>=0.32.0; extra == "trio"
31
+ Dynamic: license-file
32
+
33
+ .. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg
34
+ :target: https://github.com/agronholm/anyio/actions/workflows/test.yml
35
+ :alt: Build Status
36
+ .. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master
37
+ :target: https://coveralls.io/github/agronholm/anyio?branch=master
38
+ :alt: Code Coverage
39
+ .. image:: https://readthedocs.org/projects/anyio/badge/?version=latest
40
+ :target: https://anyio.readthedocs.io/en/latest/?badge=latest
41
+ :alt: Documentation
42
+ .. image:: https://badges.gitter.im/gitterHQ/gitter.svg
43
+ :target: https://gitter.im/python-trio/AnyIO
44
+ :alt: Gitter chat
45
+ .. image:: https://tidelift.com/badges/package/pypi/anyio
46
+ :target: https://tidelift.com/subscription/pkg/pypi-anyio
47
+ :alt: Tidelift
48
+
49
+ AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or
50
+ Trio_. It implements Trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony
51
+ with the native SC of Trio itself.
52
+
53
+ Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or
54
+ Trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full
55
+ refactoring necessary. It will blend in with the native libraries of your chosen backend.
56
+
57
+ To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it
58
+ `here <https://anyio.readthedocs.io/en/stable/why.html>`_.
59
+
60
+ Documentation
61
+ -------------
62
+
63
+ View full documentation at: https://anyio.readthedocs.io/
64
+
65
+ Features
66
+ --------
67
+
68
+ AnyIO offers the following functionality:
69
+
70
+ * Task groups (nurseries_ in trio terminology)
71
+ * High-level networking (TCP, UDP and UNIX sockets)
72
+
73
+ * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python
74
+ 3.8)
75
+ * async/await style UDP sockets (unlike asyncio where you still have to use Transports and
76
+ Protocols)
77
+
78
+ * A versatile API for byte streams and object streams
79
+ * Inter-task synchronization and communication (locks, conditions, events, semaphores, object
80
+ streams)
81
+ * Worker threads
82
+ * Subprocesses
83
+ * Subinterpreter support for code parallelization (on Python 3.13 and later)
84
+ * Asynchronous file I/O (using worker threads)
85
+ * Signal handling
86
+ * Asynchronous versions of the functools_ and itertools_ modules
87
+
88
+ AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures.
89
+ It even works with the popular Hypothesis_ library.
90
+
91
+ .. _asyncio: https://docs.python.org/3/library/asyncio.html
92
+ .. _Trio: https://github.com/python-trio/trio
93
+ .. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency
94
+ .. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning
95
+ .. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs
96
+ .. _pytest: https://docs.pytest.org/en/latest/
97
+ .. _functools: https://docs.python.org/3/library/functools.html
98
+ .. _itertools: https://docs.python.org/3/library/itertools.html
99
+ .. _Hypothesis: https://hypothesis.works/
100
+
101
+ Security contact information
102
+ ----------------------------
103
+
104
+ To report a security vulnerability, please use the `Tidelift security contact`_.
105
+ Tidelift will coordinate the fix and disclosure.
106
+
107
+ .. _Tidelift security contact: https://tidelift.com/security
.venv/Lib/site-packages/anyio-4.14.1.dist-info/RECORD ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ anyio-4.14.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
2
+ anyio-4.14.1.dist-info/METADATA,sha256=bfkjYaZLYPsPI5JV_Gn7HYF65mteyE8nhjaI0ZqC4L4,4645
3
+ anyio-4.14.1.dist-info/RECORD,,
4
+ anyio-4.14.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ anyio-4.14.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ anyio-4.14.1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39
7
+ anyio-4.14.1.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081
8
+ anyio-4.14.1.dist-info/scm_file_list.json,sha256=wDSXGv8Ehn5ZW5BhB-RlaAc16zY_OfO27qrlMfMMZy8,3654
9
+ anyio-4.14.1.dist-info/scm_version.json,sha256=gw22Q2aBbdiYhyMbObTYNN7BN-wSpzOCktNiAuulRN8,161
10
+ anyio-4.14.1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6
11
+ anyio/__init__.py,sha256=HitUIfzvAojSeaHVmJ9rFn8k_yI63G6s_jUL2QChf4U,6405
12
+ anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ anyio/_backends/_asyncio.py,sha256=-q-5gUYg_r5SsN-OYbQnF_lvtW0v51-dFlsU8_gduWA,102077
14
+ anyio/_backends/_trio.py,sha256=vR0ZgxVnOo4AHhHcHVG0worMc-3ZNpAZ6Vxh0m0ZZC0,45189
15
+ anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626
17
+ anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215
18
+ anyio/_core/_eventloop.py,sha256=ByZUeJD9alMfcyTseRo5IzTO0IltEul_Gyq9iqSjqDk,6658
19
+ anyio/_core/_exceptions.py,sha256=OfzLO4Z3Hog1TnipbIn72YNtkoYxS4lHW9MqKDeGc88,4936
20
+ anyio/_core/_fileio.py,sha256=hHfyV0bXDL-R2ZNnInwse3nmTAd36AIz1cBxgmAwzAQ,31358
21
+ anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435
22
+ anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016
23
+ anyio/_core/_sockets.py,sha256=9FU423j52XBBfGVr6MdzPTdyw8bGrzApZ5m338-AtsY,35286
24
+ anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806
25
+ anyio/_core/_subprocesses.py,sha256=tkmkPKEkEaiMD8C9WRZBlmgjOYRDRbZdte6e-unay2E,7916
26
+ anyio/_core/_synchronization.py,sha256=jn2nIbTRlBAUXL-mx_a3I_VnasF8GbVFpBRp2-YwCx0,21591
27
+ anyio/_core/_tasks.py,sha256=ELL2jscaSW0Jw_xA6MtQlm3xwvFEzjTbc1u9Tteyt0I,13244
28
+ anyio/_core/_tempfile.py,sha256=jE2w59FRF3yRo4vjkjfZF2YcqsBZvc66VWRwrJGDYGk,19624
29
+ anyio/_core/_testing.py,sha256=u7MPqGXwpTxqI7hclSdNA30z2GH1Nw258uwKvy_RfBg,2340
30
+ anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508
31
+ anyio/abc/__init__.py,sha256=6mWhcl_pGXhrgZVHP_TCfMvIXIOp9mroEFM90fYCU_U,2869
32
+ anyio/abc/_eventloop.py,sha256=OqWYSEj0TmwL_xniCJt3_jHFWsuMk9THk8tCTGsKapI,10681
33
+ anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783
34
+ anyio/abc/_sockets.py,sha256=OmVDrfemVvF9c5K1tpBgQyV6fn5v0XyCExLAqBOGz9o,13124
35
+ anyio/abc/_streams.py,sha256=HYvna1iZbWcwLROTO6IhLX79RTRLPShZMWe0sG1q54I,7481
36
+ anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067
37
+ anyio/abc/_tasks.py,sha256=m-FtE4phxeNIELSG7A3H7VUz3jA2Ib5J2JIew8-PS6o,6642
38
+ anyio/abc/_testing.py,sha256=9YYM2AXsYFvf4PLjUEr6yRxDiUeB5QbY_gOg0X_C6lY,2034
39
+ anyio/from_thread.py,sha256=JYsbaCaIB_Iit6kNhtXSteJGt4PcQ7ncq0nIpcelIrg,19265
40
+ anyio/functools.py,sha256=T4JS8IXq-x1S0Lbo2owF8l9fza2KypO147QLeyz4cjs,11797
41
+ anyio/itertools.py,sha256=QV-9mnRCr2yBph8g01QFvN-bQ_Yle-8Sl13YSydBlMI,16168
42
+ anyio/lowlevel.py,sha256=WPtppHfI2qs1nokzjn8elL8LvyqI05AK5Zslhlo71A4,6242
43
+ anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ anyio/pytest_plugin.py,sha256=paMpI_VMNQf2bir0LfvgMpXSiYJoHDzWdKUVTyoHmvQ,13609
45
+ anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ anyio/streams/buffered.py,sha256=v3xKtjFHgNV41g2SvMAkA_qd2t9WYlCI1_lNGCAatw0,6650
47
+ anyio/streams/file.py,sha256=msnrotVKGMQomUu_Rj2qz9MvIdUp6d3JGr7MOEO8kV4,4428
48
+ anyio/streams/memory.py,sha256=F0zwzvFJKAhX_LRZGoKzzqDC2oMM-f-yyTBrEYEGOaU,10740
49
+ anyio/streams/stapled.py,sha256=T8Xqwf8K6EgURPxbt1N4i7A8BAk-gScv-GRhjLXIf_o,4390
50
+ anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765
51
+ anyio/streams/tls.py,sha256=DQVkXUvsTEYKkBO8dlVU7j_5H8QOtLy4sGi1Wrjqevo,15303
52
+ anyio/to_interpreter.py,sha256=_mLngrMy97TMR6VbW4Y6YzDUk9ZuPcQMPlkuyRh3C9k,7100
53
+ anyio/to_process.py,sha256=68qhLfce7MeXysid4fOpmhfWkgdo7Z7-9BC0VyUciIE,9809
54
+ anyio/to_thread.py,sha256=f6h_k2d743GBv9FhAnhM_YpTvWgIrzBy9cOE0eJ1UJw,2693
.venv/Lib/site-packages/anyio-4.14.1.dist-info/REQUESTED ADDED
File without changes
.venv/Lib/site-packages/anyio-4.14.1.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
.venv/Lib/site-packages/anyio-4.14.1.dist-info/entry_points.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [pytest11]
2
+ anyio = anyio.pytest_plugin
.venv/Lib/site-packages/anyio-4.14.1.dist-info/licenses/LICENSE ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Alex Grönholm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.venv/Lib/site-packages/anyio-4.14.1.dist-info/scm_file_list.json ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": [
3
+ ".pre-commit-config.yaml",
4
+ "LICENSE",
5
+ "pyproject.toml",
6
+ "AGENTS.md",
7
+ "README.rst",
8
+ "CLAUDE.md",
9
+ ".readthedocs.yml",
10
+ ".gitignore",
11
+ "docs/tempfile.rst",
12
+ "docs/signals.rst",
13
+ "docs/synchronization.rst",
14
+ "docs/contextmanagers.rst",
15
+ "docs/testing.rst",
16
+ "docs/networking.rst",
17
+ "docs/contributing.rst",
18
+ "docs/index.rst",
19
+ "docs/versionhistory.rst",
20
+ "docs/threads.rst",
21
+ "docs/api.rst",
22
+ "docs/typedattrs.rst",
23
+ "docs/basics.rst",
24
+ "docs/fileio.rst",
25
+ "docs/cancellation.rst",
26
+ "docs/support.rst",
27
+ "docs/streams.rst",
28
+ "docs/why.rst",
29
+ "docs/tasks.rst",
30
+ "docs/migration.rst",
31
+ "docs/conf.py",
32
+ "docs/subprocesses.rst",
33
+ "docs/faq.rst",
34
+ "docs/subinterpreters.rst",
35
+ "src/anyio/functools.py",
36
+ "src/anyio/py.typed",
37
+ "src/anyio/__init__.py",
38
+ "src/anyio/pytest_plugin.py",
39
+ "src/anyio/itertools.py",
40
+ "src/anyio/to_interpreter.py",
41
+ "src/anyio/from_thread.py",
42
+ "src/anyio/to_process.py",
43
+ "src/anyio/to_thread.py",
44
+ "src/anyio/lowlevel.py",
45
+ "src/anyio/_backends/_trio.py",
46
+ "src/anyio/_backends/__init__.py",
47
+ "src/anyio/_backends/_asyncio.py",
48
+ "src/anyio/streams/memory.py",
49
+ "src/anyio/streams/__init__.py",
50
+ "src/anyio/streams/tls.py",
51
+ "src/anyio/streams/file.py",
52
+ "src/anyio/streams/text.py",
53
+ "src/anyio/streams/stapled.py",
54
+ "src/anyio/streams/buffered.py",
55
+ "src/anyio/abc/_eventloop.py",
56
+ "src/anyio/abc/__init__.py",
57
+ "src/anyio/abc/_sockets.py",
58
+ "src/anyio/abc/_tasks.py",
59
+ "src/anyio/abc/_subprocesses.py",
60
+ "src/anyio/abc/_resources.py",
61
+ "src/anyio/abc/_streams.py",
62
+ "src/anyio/abc/_testing.py",
63
+ "src/anyio/_core/_typedattr.py",
64
+ "src/anyio/_core/_eventloop.py",
65
+ "src/anyio/_core/__init__.py",
66
+ "src/anyio/_core/_tempfile.py",
67
+ "src/anyio/_core/_sockets.py",
68
+ "src/anyio/_core/_tasks.py",
69
+ "src/anyio/_core/_fileio.py",
70
+ "src/anyio/_core/_synchronization.py",
71
+ "src/anyio/_core/_subprocesses.py",
72
+ "src/anyio/_core/_resources.py",
73
+ "src/anyio/_core/_contextmanagers.py",
74
+ "src/anyio/_core/_exceptions.py",
75
+ "src/anyio/_core/_streams.py",
76
+ "src/anyio/_core/_signals.py",
77
+ "src/anyio/_core/_asyncio_selector_thread.py",
78
+ "src/anyio/_core/_testing.py",
79
+ "tests/test_itertools.py",
80
+ "tests/test_functools.py",
81
+ "tests/test_eventloop.py",
82
+ "tests/__init__.py",
83
+ "tests/test_to_thread.py",
84
+ "tests/test_from_thread.py",
85
+ "tests/test_lowlevel.py",
86
+ "tests/test_to_interpreter.py",
87
+ "tests/test_sockets.py",
88
+ "tests/test_typedattr.py",
89
+ "tests/test_to_process.py",
90
+ "tests/test_all_attributes.py",
91
+ "tests/test_synchronization.py",
92
+ "tests/test_debugging.py",
93
+ "tests/test_contextmanagers.py",
94
+ "tests/test_fileio.py",
95
+ "tests/conftest.py",
96
+ "tests/test_signals.py",
97
+ "tests/test_deprecations.py",
98
+ "tests/test_tempfile.py",
99
+ "tests/test_taskgroups.py",
100
+ "tests/test_pytest_plugin.py",
101
+ "tests/test_subprocesses.py",
102
+ "tests/streams/test_text.py",
103
+ "tests/streams/test_memory.py",
104
+ "tests/streams/__init__.py",
105
+ "tests/streams/test_file.py",
106
+ "tests/streams/test_stapled.py",
107
+ "tests/streams/test_tls.py",
108
+ "tests/streams/test_buffered.py",
109
+ ".github/pull_request_template.md",
110
+ ".github/dependabot.yml",
111
+ ".github/FUNDING.yml",
112
+ ".github/ISSUE_TEMPLATE/features_request.yaml",
113
+ ".github/ISSUE_TEMPLATE/bug_report.yaml",
114
+ ".github/ISSUE_TEMPLATE/config.yml",
115
+ ".github/workflows/test.yml",
116
+ ".github/workflows/test-downstream.yml",
117
+ ".github/workflows/publish.yml"
118
+ ]
119
+ }
.venv/Lib/site-packages/anyio-4.14.1.dist-info/scm_version.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tag": "4.14.1",
3
+ "distance": 0,
4
+ "node": "g149b9e907618fadf6840a4d3cebad533b0c7d033",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-06-24"
8
+ }
.venv/Lib/site-packages/anyio-4.14.1.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ anyio
.venv/Lib/site-packages/anyio/__init__.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from ._core._contextmanagers import AsyncContextManagerMixin as AsyncContextManagerMixin
4
+ from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin
5
+ from ._core._eventloop import current_time as current_time
6
+ from ._core._eventloop import get_all_backends as get_all_backends
7
+ from ._core._eventloop import get_available_backends as get_available_backends
8
+ from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class
9
+ from ._core._eventloop import run as run
10
+ from ._core._eventloop import sleep as sleep
11
+ from ._core._eventloop import sleep_forever as sleep_forever
12
+ from ._core._eventloop import sleep_until as sleep_until
13
+ from ._core._exceptions import BrokenResourceError as BrokenResourceError
14
+ from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter
15
+ from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess
16
+ from ._core._exceptions import BusyResourceError as BusyResourceError
17
+ from ._core._exceptions import ClosedResourceError as ClosedResourceError
18
+ from ._core._exceptions import ConnectionFailed as ConnectionFailed
19
+ from ._core._exceptions import DelimiterNotFound as DelimiterNotFound
20
+ from ._core._exceptions import EndOfStream as EndOfStream
21
+ from ._core._exceptions import IncompleteRead as IncompleteRead
22
+ from ._core._exceptions import NoEventLoopError as NoEventLoopError
23
+ from ._core._exceptions import RunFinishedError as RunFinishedError
24
+ from ._core._exceptions import TaskCancelled as TaskCancelled
25
+ from ._core._exceptions import TaskFailed as TaskFailed
26
+ from ._core._exceptions import TaskNotFinished as TaskNotFinished
27
+ from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError
28
+ from ._core._exceptions import WouldBlock as WouldBlock
29
+ from ._core._fileio import AsyncFile as AsyncFile
30
+ from ._core._fileio import Path as Path
31
+ from ._core._fileio import open_file as open_file
32
+ from ._core._fileio import wrap_file as wrap_file
33
+ from ._core._resources import aclose_forcefully as aclose_forcefully
34
+ from ._core._signals import open_signal_receiver as open_signal_receiver
35
+ from ._core._sockets import TCPConnectable as TCPConnectable
36
+ from ._core._sockets import UNIXConnectable as UNIXConnectable
37
+ from ._core._sockets import as_connectable as as_connectable
38
+ from ._core._sockets import connect_tcp as connect_tcp
39
+ from ._core._sockets import connect_unix as connect_unix
40
+ from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket
41
+ from ._core._sockets import (
42
+ create_connected_unix_datagram_socket as create_connected_unix_datagram_socket,
43
+ )
44
+ from ._core._sockets import create_tcp_listener as create_tcp_listener
45
+ from ._core._sockets import create_udp_socket as create_udp_socket
46
+ from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket
47
+ from ._core._sockets import create_unix_listener as create_unix_listener
48
+ from ._core._sockets import getaddrinfo as getaddrinfo
49
+ from ._core._sockets import getnameinfo as getnameinfo
50
+ from ._core._sockets import notify_closing as notify_closing
51
+ from ._core._sockets import wait_readable as wait_readable
52
+ from ._core._sockets import wait_socket_readable as wait_socket_readable
53
+ from ._core._sockets import wait_socket_writable as wait_socket_writable
54
+ from ._core._sockets import wait_writable as wait_writable
55
+ from ._core._streams import create_memory_object_stream as create_memory_object_stream
56
+ from ._core._subprocesses import open_process as open_process
57
+ from ._core._subprocesses import run_process as run_process
58
+ from ._core._synchronization import CapacityLimiter as CapacityLimiter
59
+ from ._core._synchronization import (
60
+ CapacityLimiterStatistics as CapacityLimiterStatistics,
61
+ )
62
+ from ._core._synchronization import Condition as Condition
63
+ from ._core._synchronization import ConditionStatistics as ConditionStatistics
64
+ from ._core._synchronization import Event as Event
65
+ from ._core._synchronization import EventStatistics as EventStatistics
66
+ from ._core._synchronization import Lock as Lock
67
+ from ._core._synchronization import LockStatistics as LockStatistics
68
+ from ._core._synchronization import ResourceGuard as ResourceGuard
69
+ from ._core._synchronization import Semaphore as Semaphore
70
+ from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics
71
+ from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED
72
+ from ._core._tasks import CancelScope as CancelScope
73
+ from ._core._tasks import TaskHandle as TaskHandle
74
+ from ._core._tasks import create_task_group as create_task_group
75
+ from ._core._tasks import current_effective_deadline as current_effective_deadline
76
+ from ._core._tasks import fail_after as fail_after
77
+ from ._core._tasks import move_on_after as move_on_after
78
+ from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile
79
+ from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile
80
+ from ._core._tempfile import TemporaryDirectory as TemporaryDirectory
81
+ from ._core._tempfile import TemporaryFile as TemporaryFile
82
+ from ._core._tempfile import gettempdir as gettempdir
83
+ from ._core._tempfile import gettempdirb as gettempdirb
84
+ from ._core._tempfile import mkdtemp as mkdtemp
85
+ from ._core._tempfile import mkstemp as mkstemp
86
+ from ._core._testing import TaskInfo as TaskInfo
87
+ from ._core._testing import get_current_task as get_current_task
88
+ from ._core._testing import get_running_tasks as get_running_tasks
89
+ from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked
90
+ from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider
91
+ from ._core._typedattr import TypedAttributeSet as TypedAttributeSet
92
+ from ._core._typedattr import typed_attribute as typed_attribute
93
+
94
+ # Re-export imports so they look like they live directly in this package
95
+ for __value in list(locals().values()):
96
+ if getattr(__value, "__module__", "").startswith("anyio."):
97
+ __value.__module__ = __name__
98
+
99
+
100
+ del __value
101
+
102
+
103
+ def __getattr__(attr: str) -> type[BrokenWorkerInterpreter]:
104
+ """Support deprecated aliases."""
105
+ if attr == "BrokenWorkerIntepreter":
106
+ import warnings
107
+
108
+ warnings.warn(
109
+ "The 'BrokenWorkerIntepreter' alias is deprecated, use 'BrokenWorkerInterpreter' instead.",
110
+ DeprecationWarning,
111
+ stacklevel=2,
112
+ )
113
+ return BrokenWorkerInterpreter
114
+
115
+ raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
.venv/Lib/site-packages/anyio/_backends/__init__.py ADDED
File without changes
.venv/Lib/site-packages/anyio/_backends/_asyncio.py ADDED
The diff for this file is too large to render. See raw diff
 
.venv/Lib/site-packages/anyio/_backends/_trio.py ADDED
@@ -0,0 +1,1456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import array
4
+ import math
5
+ import os
6
+ import socket
7
+ import sys
8
+ import types
9
+ import weakref
10
+ from collections.abc import (
11
+ AsyncGenerator,
12
+ AsyncIterator,
13
+ Awaitable,
14
+ Callable,
15
+ Collection,
16
+ Coroutine,
17
+ Iterable,
18
+ Sequence,
19
+ )
20
+ from contextlib import AbstractContextManager
21
+ from contextvars import Context
22
+ from dataclasses import dataclass
23
+ from functools import partial, wraps
24
+ from io import IOBase
25
+ from os import PathLike
26
+ from signal import Signals
27
+ from socket import AddressFamily, SocketKind
28
+ from types import TracebackType
29
+ from typing import (
30
+ IO,
31
+ TYPE_CHECKING,
32
+ Any,
33
+ Generic,
34
+ Literal,
35
+ NoReturn,
36
+ ParamSpec,
37
+ TypeVar,
38
+ cast,
39
+ overload,
40
+ )
41
+
42
+ import trio.from_thread
43
+ import trio.lowlevel
44
+ from outcome import Error, Outcome, Value
45
+ from trio.lowlevel import (
46
+ current_root_task,
47
+ current_task,
48
+ notify_closing,
49
+ wait_readable,
50
+ wait_writable,
51
+ )
52
+ from trio.socket import SocketType as TrioSocketType
53
+ from trio.to_thread import run_sync
54
+
55
+ from .. import (
56
+ CapacityLimiterStatistics,
57
+ EventStatistics,
58
+ LockStatistics,
59
+ RunFinishedError,
60
+ TaskInfo,
61
+ WouldBlock,
62
+ abc,
63
+ )
64
+ from .._core._eventloop import claim_worker_thread
65
+ from .._core._exceptions import (
66
+ BrokenResourceError,
67
+ BusyResourceError,
68
+ ClosedResourceError,
69
+ EndOfStream,
70
+ )
71
+ from .._core._sockets import convert_ipv6_sockaddr
72
+ from .._core._streams import create_memory_object_stream
73
+ from .._core._synchronization import (
74
+ CapacityLimiter as BaseCapacityLimiter,
75
+ )
76
+ from .._core._synchronization import Event as BaseEvent
77
+ from .._core._synchronization import Lock as BaseLock
78
+ from .._core._synchronization import (
79
+ ResourceGuard,
80
+ SemaphoreStatistics,
81
+ )
82
+ from .._core._synchronization import Semaphore as BaseSemaphore
83
+ from .._core._tasks import CancelScope as BaseCancelScope
84
+ from .._core._tasks import TaskHandle
85
+ from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType
86
+ from ..abc._eventloop import AsyncBackend, StrOrBytesPath
87
+ from ..abc._tasks import T_contra, call_for_coroutine, get_callable_name
88
+ from ..streams.memory import MemoryObjectSendStream
89
+
90
+ if TYPE_CHECKING:
91
+ from _typeshed import FileDescriptorLike
92
+
93
+ if sys.version_info >= (3, 11):
94
+ from typing import TypeVarTuple, Unpack
95
+ else:
96
+ from exceptiongroup import BaseExceptionGroup
97
+ from typing_extensions import TypeVarTuple, Unpack
98
+
99
+ T = TypeVar("T")
100
+ T_Retval = TypeVar("T_Retval")
101
+ T_co = TypeVar("T_co", covariant=True)
102
+ T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType)
103
+ PosArgsT = TypeVarTuple("PosArgsT")
104
+ P = ParamSpec("P")
105
+
106
+
107
+ def ensure_returns_coro(
108
+ func: Callable[P, Awaitable[T_Retval]],
109
+ ) -> Callable[P, Coroutine[Any, Any, T_Retval]]:
110
+ @wraps(func)
111
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T_Retval]:
112
+ awaitable = func(*args, **kwargs)
113
+ # Check the common case first.
114
+ if isinstance(awaitable, Coroutine):
115
+ return awaitable
116
+ elif not isinstance(awaitable, Awaitable):
117
+ # The user violated the type annotations. Still, we should pass this on to
118
+ # Trio so it can raise with an appropriate message.
119
+ return awaitable
120
+ else:
121
+
122
+ @wraps(func)
123
+ async def inner_wrapper() -> T_Retval:
124
+ return await awaitable
125
+
126
+ return inner_wrapper()
127
+
128
+ return wrapper
129
+
130
+
131
+ #
132
+ # Event loop
133
+ #
134
+
135
+ RunVar = trio.lowlevel.RunVar
136
+
137
+
138
+ #
139
+ # Timeouts and cancellation
140
+ #
141
+
142
+
143
+ class CancelScope(BaseCancelScope):
144
+ __slots__ = ("__original",)
145
+
146
+ def __new__(
147
+ cls, original: trio.CancelScope | None = None, **kwargs: object
148
+ ) -> CancelScope:
149
+ return object.__new__(cls)
150
+
151
+ def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None:
152
+ self.__original = original or trio.CancelScope(**kwargs)
153
+
154
+ def __enter__(self) -> CancelScope:
155
+ self.__original.__enter__()
156
+ return self
157
+
158
+ def __exit__(
159
+ self,
160
+ exc_type: type[BaseException] | None,
161
+ exc_val: BaseException | None,
162
+ exc_tb: TracebackType | None,
163
+ ) -> bool:
164
+ return self.__original.__exit__(exc_type, exc_val, exc_tb)
165
+
166
+ def cancel(self, reason: str | None = None) -> None:
167
+ self.__original.cancel(reason)
168
+
169
+ @property
170
+ def deadline(self) -> float:
171
+ return self.__original.deadline
172
+
173
+ @deadline.setter
174
+ def deadline(self, value: float) -> None:
175
+ self.__original.deadline = value
176
+
177
+ @property
178
+ def cancel_called(self) -> bool:
179
+ return self.__original.cancel_called
180
+
181
+ @property
182
+ def cancelled_caught(self) -> bool:
183
+ return self.__original.cancelled_caught
184
+
185
+ @property
186
+ def shield(self) -> bool:
187
+ return self.__original.shield
188
+
189
+ @shield.setter
190
+ def shield(self, value: bool) -> None:
191
+ self.__original.shield = value
192
+
193
+
194
+ #
195
+ # Task groups
196
+ #
197
+
198
+ empty_start_value = object()
199
+
200
+
201
+ class _TrioTaskStatus(Generic[T_contra], abc.TaskStatus[T_contra]):
202
+ early_start_value: T_contra | object = empty_start_value
203
+ real_task_status: trio.TaskStatus[T_contra | None] | None = None
204
+
205
+ def started(self, value: T_contra | None = None) -> None:
206
+ if self.real_task_status is None:
207
+ if self.early_start_value is not empty_start_value:
208
+ raise RuntimeError("called 'started' twice on the same task status")
209
+
210
+ self.early_start_value = value
211
+ else:
212
+ self.real_task_status.started(value)
213
+
214
+
215
+ class TaskGroup(abc.TaskGroup):
216
+ def __init__(self) -> None:
217
+ self._entered = False
218
+ self._active = False
219
+ self._nursery_manager = trio.open_nursery(strict_exception_groups=True)
220
+ self.cancel_scope = None # type: ignore[assignment]
221
+
222
+ async def __aenter__(self) -> TaskGroup:
223
+ if self._entered:
224
+ raise RuntimeError("TaskGroup cannot be entered more than once")
225
+
226
+ self._entered = True
227
+ self._active = True
228
+ self._nursery = await self._nursery_manager.__aenter__()
229
+ self.cancel_scope = CancelScope(self._nursery.cancel_scope)
230
+ return self
231
+
232
+ async def __aexit__(
233
+ self,
234
+ exc_type: type[BaseException] | None,
235
+ exc_val: BaseException | None,
236
+ exc_tb: TracebackType | None,
237
+ ) -> bool:
238
+ try:
239
+ # trio.Nursery.__exit__ returns bool; .open_nursery has wrong type
240
+ return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value]
241
+ except BaseExceptionGroup as exc:
242
+ if not exc.split(trio.Cancelled)[1]:
243
+ raise trio.Cancelled._create() from exc
244
+
245
+ raise
246
+ finally:
247
+ del exc_val, exc_tb
248
+ self._active = False
249
+
250
+ def _check_active(self, coro: Coroutine | None = None) -> None:
251
+ if not self._active:
252
+ if coro is not None:
253
+ coro.close()
254
+
255
+ raise RuntimeError(
256
+ "This task group is not active; no new tasks can be started."
257
+ )
258
+
259
+ def create_task(
260
+ self,
261
+ coro: Coroutine[Any, Any, T_co],
262
+ *,
263
+ name: object = None,
264
+ context: Context | None = None,
265
+ ) -> TaskHandle[T_co]:
266
+ if not isinstance(coro, Coroutine):
267
+ raise TypeError(f"expected a coroutine, got {coro.__class__.__qualname__}")
268
+
269
+ self._check_active(coro)
270
+ handle = TaskHandle(coro, name)
271
+ if context is not None:
272
+ context.run(
273
+ partial(self._nursery.start_soon, handle._run_coro, name=handle.name)
274
+ )
275
+ else:
276
+ self._nursery.start_soon(handle._run_coro, name=handle.name)
277
+
278
+ return handle
279
+
280
+ async def start(
281
+ self,
282
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
283
+ *args: Unpack[PosArgsT],
284
+ name: object = None,
285
+ return_handle: Literal[False] | Literal[True] = False,
286
+ ) -> Any:
287
+ handle: TaskHandle[T_co]
288
+
289
+ async def run_coro_with_task_status(
290
+ *, task_status: trio.TaskStatus[Any]
291
+ ) -> None:
292
+ nonlocal handle
293
+ wrapper_task_status = _TrioTaskStatus()
294
+ coro = call_for_coroutine(func, args, task_status=wrapper_task_status)
295
+ if wrapper_task_status.early_start_value is not empty_start_value:
296
+ task_status.started(wrapper_task_status.early_start_value)
297
+ else:
298
+ wrapper_task_status.real_task_status = task_status
299
+
300
+ handle = TaskHandle(coro, name)
301
+ await handle._run_coro()
302
+
303
+ self._check_active()
304
+ final_name = get_callable_name(func, name)
305
+ start_value = await self._nursery.start(
306
+ run_coro_with_task_status, name=final_name
307
+ )
308
+ if return_handle:
309
+ handle._start_value = start_value
310
+ return handle
311
+ else:
312
+ return start_value
313
+
314
+
315
+ #
316
+ # Subprocesses
317
+ #
318
+
319
+
320
+ @dataclass(eq=False)
321
+ class ReceiveStreamWrapper(abc.ByteReceiveStream):
322
+ _stream: trio.abc.ReceiveStream
323
+
324
+ async def receive(self, max_bytes: int | None = None) -> bytes:
325
+ try:
326
+ data = await self._stream.receive_some(max_bytes)
327
+ except trio.ClosedResourceError as exc:
328
+ raise ClosedResourceError from exc.__cause__
329
+ except trio.BrokenResourceError as exc:
330
+ raise BrokenResourceError from exc.__cause__
331
+
332
+ if data:
333
+ return bytes(data)
334
+ else:
335
+ raise EndOfStream
336
+
337
+ async def aclose(self) -> None:
338
+ await self._stream.aclose()
339
+
340
+
341
+ @dataclass(eq=False)
342
+ class SendStreamWrapper(abc.ByteSendStream):
343
+ _stream: trio.abc.SendStream
344
+
345
+ async def send(self, item: bytes) -> None:
346
+ try:
347
+ await self._stream.send_all(item)
348
+ except trio.ClosedResourceError as exc:
349
+ raise ClosedResourceError from exc.__cause__
350
+ except trio.BrokenResourceError as exc:
351
+ raise BrokenResourceError from exc.__cause__
352
+
353
+ async def aclose(self) -> None:
354
+ await self._stream.aclose()
355
+
356
+
357
+ @dataclass(eq=False)
358
+ class Process(abc.Process):
359
+ _process: trio.Process
360
+ _stdin: abc.ByteSendStream | None
361
+ _stdout: abc.ByteReceiveStream | None
362
+ _stderr: abc.ByteReceiveStream | None
363
+
364
+ async def aclose(self) -> None:
365
+ with CancelScope(shield=True):
366
+ if self._stdin:
367
+ await self._stdin.aclose()
368
+ if self._stdout:
369
+ await self._stdout.aclose()
370
+ if self._stderr:
371
+ await self._stderr.aclose()
372
+
373
+ try:
374
+ await self.wait()
375
+ except BaseException:
376
+ self.kill()
377
+ with CancelScope(shield=True):
378
+ await self.wait()
379
+ raise
380
+
381
+ async def wait(self) -> int:
382
+ return await self._process.wait()
383
+
384
+ def terminate(self) -> None:
385
+ self._process.terminate()
386
+
387
+ def kill(self) -> None:
388
+ self._process.kill()
389
+
390
+ def send_signal(self, signal: Signals) -> None:
391
+ self._process.send_signal(signal)
392
+
393
+ @property
394
+ def pid(self) -> int:
395
+ return self._process.pid
396
+
397
+ @property
398
+ def returncode(self) -> int | None:
399
+ return self._process.returncode
400
+
401
+ @property
402
+ def stdin(self) -> abc.ByteSendStream | None:
403
+ return self._stdin
404
+
405
+ @property
406
+ def stdout(self) -> abc.ByteReceiveStream | None:
407
+ return self._stdout
408
+
409
+ @property
410
+ def stderr(self) -> abc.ByteReceiveStream | None:
411
+ return self._stderr
412
+
413
+
414
+ class _ProcessPoolShutdownInstrument(trio.abc.Instrument):
415
+ def after_run(self) -> None:
416
+ super().after_run()
417
+
418
+
419
+ current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar(
420
+ "current_default_worker_process_limiter"
421
+ )
422
+
423
+
424
+ async def _shutdown_process_pool(workers: set[abc.Process]) -> None:
425
+ try:
426
+ await trio.sleep(math.inf)
427
+ except trio.Cancelled:
428
+ for process in workers:
429
+ if process.returncode is None:
430
+ process.kill()
431
+
432
+ with CancelScope(shield=True):
433
+ for process in workers:
434
+ await process.aclose()
435
+
436
+
437
+ #
438
+ # Sockets and networking
439
+ #
440
+
441
+
442
+ class _TrioSocketMixin(Generic[T_SockAddr]):
443
+ def __init__(self, trio_socket: TrioSocketType) -> None:
444
+ self._trio_socket = trio_socket
445
+ self._closed = False
446
+
447
+ def _check_closed(self) -> None:
448
+ if self._closed:
449
+ raise ClosedResourceError
450
+ if self._trio_socket.fileno() < 0:
451
+ raise BrokenResourceError
452
+
453
+ @property
454
+ def _raw_socket(self) -> socket.socket:
455
+ return self._trio_socket._sock # type: ignore[attr-defined]
456
+
457
+ async def aclose(self) -> None:
458
+ if self._trio_socket.fileno() >= 0:
459
+ self._closed = True
460
+ self._trio_socket.close()
461
+
462
+ def _convert_socket_error(self, exc: BaseException) -> NoReturn:
463
+ if isinstance(exc, trio.ClosedResourceError):
464
+ raise ClosedResourceError from exc
465
+ elif self._trio_socket.fileno() < 0 and self._closed:
466
+ raise ClosedResourceError from None
467
+ elif isinstance(exc, OSError):
468
+ raise BrokenResourceError from exc
469
+ else:
470
+ raise exc
471
+
472
+
473
+ class SocketStream(_TrioSocketMixin, abc.SocketStream):
474
+ def __init__(self, trio_socket: TrioSocketType) -> None:
475
+ super().__init__(trio_socket)
476
+ self._receive_guard = ResourceGuard("reading from")
477
+ self._send_guard = ResourceGuard("writing to")
478
+
479
+ async def receive(self, max_bytes: int = 65536) -> bytes:
480
+ with self._receive_guard:
481
+ try:
482
+ data = await self._trio_socket.recv(max_bytes)
483
+ except BaseException as exc:
484
+ self._convert_socket_error(exc)
485
+
486
+ if data:
487
+ return data
488
+ else:
489
+ raise EndOfStream
490
+
491
+ async def send(self, item: bytes) -> None:
492
+ with self._send_guard:
493
+ view = memoryview(item)
494
+ while view:
495
+ try:
496
+ bytes_sent = await self._trio_socket.send(view)
497
+ except BaseException as exc:
498
+ self._convert_socket_error(exc)
499
+
500
+ view = view[bytes_sent:]
501
+
502
+ async def send_eof(self) -> None:
503
+ self._trio_socket.shutdown(socket.SHUT_WR)
504
+
505
+
506
+ class UNIXSocketStream(SocketStream, abc.UNIXSocketStream):
507
+ async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]:
508
+ if not isinstance(msglen, int) or msglen < 0:
509
+ raise ValueError("msglen must be a non-negative integer")
510
+ if not isinstance(maxfds, int) or maxfds < 1:
511
+ raise ValueError("maxfds must be a positive integer")
512
+
513
+ fds = array.array("i")
514
+ await trio.lowlevel.checkpoint()
515
+ with self._receive_guard:
516
+ while True:
517
+ try:
518
+ message, ancdata, flags, addr = await self._trio_socket.recvmsg(
519
+ msglen, socket.CMSG_LEN(maxfds * fds.itemsize)
520
+ )
521
+ except BaseException as exc:
522
+ self._convert_socket_error(exc)
523
+ else:
524
+ if not message and not ancdata:
525
+ raise EndOfStream
526
+
527
+ break
528
+
529
+ for cmsg_level, cmsg_type, cmsg_data in ancdata:
530
+ if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS:
531
+ raise RuntimeError(
532
+ f"Received unexpected ancillary data; message = {message!r}, "
533
+ f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}"
534
+ )
535
+
536
+ fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
537
+
538
+ return message, list(fds)
539
+
540
+ async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None:
541
+ if not message:
542
+ raise ValueError("message must not be empty")
543
+ if not fds:
544
+ raise ValueError("fds must not be empty")
545
+
546
+ filenos: list[int] = []
547
+ for fd in fds:
548
+ if isinstance(fd, int):
549
+ filenos.append(fd)
550
+ elif isinstance(fd, IOBase):
551
+ filenos.append(fd.fileno())
552
+
553
+ fdarray = array.array("i", filenos)
554
+ await trio.lowlevel.checkpoint()
555
+ with self._send_guard:
556
+ while True:
557
+ try:
558
+ await self._trio_socket.sendmsg(
559
+ [message],
560
+ [
561
+ (
562
+ socket.SOL_SOCKET,
563
+ socket.SCM_RIGHTS,
564
+ fdarray,
565
+ )
566
+ ],
567
+ )
568
+ break
569
+ except BaseException as exc:
570
+ self._convert_socket_error(exc)
571
+
572
+
573
+ class TCPSocketListener(_TrioSocketMixin, abc.SocketListener):
574
+ def __init__(self, raw_socket: socket.socket):
575
+ super().__init__(trio.socket.from_stdlib_socket(raw_socket))
576
+ self._accept_guard = ResourceGuard("accepting connections from")
577
+
578
+ async def accept(self) -> SocketStream:
579
+ with self._accept_guard:
580
+ try:
581
+ trio_socket, _addr = await self._trio_socket.accept()
582
+ except BaseException as exc:
583
+ self._convert_socket_error(exc)
584
+
585
+ trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
586
+ return SocketStream(trio_socket)
587
+
588
+
589
+ class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener):
590
+ def __init__(self, raw_socket: socket.socket):
591
+ super().__init__(trio.socket.from_stdlib_socket(raw_socket))
592
+ self._accept_guard = ResourceGuard("accepting connections from")
593
+
594
+ async def accept(self) -> UNIXSocketStream:
595
+ with self._accept_guard:
596
+ try:
597
+ trio_socket, _addr = await self._trio_socket.accept()
598
+ except BaseException as exc:
599
+ self._convert_socket_error(exc)
600
+
601
+ return UNIXSocketStream(trio_socket)
602
+
603
+
604
+ class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket):
605
+ def __init__(self, trio_socket: TrioSocketType) -> None:
606
+ super().__init__(trio_socket)
607
+ self._receive_guard = ResourceGuard("reading from")
608
+ self._send_guard = ResourceGuard("writing to")
609
+
610
+ async def receive(self) -> tuple[bytes, IPSockAddrType]:
611
+ with self._receive_guard:
612
+ try:
613
+ data, addr = await self._trio_socket.recvfrom(65536)
614
+ return data, convert_ipv6_sockaddr(addr)
615
+ except BaseException as exc:
616
+ self._convert_socket_error(exc)
617
+
618
+ async def send(self, item: UDPPacketType) -> None:
619
+ with self._send_guard:
620
+ try:
621
+ await self._trio_socket.sendto(*item)
622
+ except BaseException as exc:
623
+ self._convert_socket_error(exc)
624
+
625
+
626
+ class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket):
627
+ def __init__(self, trio_socket: TrioSocketType) -> None:
628
+ super().__init__(trio_socket)
629
+ self._receive_guard = ResourceGuard("reading from")
630
+ self._send_guard = ResourceGuard("writing to")
631
+
632
+ async def receive(self) -> bytes:
633
+ with self._receive_guard:
634
+ try:
635
+ return await self._trio_socket.recv(65536)
636
+ except BaseException as exc:
637
+ self._convert_socket_error(exc)
638
+
639
+ async def send(self, item: bytes) -> None:
640
+ with self._send_guard:
641
+ try:
642
+ await self._trio_socket.send(item)
643
+ except BaseException as exc:
644
+ self._convert_socket_error(exc)
645
+
646
+
647
+ class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket):
648
+ def __init__(self, trio_socket: TrioSocketType) -> None:
649
+ super().__init__(trio_socket)
650
+ self._receive_guard = ResourceGuard("reading from")
651
+ self._send_guard = ResourceGuard("writing to")
652
+
653
+ async def receive(self) -> UNIXDatagramPacketType:
654
+ with self._receive_guard:
655
+ try:
656
+ data, addr = await self._trio_socket.recvfrom(65536)
657
+ return data, addr
658
+ except BaseException as exc:
659
+ self._convert_socket_error(exc)
660
+
661
+ async def send(self, item: UNIXDatagramPacketType) -> None:
662
+ with self._send_guard:
663
+ try:
664
+ await self._trio_socket.sendto(*item)
665
+ except BaseException as exc:
666
+ self._convert_socket_error(exc)
667
+
668
+
669
+ class ConnectedUNIXDatagramSocket(
670
+ _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket
671
+ ):
672
+ def __init__(self, trio_socket: TrioSocketType) -> None:
673
+ super().__init__(trio_socket)
674
+ self._receive_guard = ResourceGuard("reading from")
675
+ self._send_guard = ResourceGuard("writing to")
676
+
677
+ async def receive(self) -> bytes:
678
+ with self._receive_guard:
679
+ try:
680
+ return await self._trio_socket.recv(65536)
681
+ except BaseException as exc:
682
+ self._convert_socket_error(exc)
683
+
684
+ async def send(self, item: bytes) -> None:
685
+ with self._send_guard:
686
+ try:
687
+ await self._trio_socket.send(item)
688
+ except BaseException as exc:
689
+ self._convert_socket_error(exc)
690
+
691
+
692
+ #
693
+ # Synchronization
694
+ #
695
+
696
+
697
+ class Event(BaseEvent):
698
+ __slots__ = ("__original",)
699
+
700
+ def __new__(cls) -> Event:
701
+ return object.__new__(cls)
702
+
703
+ def __init__(self) -> None:
704
+ self.__original = trio.Event()
705
+
706
+ def is_set(self) -> bool:
707
+ return self.__original.is_set()
708
+
709
+ async def wait(self) -> None:
710
+ return await self.__original.wait()
711
+
712
+ def statistics(self) -> EventStatistics:
713
+ orig_statistics = self.__original.statistics()
714
+ return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting)
715
+
716
+ def set(self) -> None:
717
+ self.__original.set()
718
+
719
+
720
+ class Lock(BaseLock):
721
+ __slots__ = "_fast_acquire", "__original"
722
+
723
+ def __new__(cls, *, fast_acquire: bool = False) -> Lock:
724
+ return object.__new__(cls)
725
+
726
+ def __init__(self, *, fast_acquire: bool = False) -> None:
727
+ self._fast_acquire = fast_acquire
728
+ self.__original = trio.Lock()
729
+
730
+ @staticmethod
731
+ def _convert_runtime_error_msg(exc: RuntimeError) -> None:
732
+ if exc.args == ("attempt to re-acquire an already held Lock",):
733
+ exc.args = ("Attempted to acquire an already held Lock",)
734
+
735
+ async def acquire(self) -> None:
736
+ if not self._fast_acquire:
737
+ try:
738
+ await self.__original.acquire()
739
+ except RuntimeError as exc:
740
+ self._convert_runtime_error_msg(exc)
741
+ raise
742
+
743
+ return
744
+
745
+ # This is the "fast path" where we don't let other tasks run
746
+ await trio.lowlevel.checkpoint_if_cancelled()
747
+ try:
748
+ self.__original.acquire_nowait()
749
+ except trio.WouldBlock:
750
+ await self.__original._lot.park()
751
+ except RuntimeError as exc:
752
+ self._convert_runtime_error_msg(exc)
753
+ raise
754
+
755
+ def acquire_nowait(self) -> None:
756
+ try:
757
+ self.__original.acquire_nowait()
758
+ except trio.WouldBlock:
759
+ raise WouldBlock from None
760
+ except RuntimeError as exc:
761
+ self._convert_runtime_error_msg(exc)
762
+ raise
763
+
764
+ def locked(self) -> bool:
765
+ return self.__original.locked()
766
+
767
+ def release(self) -> None:
768
+ self.__original.release()
769
+
770
+ def statistics(self) -> LockStatistics:
771
+ orig_statistics = self.__original.statistics()
772
+ owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None
773
+ return LockStatistics(
774
+ orig_statistics.locked, owner, orig_statistics.tasks_waiting
775
+ )
776
+
777
+
778
+ class Semaphore(BaseSemaphore):
779
+ __slots__ = ("__original",)
780
+
781
+ def __new__(
782
+ cls,
783
+ initial_value: int,
784
+ *,
785
+ max_value: int | None = None,
786
+ fast_acquire: bool = False,
787
+ ) -> Semaphore:
788
+ return object.__new__(cls)
789
+
790
+ def __init__(
791
+ self,
792
+ initial_value: int,
793
+ *,
794
+ max_value: int | None = None,
795
+ fast_acquire: bool = False,
796
+ ) -> None:
797
+ super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire)
798
+ self.__original = trio.Semaphore(initial_value, max_value=max_value)
799
+
800
+ async def acquire(self) -> None:
801
+ if not self._fast_acquire:
802
+ await self.__original.acquire()
803
+ return
804
+
805
+ # This is the "fast path" where we don't let other tasks run
806
+ await trio.lowlevel.checkpoint_if_cancelled()
807
+ try:
808
+ self.__original.acquire_nowait()
809
+ except trio.WouldBlock:
810
+ await self.__original._lot.park()
811
+
812
+ def acquire_nowait(self) -> None:
813
+ try:
814
+ self.__original.acquire_nowait()
815
+ except trio.WouldBlock:
816
+ raise WouldBlock from None
817
+
818
+ @property
819
+ def max_value(self) -> int | None:
820
+ return self.__original.max_value
821
+
822
+ @property
823
+ def value(self) -> int:
824
+ return self.__original.value
825
+
826
+ def release(self) -> None:
827
+ self.__original.release()
828
+
829
+ def statistics(self) -> SemaphoreStatistics:
830
+ orig_statistics = self.__original.statistics()
831
+ return SemaphoreStatistics(orig_statistics.tasks_waiting)
832
+
833
+
834
+ class CapacityLimiter(BaseCapacityLimiter):
835
+ __slots__ = ("__original",)
836
+
837
+ def __new__(
838
+ cls,
839
+ total_tokens: float | None = None,
840
+ *,
841
+ original: trio.CapacityLimiter | None = None,
842
+ ) -> CapacityLimiter:
843
+ return object.__new__(cls)
844
+
845
+ def __init__(
846
+ self,
847
+ total_tokens: float | None = None,
848
+ *,
849
+ original: trio.CapacityLimiter | None = None,
850
+ ) -> None:
851
+ if original is not None:
852
+ self.__original = original
853
+ else:
854
+ assert total_tokens is not None
855
+ self.__original = trio.CapacityLimiter(total_tokens)
856
+
857
+ async def __aenter__(self) -> None:
858
+ return await self.__original.__aenter__()
859
+
860
+ async def __aexit__(
861
+ self,
862
+ exc_type: type[BaseException] | None,
863
+ exc_val: BaseException | None,
864
+ exc_tb: TracebackType | None,
865
+ ) -> None:
866
+ await self.__original.__aexit__(exc_type, exc_val, exc_tb)
867
+
868
+ @property
869
+ def total_tokens(self) -> float:
870
+ return self.__original.total_tokens
871
+
872
+ @total_tokens.setter
873
+ def total_tokens(self, value: float) -> None:
874
+ self.__original.total_tokens = value
875
+
876
+ @property
877
+ def borrowed_tokens(self) -> int:
878
+ return self.__original.borrowed_tokens
879
+
880
+ @property
881
+ def available_tokens(self) -> float:
882
+ return self.__original.available_tokens
883
+
884
+ def acquire_nowait(self) -> None:
885
+ self.__original.acquire_nowait()
886
+
887
+ def acquire_on_behalf_of_nowait(self, borrower: object) -> None:
888
+ self.__original.acquire_on_behalf_of_nowait(borrower)
889
+
890
+ async def acquire(self) -> None:
891
+ await self.__original.acquire()
892
+
893
+ async def acquire_on_behalf_of(self, borrower: object) -> None:
894
+ await self.__original.acquire_on_behalf_of(borrower)
895
+
896
+ def release(self) -> None:
897
+ return self.__original.release()
898
+
899
+ def release_on_behalf_of(self, borrower: object) -> None:
900
+ return self.__original.release_on_behalf_of(borrower)
901
+
902
+ def statistics(self) -> CapacityLimiterStatistics:
903
+ orig = self.__original.statistics()
904
+ return CapacityLimiterStatistics(
905
+ borrowed_tokens=orig.borrowed_tokens,
906
+ total_tokens=orig.total_tokens,
907
+ borrowers=tuple(orig.borrowers),
908
+ tasks_waiting=orig.tasks_waiting,
909
+ )
910
+
911
+
912
+ _capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper")
913
+
914
+
915
+ #
916
+ # Signal handling
917
+ #
918
+
919
+
920
+ class _SignalReceiver:
921
+ _iterator: AsyncIterator[int]
922
+
923
+ def __init__(self, signals: tuple[Signals, ...]):
924
+ self._signals = signals
925
+
926
+ def __enter__(self) -> _SignalReceiver:
927
+ self._cm = trio.open_signal_receiver(*self._signals)
928
+ self._iterator = self._cm.__enter__()
929
+ return self
930
+
931
+ def __exit__(
932
+ self,
933
+ exc_type: type[BaseException] | None,
934
+ exc_val: BaseException | None,
935
+ exc_tb: TracebackType | None,
936
+ ) -> bool | None:
937
+ return self._cm.__exit__(exc_type, exc_val, exc_tb)
938
+
939
+ def __aiter__(self) -> _SignalReceiver:
940
+ return self
941
+
942
+ async def __anext__(self) -> Signals:
943
+ signum = await self._iterator.__anext__()
944
+ return Signals(signum)
945
+
946
+
947
+ #
948
+ # Testing and debugging
949
+ #
950
+
951
+
952
+ class TestRunner(abc.TestRunner):
953
+ def __init__(self, **options: Any) -> None:
954
+ from queue import Queue
955
+
956
+ self._call_queue: Queue[Callable[[], object]] = Queue()
957
+ self._send_stream: (
958
+ MemoryObjectSendStream[tuple[Awaitable[Any], list[Outcome]]] | None
959
+ ) = None
960
+ self._options = options
961
+
962
+ def __exit__(
963
+ self,
964
+ exc_type: type[BaseException] | None,
965
+ exc_val: BaseException | None,
966
+ exc_tb: types.TracebackType | None,
967
+ ) -> None:
968
+ if self._send_stream:
969
+ self._send_stream.close()
970
+ while self._send_stream is not None:
971
+ self._call_queue.get()()
972
+
973
+ def is_running(self) -> bool:
974
+ return trio.lowlevel.in_trio_task()
975
+
976
+ async def _run_tests_and_fixtures(self) -> None:
977
+ self._send_stream, receive_stream = create_memory_object_stream[
978
+ tuple[Awaitable[Any], list[Outcome]]
979
+ ](1)
980
+ with receive_stream:
981
+ async for awaitable, outcome_holder in receive_stream:
982
+ try:
983
+ retval = await awaitable
984
+ except BaseException as exc:
985
+ outcome_holder.append(Error(exc))
986
+ else:
987
+ outcome_holder.append(Value(retval))
988
+
989
+ def _main_task_finished(self, outcome: object) -> None:
990
+ self._send_stream = None
991
+
992
+ def _call_in_runner_task(
993
+ self,
994
+ func: Callable[P, Awaitable[T_Retval]],
995
+ /,
996
+ *args: P.args,
997
+ **kwargs: P.kwargs,
998
+ ) -> T_Retval:
999
+ if self._send_stream is None:
1000
+ trio.lowlevel.start_guest_run(
1001
+ self._run_tests_and_fixtures,
1002
+ run_sync_soon_threadsafe=self._call_queue.put,
1003
+ done_callback=self._main_task_finished,
1004
+ **self._options,
1005
+ )
1006
+ while self._send_stream is None:
1007
+ self._call_queue.get()()
1008
+
1009
+ outcome_holder: list[Outcome] = []
1010
+ self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder))
1011
+ while not outcome_holder:
1012
+ self._call_queue.get()()
1013
+
1014
+ return outcome_holder[0].unwrap()
1015
+
1016
+ def run_asyncgen_fixture(
1017
+ self,
1018
+ fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]],
1019
+ kwargs: dict[str, Any],
1020
+ ) -> Iterable[T_Retval]:
1021
+ asyncgen = fixture_func(**kwargs)
1022
+ fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None)
1023
+
1024
+ yield fixturevalue
1025
+
1026
+ try:
1027
+ self._call_in_runner_task(asyncgen.asend, None)
1028
+ except StopAsyncIteration:
1029
+ pass
1030
+ else:
1031
+ self._call_in_runner_task(asyncgen.aclose)
1032
+ raise RuntimeError("Async generator fixture did not stop")
1033
+
1034
+ def run_fixture(
1035
+ self,
1036
+ fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]],
1037
+ kwargs: dict[str, Any],
1038
+ ) -> T_Retval:
1039
+ return self._call_in_runner_task(fixture_func, **kwargs)
1040
+
1041
+ def run_test(
1042
+ self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any]
1043
+ ) -> None:
1044
+ self._call_in_runner_task(test_func, **kwargs)
1045
+
1046
+
1047
+ class TrioTaskInfo(TaskInfo):
1048
+ def __init__(self, task: trio.lowlevel.Task):
1049
+ parent_id = None
1050
+ if task.parent_nursery and task.parent_nursery.parent_task:
1051
+ parent_id = id(task.parent_nursery.parent_task)
1052
+
1053
+ super().__init__(id(task), parent_id, task.name, task.coro)
1054
+ self._task = weakref.proxy(task)
1055
+
1056
+ def has_pending_cancellation(self) -> bool:
1057
+ try:
1058
+ return self._task._cancel_status.effectively_cancelled
1059
+ except ReferenceError:
1060
+ # If the task is no longer around, it surely doesn't have a cancellation
1061
+ # pending
1062
+ return False
1063
+
1064
+
1065
+ class TrioBackend(AsyncBackend):
1066
+ @classmethod
1067
+ def run(
1068
+ cls,
1069
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
1070
+ args: tuple[Unpack[PosArgsT]],
1071
+ kwargs: dict[str, Any],
1072
+ options: dict[str, Any],
1073
+ ) -> T_Retval:
1074
+ assert not kwargs, "unreachable, and not supported by Trio"
1075
+ return trio.run(ensure_returns_coro(func), *args, **options)
1076
+
1077
+ @classmethod
1078
+ def current_token(cls) -> object:
1079
+ return trio.lowlevel.current_trio_token()
1080
+
1081
+ @classmethod
1082
+ def current_time(cls) -> float:
1083
+ return trio.current_time()
1084
+
1085
+ @classmethod
1086
+ def cancelled_exception_class(cls) -> type[BaseException]:
1087
+ return trio.Cancelled
1088
+
1089
+ @classmethod
1090
+ async def checkpoint(cls) -> None:
1091
+ await trio.lowlevel.checkpoint()
1092
+
1093
+ @classmethod
1094
+ async def checkpoint_if_cancelled(cls) -> None:
1095
+ await trio.lowlevel.checkpoint_if_cancelled()
1096
+
1097
+ @classmethod
1098
+ async def cancel_shielded_checkpoint(cls) -> None:
1099
+ await trio.lowlevel.cancel_shielded_checkpoint()
1100
+
1101
+ @classmethod
1102
+ async def sleep(cls, delay: float) -> None:
1103
+ await trio.sleep(delay)
1104
+
1105
+ @classmethod
1106
+ def create_cancel_scope(
1107
+ cls, *, deadline: float = math.inf, shield: bool = False
1108
+ ) -> abc.CancelScope:
1109
+ return CancelScope(deadline=deadline, shield=shield)
1110
+
1111
+ @classmethod
1112
+ def current_effective_deadline(cls) -> float:
1113
+ return trio.current_effective_deadline()
1114
+
1115
+ @classmethod
1116
+ def create_task_group(cls) -> abc.TaskGroup:
1117
+ return TaskGroup()
1118
+
1119
+ @classmethod
1120
+ def create_event(cls) -> abc.Event:
1121
+ return Event()
1122
+
1123
+ @classmethod
1124
+ def create_lock(cls, *, fast_acquire: bool) -> Lock:
1125
+ return Lock(fast_acquire=fast_acquire)
1126
+
1127
+ @classmethod
1128
+ def create_semaphore(
1129
+ cls,
1130
+ initial_value: int,
1131
+ *,
1132
+ max_value: int | None = None,
1133
+ fast_acquire: bool = False,
1134
+ ) -> abc.Semaphore:
1135
+ return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire)
1136
+
1137
+ @classmethod
1138
+ def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter:
1139
+ return CapacityLimiter(total_tokens)
1140
+
1141
+ @classmethod
1142
+ async def run_sync_in_worker_thread(
1143
+ cls,
1144
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
1145
+ args: tuple[Unpack[PosArgsT]],
1146
+ abandon_on_cancel: bool = False,
1147
+ limiter: abc.CapacityLimiter | None = None,
1148
+ ) -> T_Retval:
1149
+ def wrapper() -> T_Retval:
1150
+ with claim_worker_thread(TrioBackend, token):
1151
+ return func(*args)
1152
+
1153
+ token = TrioBackend.current_token()
1154
+ return await run_sync(
1155
+ wrapper,
1156
+ abandon_on_cancel=abandon_on_cancel,
1157
+ limiter=cast(trio.CapacityLimiter, limiter),
1158
+ )
1159
+
1160
+ @classmethod
1161
+ def check_cancelled(cls) -> None:
1162
+ trio.from_thread.check_cancelled()
1163
+
1164
+ @classmethod
1165
+ def run_async_from_thread(
1166
+ cls,
1167
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
1168
+ args: tuple[Unpack[PosArgsT]],
1169
+ token: object,
1170
+ ) -> T_co:
1171
+ trio_token = cast("trio.lowlevel.TrioToken | None", token)
1172
+ try:
1173
+ return trio.from_thread.run(func, *args, trio_token=trio_token)
1174
+ except trio.RunFinishedError:
1175
+ raise RunFinishedError from None
1176
+
1177
+ @classmethod
1178
+ def run_sync_from_thread(
1179
+ cls,
1180
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
1181
+ args: tuple[Unpack[PosArgsT]],
1182
+ token: object,
1183
+ ) -> T_Retval:
1184
+ trio_token = cast("trio.lowlevel.TrioToken | None", token)
1185
+ try:
1186
+ return trio.from_thread.run_sync(func, *args, trio_token=trio_token)
1187
+ except trio.RunFinishedError:
1188
+ raise RunFinishedError from None
1189
+
1190
+ @classmethod
1191
+ async def open_process(
1192
+ cls,
1193
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
1194
+ *,
1195
+ stdin: int | IO[Any] | None,
1196
+ stdout: int | IO[Any] | None,
1197
+ stderr: int | IO[Any] | None,
1198
+ **kwargs: Any,
1199
+ ) -> Process:
1200
+ def convert_item(item: StrOrBytesPath) -> str:
1201
+ str_or_bytes = os.fspath(item)
1202
+ if isinstance(str_or_bytes, str):
1203
+ return str_or_bytes
1204
+ else:
1205
+ return os.fsdecode(str_or_bytes)
1206
+
1207
+ if isinstance(command, (str, bytes, PathLike)):
1208
+ process = await trio.lowlevel.open_process(
1209
+ convert_item(command),
1210
+ stdin=stdin,
1211
+ stdout=stdout,
1212
+ stderr=stderr,
1213
+ shell=True,
1214
+ **kwargs,
1215
+ )
1216
+ else:
1217
+ process = await trio.lowlevel.open_process(
1218
+ [convert_item(item) for item in command],
1219
+ stdin=stdin,
1220
+ stdout=stdout,
1221
+ stderr=stderr,
1222
+ shell=False,
1223
+ **kwargs,
1224
+ )
1225
+
1226
+ stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None
1227
+ stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None
1228
+ stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None
1229
+ return Process(process, stdin_stream, stdout_stream, stderr_stream)
1230
+
1231
+ @classmethod
1232
+ def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None:
1233
+ trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers)
1234
+
1235
+ @classmethod
1236
+ async def connect_tcp(
1237
+ cls, host: str, port: int, local_address: IPSockAddrType | None = None
1238
+ ) -> SocketStream:
1239
+ family = socket.AF_INET6 if ":" in host else socket.AF_INET
1240
+ trio_socket = trio.socket.socket(family)
1241
+ trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
1242
+ if local_address:
1243
+ await trio_socket.bind(local_address)
1244
+
1245
+ try:
1246
+ await trio_socket.connect((host, port))
1247
+ except BaseException:
1248
+ trio_socket.close()
1249
+ raise
1250
+
1251
+ return SocketStream(trio_socket)
1252
+
1253
+ @classmethod
1254
+ async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream:
1255
+ trio_socket = trio.socket.socket(socket.AF_UNIX)
1256
+ try:
1257
+ await trio_socket.connect(path)
1258
+ except BaseException:
1259
+ trio_socket.close()
1260
+ raise
1261
+
1262
+ return UNIXSocketStream(trio_socket)
1263
+
1264
+ @classmethod
1265
+ def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener:
1266
+ return TCPSocketListener(sock)
1267
+
1268
+ @classmethod
1269
+ def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener:
1270
+ return UNIXSocketListener(sock)
1271
+
1272
+ @classmethod
1273
+ async def create_udp_socket(
1274
+ cls,
1275
+ family: socket.AddressFamily,
1276
+ local_address: IPSockAddrType | None,
1277
+ remote_address: IPSockAddrType | None,
1278
+ reuse_port: bool,
1279
+ ) -> UDPSocket | ConnectedUDPSocket:
1280
+ trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM)
1281
+
1282
+ if reuse_port:
1283
+ trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
1284
+
1285
+ if local_address:
1286
+ await trio_socket.bind(local_address)
1287
+
1288
+ if remote_address:
1289
+ await trio_socket.connect(remote_address)
1290
+ return ConnectedUDPSocket(trio_socket)
1291
+ else:
1292
+ return UDPSocket(trio_socket)
1293
+
1294
+ @classmethod
1295
+ @overload
1296
+ async def create_unix_datagram_socket(
1297
+ cls, raw_socket: socket.socket, remote_path: None
1298
+ ) -> abc.UNIXDatagramSocket: ...
1299
+
1300
+ @classmethod
1301
+ @overload
1302
+ async def create_unix_datagram_socket(
1303
+ cls, raw_socket: socket.socket, remote_path: str | bytes
1304
+ ) -> abc.ConnectedUNIXDatagramSocket: ...
1305
+
1306
+ @classmethod
1307
+ async def create_unix_datagram_socket(
1308
+ cls, raw_socket: socket.socket, remote_path: str | bytes | None
1309
+ ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket:
1310
+ trio_socket = trio.socket.from_stdlib_socket(raw_socket)
1311
+
1312
+ if remote_path:
1313
+ await trio_socket.connect(remote_path)
1314
+ return ConnectedUNIXDatagramSocket(trio_socket)
1315
+ else:
1316
+ return UNIXDatagramSocket(trio_socket)
1317
+
1318
+ @classmethod
1319
+ async def getaddrinfo(
1320
+ cls,
1321
+ host: bytes | str | None,
1322
+ port: str | int | None,
1323
+ *,
1324
+ family: int | AddressFamily = 0,
1325
+ type: int | SocketKind = 0,
1326
+ proto: int = 0,
1327
+ flags: int = 0,
1328
+ ) -> Sequence[
1329
+ tuple[
1330
+ AddressFamily,
1331
+ SocketKind,
1332
+ int,
1333
+ str,
1334
+ tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],
1335
+ ]
1336
+ ]:
1337
+ return await trio.socket.getaddrinfo(host, port, family, type, proto, flags)
1338
+
1339
+ @classmethod
1340
+ async def getnameinfo(
1341
+ cls, sockaddr: IPSockAddrType, flags: int = 0
1342
+ ) -> tuple[str, str]:
1343
+ return await trio.socket.getnameinfo(sockaddr, flags)
1344
+
1345
+ @classmethod
1346
+ async def wait_readable(cls, obj: FileDescriptorLike) -> None:
1347
+ try:
1348
+ await wait_readable(obj)
1349
+ except trio.ClosedResourceError as exc:
1350
+ raise ClosedResourceError().with_traceback(exc.__traceback__) from None
1351
+ except trio.BusyResourceError:
1352
+ raise BusyResourceError("reading from") from None
1353
+
1354
+ @classmethod
1355
+ async def wait_writable(cls, obj: FileDescriptorLike) -> None:
1356
+ try:
1357
+ await wait_writable(obj)
1358
+ except trio.ClosedResourceError as exc:
1359
+ raise ClosedResourceError().with_traceback(exc.__traceback__) from None
1360
+ except trio.BusyResourceError:
1361
+ raise BusyResourceError("writing to") from None
1362
+
1363
+ @classmethod
1364
+ def notify_closing(cls, obj: FileDescriptorLike) -> None:
1365
+ notify_closing(obj)
1366
+
1367
+ @classmethod
1368
+ async def wrap_listener_socket(cls, sock: socket.socket) -> abc.SocketListener:
1369
+ if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX:
1370
+ return UNIXSocketListener(sock)
1371
+
1372
+ return TCPSocketListener(sock)
1373
+
1374
+ @classmethod
1375
+ async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream:
1376
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1377
+ return SocketStream(trio_sock)
1378
+
1379
+ @classmethod
1380
+ async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream:
1381
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1382
+ return UNIXSocketStream(trio_sock)
1383
+
1384
+ @classmethod
1385
+ async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket:
1386
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1387
+ return UDPSocket(trio_sock)
1388
+
1389
+ @classmethod
1390
+ async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket:
1391
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1392
+ return ConnectedUDPSocket(trio_sock)
1393
+
1394
+ @classmethod
1395
+ async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket:
1396
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1397
+ return UNIXDatagramSocket(trio_sock)
1398
+
1399
+ @classmethod
1400
+ async def wrap_connected_unix_datagram_socket(
1401
+ cls, sock: socket.socket
1402
+ ) -> ConnectedUNIXDatagramSocket:
1403
+ trio_sock = trio.socket.from_stdlib_socket(sock)
1404
+ return ConnectedUNIXDatagramSocket(trio_sock)
1405
+
1406
+ @classmethod
1407
+ def current_default_thread_limiter(cls) -> CapacityLimiter:
1408
+ try:
1409
+ return _capacity_limiter_wrapper.get()
1410
+ except LookupError:
1411
+ limiter = CapacityLimiter(
1412
+ original=trio.to_thread.current_default_thread_limiter()
1413
+ )
1414
+ _capacity_limiter_wrapper.set(limiter)
1415
+ return limiter
1416
+
1417
+ @classmethod
1418
+ def open_signal_receiver(
1419
+ cls, *signals: Signals
1420
+ ) -> AbstractContextManager[AsyncIterator[Signals]]:
1421
+ return _SignalReceiver(signals)
1422
+
1423
+ @classmethod
1424
+ def get_current_task(cls) -> TaskInfo:
1425
+ task = current_task()
1426
+ return TrioTaskInfo(task)
1427
+
1428
+ @classmethod
1429
+ def get_running_tasks(cls) -> Sequence[TaskInfo]:
1430
+ root_task = current_root_task()
1431
+ assert root_task
1432
+ task_infos = [TrioTaskInfo(root_task)]
1433
+ nurseries = root_task.child_nurseries
1434
+ while nurseries:
1435
+ new_nurseries: list[trio.Nursery] = []
1436
+ for nursery in nurseries:
1437
+ for task in nursery.child_tasks:
1438
+ task_infos.append(TrioTaskInfo(task))
1439
+ new_nurseries.extend(task.child_nurseries)
1440
+
1441
+ nurseries = new_nurseries
1442
+
1443
+ return task_infos
1444
+
1445
+ @classmethod
1446
+ async def wait_all_tasks_blocked(cls) -> None:
1447
+ from trio.testing import wait_all_tasks_blocked
1448
+
1449
+ await wait_all_tasks_blocked()
1450
+
1451
+ @classmethod
1452
+ def create_test_runner(cls, options: dict[str, Any]) -> TestRunner:
1453
+ return TestRunner(**options)
1454
+
1455
+
1456
+ backend_class = TrioBackend
.venv/Lib/site-packages/anyio/_core/__init__.py ADDED
File without changes
.venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import socket
5
+ import threading
6
+ from collections.abc import Callable
7
+ from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ if TYPE_CHECKING:
11
+ from _typeshed import FileDescriptorLike
12
+
13
+ _selector_lock = threading.Lock()
14
+ _selector: Selector | None = None
15
+
16
+
17
+ class Selector:
18
+ def __init__(self) -> None:
19
+ self._thread = threading.Thread(target=self.run, name="AnyIO socket selector")
20
+ self._selector = DefaultSelector()
21
+ self._send, self._receive = socket.socketpair()
22
+ self._send.setblocking(False)
23
+ self._receive.setblocking(False)
24
+ # This somewhat reduces the amount of memory wasted queueing up data
25
+ # for wakeups. With these settings, maximum number of 1-byte sends
26
+ # before getting BlockingIOError:
27
+ # Linux 4.8: 6
28
+ # macOS (darwin 15.5): 1
29
+ # Windows 10: 525347
30
+ # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send
31
+ # blocking, even on non-blocking sockets, so don't do that.)
32
+ self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1)
33
+ self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
34
+ # On Windows this is a TCP socket so this might matter. On other
35
+ # platforms this fails b/c AF_UNIX sockets aren't actually TCP.
36
+ try:
37
+ self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
38
+ except OSError:
39
+ pass
40
+
41
+ self._selector.register(self._receive, EVENT_READ)
42
+ self._closed = False
43
+
44
+ def start(self) -> None:
45
+ self._thread.start()
46
+ threading._register_atexit(self._stop) # type: ignore[attr-defined]
47
+
48
+ def _stop(self) -> None:
49
+ global _selector
50
+ self._closed = True
51
+ self._notify_self()
52
+ self._send.close()
53
+ self._thread.join()
54
+ self._selector.unregister(self._receive)
55
+ self._receive.close()
56
+ self._selector.close()
57
+ _selector = None
58
+ assert not self._selector.get_map(), (
59
+ "selector still has registered file descriptors after shutdown"
60
+ )
61
+
62
+ def _notify_self(self) -> None:
63
+ try:
64
+ self._send.send(b"\x00")
65
+ except BlockingIOError:
66
+ pass
67
+
68
+ def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None:
69
+ loop = asyncio.get_running_loop()
70
+ try:
71
+ key = self._selector.get_key(fd)
72
+ except KeyError:
73
+ self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)})
74
+ else:
75
+ if EVENT_READ in key.data:
76
+ raise ValueError(
77
+ "this file descriptor is already registered for reading"
78
+ )
79
+
80
+ key.data[EVENT_READ] = loop, callback
81
+ self._selector.modify(fd, key.events | EVENT_READ, key.data)
82
+
83
+ self._notify_self()
84
+
85
+ def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None:
86
+ loop = asyncio.get_running_loop()
87
+ try:
88
+ key = self._selector.get_key(fd)
89
+ except KeyError:
90
+ self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)})
91
+ else:
92
+ if EVENT_WRITE in key.data:
93
+ raise ValueError(
94
+ "this file descriptor is already registered for writing"
95
+ )
96
+
97
+ key.data[EVENT_WRITE] = loop, callback
98
+ self._selector.modify(fd, key.events | EVENT_WRITE, key.data)
99
+
100
+ self._notify_self()
101
+
102
+ def remove_reader(self, fd: FileDescriptorLike) -> bool:
103
+ try:
104
+ key = self._selector.get_key(fd)
105
+ except KeyError:
106
+ return False
107
+
108
+ if new_events := key.events ^ EVENT_READ:
109
+ del key.data[EVENT_READ]
110
+ self._selector.modify(fd, new_events, key.data)
111
+ else:
112
+ self._selector.unregister(fd)
113
+
114
+ return True
115
+
116
+ def remove_writer(self, fd: FileDescriptorLike) -> bool:
117
+ try:
118
+ key = self._selector.get_key(fd)
119
+ except KeyError:
120
+ return False
121
+
122
+ if new_events := key.events ^ EVENT_WRITE:
123
+ del key.data[EVENT_WRITE]
124
+ self._selector.modify(fd, new_events, key.data)
125
+ else:
126
+ self._selector.unregister(fd)
127
+
128
+ return True
129
+
130
+ def run(self) -> None:
131
+ while not self._closed:
132
+ for key, events in self._selector.select():
133
+ if key.fileobj is self._receive:
134
+ try:
135
+ while self._receive.recv(4096):
136
+ pass
137
+ except BlockingIOError:
138
+ pass
139
+
140
+ continue
141
+
142
+ if events & EVENT_READ:
143
+ loop, callback = key.data[EVENT_READ]
144
+ self.remove_reader(key.fd)
145
+ try:
146
+ loop.call_soon_threadsafe(callback)
147
+ except RuntimeError:
148
+ pass # the loop was already closed
149
+
150
+ if events & EVENT_WRITE:
151
+ loop, callback = key.data[EVENT_WRITE]
152
+ self.remove_writer(key.fd)
153
+ try:
154
+ loop.call_soon_threadsafe(callback)
155
+ except RuntimeError:
156
+ pass # the loop was already closed
157
+
158
+
159
+ def get_selector() -> Selector:
160
+ global _selector
161
+
162
+ with _selector_lock:
163
+ if _selector is None:
164
+ _selector = Selector()
165
+ _selector.start()
166
+
167
+ return _selector
.venv/Lib/site-packages/anyio/_core/_contextmanagers.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from contextlib import AbstractAsyncContextManager, AbstractContextManager
5
+ from inspect import isasyncgen, iscoroutine, isgenerator
6
+ from types import TracebackType
7
+ from typing import Protocol, TypeVar, cast, final
8
+
9
+ _T_co = TypeVar("_T_co", covariant=True)
10
+ _ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None")
11
+
12
+
13
+ class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]):
14
+ def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ...
15
+
16
+
17
+ class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]):
18
+ def __asynccontextmanager__(
19
+ self,
20
+ ) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ...
21
+
22
+
23
+ class ContextManagerMixin:
24
+ """
25
+ Mixin class providing context manager functionality via a generator-based
26
+ implementation.
27
+
28
+ This class allows you to implement a context manager via :meth:`__contextmanager__`
29
+ which should return a generator. The mechanics are meant to mirror those of
30
+ :func:`@contextmanager <contextlib.contextmanager>`.
31
+
32
+ .. note:: Classes using this mix-in are not reentrant as context managers, meaning
33
+ that once you enter it, you can't re-enter before first exiting it.
34
+
35
+ .. seealso:: :doc:`contextmanagers`
36
+ """
37
+
38
+ __cm: AbstractContextManager[object, bool | None] | None = None
39
+
40
+ @final
41
+ def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co:
42
+ # Needed for mypy to assume self still has the __cm member
43
+ assert isinstance(self, ContextManagerMixin)
44
+ if self.__cm is not None:
45
+ raise RuntimeError(
46
+ f"this {self.__class__.__qualname__} has already been entered"
47
+ )
48
+
49
+ cm = self.__contextmanager__()
50
+ if not isinstance(cm, AbstractContextManager):
51
+ if isgenerator(cm):
52
+ raise TypeError(
53
+ "__contextmanager__() returned a generator object instead of "
54
+ "a context manager. Did you forget to add the @contextmanager "
55
+ "decorator?"
56
+ )
57
+
58
+ raise TypeError(
59
+ f"__contextmanager__() did not return a context manager object, "
60
+ f"but {cm.__class__!r}"
61
+ )
62
+
63
+ if cm is self:
64
+ raise TypeError(
65
+ f"{self.__class__.__qualname__}.__contextmanager__() returned "
66
+ f"self. Did you forget to add the @contextmanager decorator and a "
67
+ f"'yield' statement?"
68
+ )
69
+
70
+ value = cm.__enter__()
71
+ self.__cm = cm
72
+ return value
73
+
74
+ @final
75
+ def __exit__(
76
+ self: _SupportsCtxMgr[object, _ExitT_co],
77
+ exc_type: type[BaseException] | None,
78
+ exc_val: BaseException | None,
79
+ exc_tb: TracebackType | None,
80
+ ) -> _ExitT_co:
81
+ # Needed for mypy to assume self still has the __cm member
82
+ assert isinstance(self, ContextManagerMixin)
83
+ if self.__cm is None:
84
+ raise RuntimeError(
85
+ f"this {self.__class__.__qualname__} has not been entered yet"
86
+ )
87
+
88
+ # Prevent circular references
89
+ cm = self.__cm
90
+ del self.__cm
91
+
92
+ return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb))
93
+
94
+ @abstractmethod
95
+ def __contextmanager__(self) -> AbstractContextManager[object, bool | None]:
96
+ """
97
+ Implement your context manager logic here.
98
+
99
+ This method **must** be decorated with
100
+ :func:`@contextmanager <contextlib.contextmanager>`.
101
+
102
+ .. note:: Remember that the ``yield`` will raise any exception raised in the
103
+ enclosed context block, so use a ``finally:`` block to clean up resources!
104
+
105
+ :return: a context manager object
106
+ """
107
+
108
+
109
+ class AsyncContextManagerMixin:
110
+ """
111
+ Mixin class providing async context manager functionality via a generator-based
112
+ implementation.
113
+
114
+ This class allows you to implement a context manager via
115
+ :meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of
116
+ :func:`@asynccontextmanager <contextlib.asynccontextmanager>`.
117
+
118
+ .. note:: Classes using this mix-in are not reentrant as context managers, meaning
119
+ that once you enter it, you can't re-enter before first exiting it.
120
+
121
+ .. seealso:: :doc:`contextmanagers`
122
+ """
123
+
124
+ __cm: AbstractAsyncContextManager[object, bool | None] | None = None
125
+
126
+ @final
127
+ async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co:
128
+ # Needed for mypy to assume self still has the __cm member
129
+ assert isinstance(self, AsyncContextManagerMixin)
130
+ if self.__cm is not None:
131
+ raise RuntimeError(
132
+ f"this {self.__class__.__qualname__} has already been entered"
133
+ )
134
+
135
+ cm = self.__asynccontextmanager__()
136
+ if not isinstance(cm, AbstractAsyncContextManager):
137
+ if isasyncgen(cm):
138
+ raise TypeError(
139
+ "__asynccontextmanager__() returned an async generator instead of "
140
+ "an async context manager. Did you forget to add the "
141
+ "@asynccontextmanager decorator?"
142
+ )
143
+ elif iscoroutine(cm):
144
+ cm.close()
145
+ raise TypeError(
146
+ "__asynccontextmanager__() returned a coroutine object instead of "
147
+ "an async context manager. Did you forget to add the "
148
+ "@asynccontextmanager decorator and a 'yield' statement?"
149
+ )
150
+
151
+ raise TypeError(
152
+ f"__asynccontextmanager__() did not return an async context manager, "
153
+ f"but {cm.__class__!r}"
154
+ )
155
+
156
+ if cm is self:
157
+ raise TypeError(
158
+ f"{self.__class__.__qualname__}.__asynccontextmanager__() returned "
159
+ f"self. Did you forget to add the @asynccontextmanager decorator and a "
160
+ f"'yield' statement?"
161
+ )
162
+
163
+ value = await cm.__aenter__()
164
+ self.__cm = cm
165
+ return value
166
+
167
+ @final
168
+ async def __aexit__(
169
+ self: _SupportsAsyncCtxMgr[object, _ExitT_co],
170
+ exc_type: type[BaseException] | None,
171
+ exc_val: BaseException | None,
172
+ exc_tb: TracebackType | None,
173
+ ) -> _ExitT_co:
174
+ assert isinstance(self, AsyncContextManagerMixin)
175
+ if self.__cm is None:
176
+ raise RuntimeError(
177
+ f"this {self.__class__.__qualname__} has not been entered yet"
178
+ )
179
+
180
+ # Prevent circular references
181
+ cm = self.__cm
182
+ del self.__cm
183
+
184
+ return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb))
185
+
186
+ @abstractmethod
187
+ def __asynccontextmanager__(
188
+ self,
189
+ ) -> AbstractAsyncContextManager[object, bool | None]:
190
+ """
191
+ Implement your async context manager logic here.
192
+
193
+ This method **must** be decorated with
194
+ :func:`@asynccontextmanager <contextlib.asynccontextmanager>`.
195
+
196
+ .. note:: Remember that the ``yield`` will raise any exception raised in the
197
+ enclosed context block, so use a ``finally:`` block to clean up resources!
198
+
199
+ :return: an async context manager object
200
+ """
.venv/Lib/site-packages/anyio/_core/_eventloop.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import sys
5
+ import threading
6
+ from collections.abc import Awaitable, Callable, Generator
7
+ from contextlib import contextmanager
8
+ from contextvars import Token
9
+ from importlib import import_module
10
+ from typing import TYPE_CHECKING, Any, TypeVar
11
+
12
+ from ._exceptions import NoEventLoopError
13
+
14
+ if sys.version_info >= (3, 11):
15
+ from typing import TypeVarTuple, Unpack
16
+ else:
17
+ from typing_extensions import TypeVarTuple, Unpack
18
+
19
+ sniffio: Any
20
+ try:
21
+ import sniffio
22
+ except ModuleNotFoundError:
23
+ sniffio = None
24
+
25
+ if TYPE_CHECKING:
26
+ from ..abc import AsyncBackend
27
+
28
+ # This must be updated when new backends are introduced
29
+ BACKENDS = "asyncio", "trio"
30
+
31
+ T_Retval = TypeVar("T_Retval")
32
+ PosArgsT = TypeVarTuple("PosArgsT")
33
+
34
+ threadlocals = threading.local()
35
+ loaded_backends: dict[str, type[AsyncBackend]] = {}
36
+
37
+
38
+ def run(
39
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
40
+ *args: Unpack[PosArgsT],
41
+ backend: str = "asyncio",
42
+ backend_options: dict[str, Any] | None = None,
43
+ ) -> T_Retval:
44
+ """
45
+ Run the given coroutine function in an asynchronous event loop.
46
+
47
+ The current thread must not be already running an event loop.
48
+
49
+ :param func: a coroutine function
50
+ :param args: positional arguments to ``func``
51
+ :param backend: name of the asynchronous event loop implementation – currently
52
+ either ``asyncio`` or ``trio``
53
+ :param backend_options: keyword arguments to call the backend ``run()``
54
+ implementation with (documented :ref:`here <backend options>`)
55
+ :return: the return value of the coroutine function
56
+ :raises RuntimeError: if an asynchronous event loop is already running in this
57
+ thread
58
+ :raises LookupError: if the named backend is not found
59
+
60
+ """
61
+ if asynclib_name := current_async_library():
62
+ raise RuntimeError(f"Already running {asynclib_name} in this thread")
63
+
64
+ try:
65
+ async_backend = get_async_backend(backend)
66
+ except ImportError as exc:
67
+ if backend in BACKENDS:
68
+ raise LookupError(
69
+ f"Backend {backend!r} is not available. "
70
+ f"Install it with: pip install anyio[{backend}]"
71
+ ) from exc
72
+
73
+ raise LookupError(f"No such backend: {backend}") from exc
74
+
75
+ token = None
76
+ if asynclib_name is None:
77
+ # Since we're in control of the event loop, we can cache the name of the async
78
+ # library
79
+ token = set_current_async_library(backend)
80
+
81
+ try:
82
+ backend_options = backend_options or {}
83
+ return async_backend.run(func, args, {}, backend_options)
84
+ finally:
85
+ reset_current_async_library(token)
86
+
87
+
88
+ async def sleep(delay: float) -> None:
89
+ """
90
+ Pause the current task for the specified duration.
91
+
92
+ :param delay: the duration, in seconds
93
+
94
+ """
95
+ return await get_async_backend().sleep(delay)
96
+
97
+
98
+ async def sleep_forever() -> None:
99
+ """
100
+ Pause the current task until it's cancelled.
101
+
102
+ This is a shortcut for ``sleep(math.inf)``.
103
+
104
+ .. versionadded:: 3.1
105
+
106
+ """
107
+ await sleep(math.inf)
108
+
109
+
110
+ async def sleep_until(deadline: float) -> None:
111
+ """
112
+ Pause the current task until the given time.
113
+
114
+ :param deadline: the absolute time to wake up at (according to the internal
115
+ monotonic clock of the event loop)
116
+
117
+ .. versionadded:: 3.1
118
+
119
+ """
120
+ now = current_time()
121
+ await sleep(max(deadline - now, 0))
122
+
123
+
124
+ def current_time() -> float:
125
+ """
126
+ Return the current value of the event loop's internal clock.
127
+
128
+ :return: the clock value (seconds)
129
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
130
+ current thread
131
+
132
+ """
133
+ return get_async_backend().current_time()
134
+
135
+
136
+ def get_all_backends() -> tuple[str, ...]:
137
+ """Return a tuple of the names of all built-in backends."""
138
+ return BACKENDS
139
+
140
+
141
+ def get_available_backends() -> tuple[str, ...]:
142
+ """
143
+ Test for the availability of built-in backends.
144
+
145
+ :return a tuple of the built-in backend names that were successfully imported
146
+
147
+ .. versionadded:: 4.12
148
+
149
+ """
150
+ available_backends: list[str] = []
151
+ for backend_name in get_all_backends():
152
+ try:
153
+ get_async_backend(backend_name)
154
+ except ImportError:
155
+ continue
156
+
157
+ available_backends.append(backend_name)
158
+
159
+ return tuple(available_backends)
160
+
161
+
162
+ def get_cancelled_exc_class() -> type[BaseException]:
163
+ """
164
+ Return the current async library's cancellation exception class.
165
+
166
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
167
+ current thread
168
+
169
+ """
170
+ return get_async_backend().cancelled_exception_class()
171
+
172
+
173
+ #
174
+ # Private API
175
+ #
176
+
177
+
178
+ @contextmanager
179
+ def claim_worker_thread(
180
+ backend_class: type[AsyncBackend], token: object
181
+ ) -> Generator[Any, None, None]:
182
+ from ..lowlevel import EventLoopToken
183
+
184
+ threadlocals.current_token = EventLoopToken(backend_class, token)
185
+ try:
186
+ yield
187
+ finally:
188
+ del threadlocals.current_token
189
+
190
+
191
+ def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]:
192
+ if asynclib_name is None:
193
+ asynclib_name = current_async_library()
194
+ if not asynclib_name:
195
+ raise NoEventLoopError(
196
+ f"Not currently running on any asynchronous event loop. "
197
+ f"Available async backends: {', '.join(get_all_backends())}"
198
+ )
199
+
200
+ # We use our own dict instead of sys.modules to get the already imported back-end
201
+ # class because the appropriate modules in sys.modules could potentially be only
202
+ # partially initialized
203
+ try:
204
+ return loaded_backends[asynclib_name]
205
+ except KeyError:
206
+ module = import_module(f"anyio._backends._{asynclib_name}")
207
+ loaded_backends[asynclib_name] = module.backend_class
208
+ return module.backend_class
209
+
210
+
211
+ def current_async_library() -> str | None:
212
+ if sniffio is None:
213
+ # If sniffio is not installed, we assume we're either running asyncio or nothing
214
+ import asyncio
215
+
216
+ try:
217
+ asyncio.get_running_loop()
218
+ return "asyncio"
219
+ except RuntimeError:
220
+ pass
221
+ else:
222
+ try:
223
+ return sniffio.current_async_library()
224
+ except sniffio.AsyncLibraryNotFoundError:
225
+ pass
226
+
227
+ return None
228
+
229
+
230
+ def set_current_async_library(asynclib_name: str | None) -> Token | None:
231
+ # no-op if sniffio is not installed
232
+ if sniffio is None:
233
+ return None
234
+
235
+ return sniffio.current_async_library_cvar.set(asynclib_name)
236
+
237
+
238
+ def reset_current_async_library(token: Token | None) -> None:
239
+ if token is not None:
240
+ sniffio.current_async_library_cvar.reset(token)
.venv/Lib/site-packages/anyio/_core/_exceptions.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from collections.abc import Generator
5
+ from textwrap import dedent
6
+ from typing import Any
7
+
8
+ if sys.version_info < (3, 11):
9
+ from exceptiongroup import BaseExceptionGroup
10
+
11
+
12
+ class BrokenResourceError(Exception):
13
+ """
14
+ Raised when trying to use a resource that has been rendered unusable due to external
15
+ causes (e.g. a send stream whose peer has disconnected).
16
+ """
17
+
18
+
19
+ class BrokenWorkerProcess(Exception):
20
+ """
21
+ Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or
22
+ otherwise misbehaves.
23
+ """
24
+
25
+
26
+ class BrokenWorkerInterpreter(Exception):
27
+ """
28
+ Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is
29
+ raised in the subinterpreter.
30
+ """
31
+
32
+ def __init__(self, excinfo: Any):
33
+ # This was adapted from concurrent.futures.interpreter.ExecutionFailed
34
+ msg = excinfo.formatted
35
+ if not msg:
36
+ if excinfo.type and excinfo.msg:
37
+ msg = f"{excinfo.type.__name__}: {excinfo.msg}"
38
+ else:
39
+ msg = excinfo.type.__name__ or excinfo.msg
40
+
41
+ super().__init__(msg)
42
+ self.excinfo = excinfo
43
+
44
+ def __str__(self) -> str:
45
+ try:
46
+ formatted = self.excinfo.errdisplay
47
+ except Exception:
48
+ return super().__str__()
49
+ else:
50
+ return dedent(
51
+ f"""
52
+ {super().__str__()}
53
+
54
+ Uncaught in the interpreter:
55
+
56
+ {formatted}
57
+ """.strip()
58
+ )
59
+
60
+
61
+ class BusyResourceError(Exception):
62
+ """
63
+ Raised when two tasks are trying to read from or write to the same resource
64
+ concurrently.
65
+ """
66
+
67
+ def __init__(self, action: str):
68
+ super().__init__(f"Another task is already {action} this resource")
69
+
70
+
71
+ class ClosedResourceError(Exception):
72
+ """Raised when trying to use a resource that has been closed."""
73
+
74
+
75
+ class ConnectionFailed(OSError):
76
+ """
77
+ Raised when a connection attempt fails.
78
+
79
+ .. note:: This class inherits from :exc:`OSError` for backwards compatibility.
80
+ """
81
+
82
+
83
+ def iterate_exceptions(
84
+ exception: BaseException,
85
+ ) -> Generator[BaseException, None, None]:
86
+ if isinstance(exception, BaseExceptionGroup):
87
+ for exc in exception.exceptions:
88
+ yield from iterate_exceptions(exc)
89
+ else:
90
+ yield exception
91
+
92
+
93
+ class DelimiterNotFound(Exception):
94
+ """
95
+ Raised during
96
+ :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
97
+ maximum number of bytes has been read without the delimiter being found.
98
+ """
99
+
100
+ def __init__(self, max_bytes: int) -> None:
101
+ super().__init__(
102
+ f"The delimiter was not found among the first {max_bytes} bytes"
103
+ )
104
+
105
+
106
+ class EndOfStream(Exception):
107
+ """
108
+ Raised when trying to read from a stream that has been closed from the other end.
109
+ """
110
+
111
+
112
+ class IncompleteRead(Exception):
113
+ """
114
+ Raised during
115
+ :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or
116
+ :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
117
+ connection is closed before the requested amount of bytes has been read.
118
+ """
119
+
120
+ def __init__(self) -> None:
121
+ super().__init__(
122
+ "The stream was closed before the read operation could be completed"
123
+ )
124
+
125
+
126
+ class TypedAttributeLookupError(LookupError):
127
+ """
128
+ Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute
129
+ is not found and no default value has been given.
130
+ """
131
+
132
+
133
+ class WouldBlock(Exception):
134
+ """Raised by ``X_nowait`` functions if ``X()`` would block."""
135
+
136
+
137
+ class NoEventLoopError(RuntimeError):
138
+ """
139
+ Raised by several functions that require an event loop to be running in the current
140
+ thread when there is no running event loop.
141
+
142
+ This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync`
143
+ if not calling from an AnyIO worker thread, and no ``token`` was passed.
144
+ """
145
+
146
+
147
+ class RunFinishedError(RuntimeError):
148
+ """
149
+ Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event
150
+ loop associated with the explicitly passed token has already finished.
151
+ """
152
+
153
+ def __init__(self) -> None:
154
+ super().__init__(
155
+ "The event loop associated with the given token has already finished"
156
+ )
157
+
158
+
159
+ class TaskFailed(Exception):
160
+ """
161
+ Raised when awaiting on, or attempting to access the return value of, a
162
+ :class:`.TaskHandle` that raised an exception.
163
+ """
164
+
165
+
166
+ class TaskCancelled(TaskFailed):
167
+ """
168
+ Raised when awaiting on, or attempting to access the return value of, a
169
+ :class:`.TaskHandle` that was cancelled.
170
+ """
171
+
172
+
173
+ class TaskNotFinished(Exception):
174
+ """
175
+ Raised when attempting to access the return value or exception of a
176
+ :class:`.TaskHandle` that is still pending completion.
177
+ """
.venv/Lib/site-packages/anyio/_core/_fileio.py ADDED
@@ -0,0 +1,960 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import pathlib
5
+ import sys
6
+ from collections.abc import (
7
+ AsyncIterator,
8
+ Callable,
9
+ Iterable,
10
+ Iterator,
11
+ Sequence,
12
+ )
13
+ from dataclasses import dataclass
14
+ from functools import partial
15
+ from os import PathLike
16
+ from typing import (
17
+ IO,
18
+ TYPE_CHECKING,
19
+ Any,
20
+ AnyStr,
21
+ ClassVar,
22
+ Final,
23
+ Generic,
24
+ TypeVar,
25
+ overload,
26
+ )
27
+
28
+ from .. import to_thread
29
+ from ..abc import AsyncResource
30
+ from ._synchronization import CapacityLimiter
31
+
32
+ if sys.version_info >= (3, 11):
33
+ from typing import Self
34
+ else:
35
+ from typing_extensions import Self
36
+
37
+ if sys.version_info >= (3, 14):
38
+ from pathlib.types import PathInfo
39
+
40
+ if TYPE_CHECKING:
41
+ from types import ModuleType
42
+
43
+ from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
44
+ else:
45
+ ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object
46
+
47
+
48
+ T = TypeVar("T", bound="Path")
49
+
50
+
51
+ class AsyncFile(AsyncResource, Generic[AnyStr]):
52
+ """
53
+ An asynchronous file object.
54
+
55
+ This class wraps a standard file object and provides async friendly versions of the
56
+ following blocking methods (where available on the original file object):
57
+
58
+ * read
59
+ * read1
60
+ * readline
61
+ * readlines
62
+ * readinto
63
+ * readinto1
64
+ * write
65
+ * writelines
66
+ * truncate
67
+ * seek
68
+ * tell
69
+ * flush
70
+
71
+ All other methods are directly passed through.
72
+
73
+ This class supports the asynchronous context manager protocol which closes the
74
+ underlying file at the end of the context block.
75
+
76
+ This class also supports asynchronous iteration::
77
+
78
+ async with await open_file(...) as f:
79
+ async for line in f:
80
+ print(line)
81
+ """
82
+
83
+ def __init__(
84
+ self, fp: IO[AnyStr], *, limiter: CapacityLimiter | None = None
85
+ ) -> None:
86
+ if limiter is not None and not isinstance(limiter, CapacityLimiter):
87
+ raise TypeError(
88
+ f"limiter must be a CapacityLimiter or None, not "
89
+ f"{limiter.__class__.__name__}"
90
+ )
91
+
92
+ self._fp: Any = fp
93
+ self._limiter = limiter
94
+
95
+ def __getattr__(self, name: str) -> object:
96
+ return getattr(self._fp, name)
97
+
98
+ @property
99
+ def limiter(self) -> CapacityLimiter | None:
100
+ """The capacity limiter used by this file object, if not the global limiter."""
101
+ return self._limiter
102
+
103
+ @property
104
+ def wrapped(self) -> IO[AnyStr]:
105
+ """The wrapped file object."""
106
+ return self._fp
107
+
108
+ async def __aiter__(self) -> AsyncIterator[AnyStr]:
109
+ while True:
110
+ line = await self.readline()
111
+ if line:
112
+ yield line
113
+ else:
114
+ break
115
+
116
+ async def aclose(self) -> None:
117
+ return await to_thread.run_sync(self._fp.close, limiter=self._limiter)
118
+
119
+ async def read(self, size: int = -1) -> AnyStr:
120
+ return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter)
121
+
122
+ async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes:
123
+ return await to_thread.run_sync(self._fp.read1, size, limiter=self._limiter)
124
+
125
+ async def readline(self) -> AnyStr:
126
+ return await to_thread.run_sync(self._fp.readline, limiter=self._limiter)
127
+
128
+ async def readlines(self) -> list[AnyStr]:
129
+ return await to_thread.run_sync(self._fp.readlines, limiter=self._limiter)
130
+
131
+ async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
132
+ return await to_thread.run_sync(self._fp.readinto, b, limiter=self._limiter)
133
+
134
+ async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int:
135
+ return await to_thread.run_sync(self._fp.readinto1, b, limiter=self._limiter)
136
+
137
+ @overload
138
+ async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ...
139
+
140
+ @overload
141
+ async def write(self: AsyncFile[str], b: str) -> int: ...
142
+
143
+ async def write(self, b: ReadableBuffer | str) -> int:
144
+ return await to_thread.run_sync(self._fp.write, b, limiter=self._limiter)
145
+
146
+ @overload
147
+ async def writelines(
148
+ self: AsyncFile[bytes], lines: Iterable[ReadableBuffer]
149
+ ) -> None: ...
150
+
151
+ @overload
152
+ async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ...
153
+
154
+ async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None:
155
+ return await to_thread.run_sync(
156
+ self._fp.writelines, lines, limiter=self._limiter
157
+ )
158
+
159
+ async def truncate(self, size: int | None = None) -> int:
160
+ return await to_thread.run_sync(self._fp.truncate, size, limiter=self._limiter)
161
+
162
+ async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int:
163
+ return await to_thread.run_sync(
164
+ self._fp.seek, offset, whence, limiter=self._limiter
165
+ )
166
+
167
+ async def tell(self) -> int:
168
+ return await to_thread.run_sync(self._fp.tell, limiter=self._limiter)
169
+
170
+ async def flush(self) -> None:
171
+ return await to_thread.run_sync(self._fp.flush, limiter=self._limiter)
172
+
173
+
174
+ @overload
175
+ async def open_file(
176
+ file: str | PathLike[str] | int,
177
+ mode: OpenBinaryMode,
178
+ buffering: int = ...,
179
+ encoding: str | None = ...,
180
+ errors: str | None = ...,
181
+ newline: str | None = ...,
182
+ closefd: bool = ...,
183
+ opener: Callable[[str, int], int] | None = ...,
184
+ *,
185
+ limiter: CapacityLimiter | None = ...,
186
+ ) -> AsyncFile[bytes]: ...
187
+
188
+
189
+ @overload
190
+ async def open_file(
191
+ file: str | PathLike[str] | int,
192
+ mode: OpenTextMode = ...,
193
+ buffering: int = ...,
194
+ encoding: str | None = ...,
195
+ errors: str | None = ...,
196
+ newline: str | None = ...,
197
+ closefd: bool = ...,
198
+ opener: Callable[[str, int], int] | None = ...,
199
+ *,
200
+ limiter: CapacityLimiter | None = ...,
201
+ ) -> AsyncFile[str]: ...
202
+
203
+
204
+ async def open_file(
205
+ file: str | PathLike[str] | int,
206
+ mode: str = "r",
207
+ buffering: int = -1,
208
+ encoding: str | None = None,
209
+ errors: str | None = None,
210
+ newline: str | None = None,
211
+ closefd: bool = True,
212
+ opener: Callable[[str, int], int] | None = None,
213
+ *,
214
+ limiter: CapacityLimiter | None = None,
215
+ ) -> AsyncFile[Any]:
216
+ """
217
+ Open a file asynchronously.
218
+
219
+ Except for ``limiter``, the arguments are exactly the same as for the builtin :func:`open`.
220
+
221
+ :param limiter: an optional capacity limiter to use with the file
222
+ instead of the default one
223
+ :return: an asynchronous file object
224
+
225
+ .. versionchanged:: 4.14.0
226
+ Added the ``limiter`` keyword argument.
227
+
228
+ """
229
+ fp = await to_thread.run_sync(
230
+ open,
231
+ file,
232
+ mode,
233
+ buffering,
234
+ encoding,
235
+ errors,
236
+ newline,
237
+ closefd,
238
+ opener,
239
+ limiter=limiter,
240
+ )
241
+ return AsyncFile(fp, limiter=limiter)
242
+
243
+
244
+ def wrap_file(
245
+ file: IO[AnyStr], *, limiter: CapacityLimiter | None = None
246
+ ) -> AsyncFile[AnyStr]:
247
+ """
248
+ Wrap an existing file as an asynchronous file.
249
+
250
+ :param file: an existing file-like object
251
+ :param limiter: an optional capacity limiter to use with the file
252
+ instead of the default one
253
+ :return: an asynchronous file object
254
+
255
+ .. versionchanged:: 4.14.0
256
+ Added the ``limiter`` keyword argument.
257
+
258
+ """
259
+ return AsyncFile(file, limiter=limiter)
260
+
261
+
262
+ @dataclass(eq=False)
263
+ class _PathIterator(AsyncIterator[T]):
264
+ iterator: Iterator[PathLike[str]]
265
+ limiter: CapacityLimiter | None
266
+ # This was added to ensure that iterating over a subclass of Path yields instances
267
+ # of that subclass rather than the base Path class.
268
+ path_cls: type[T]
269
+
270
+ async def __anext__(self) -> T:
271
+ nextval = await to_thread.run_sync(
272
+ next, self.iterator, None, abandon_on_cancel=True, limiter=self.limiter
273
+ )
274
+ if nextval is None:
275
+ raise StopAsyncIteration from None
276
+
277
+ return self.path_cls(nextval, limiter=self.limiter)
278
+
279
+
280
+ class Path:
281
+ """
282
+ An asynchronous version of :class:`pathlib.Path`.
283
+
284
+ This class cannot be substituted for :class:`pathlib.Path` or
285
+ :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike`
286
+ interface.
287
+
288
+ It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for
289
+ the deprecated :meth:`~pathlib.Path.link_to` method.
290
+
291
+ Some methods may be unavailable or have limited functionality, based on the Python
292
+ version:
293
+
294
+ * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later)
295
+ * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later)
296
+ * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later)
297
+ * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later)
298
+ * :attr:`~pathlib.Path.info` (available on Python 3.14 or later)
299
+ * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later)
300
+ * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only
301
+ available on Python 3.13 or later)
302
+ * :meth:`~pathlib.Path.move` (available on Python 3.14 or later)
303
+ * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later)
304
+ * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available
305
+ on Python 3.12 or later)
306
+ * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later)
307
+
308
+ Any methods that do disk I/O need to be awaited on. These methods are:
309
+
310
+ * :meth:`~pathlib.Path.absolute`
311
+ * :meth:`~pathlib.Path.chmod`
312
+ * :meth:`~pathlib.Path.cwd`
313
+ * :meth:`~pathlib.Path.exists`
314
+ * :meth:`~pathlib.Path.expanduser`
315
+ * :meth:`~pathlib.Path.group`
316
+ * :meth:`~pathlib.Path.hardlink_to`
317
+ * :meth:`~pathlib.Path.home`
318
+ * :meth:`~pathlib.Path.is_block_device`
319
+ * :meth:`~pathlib.Path.is_char_device`
320
+ * :meth:`~pathlib.Path.is_dir`
321
+ * :meth:`~pathlib.Path.is_fifo`
322
+ * :meth:`~pathlib.Path.is_file`
323
+ * :meth:`~pathlib.Path.is_junction`
324
+ * :meth:`~pathlib.Path.is_mount`
325
+ * :meth:`~pathlib.Path.is_socket`
326
+ * :meth:`~pathlib.Path.is_symlink`
327
+ * :meth:`~pathlib.Path.lchmod`
328
+ * :meth:`~pathlib.Path.lstat`
329
+ * :meth:`~pathlib.Path.mkdir`
330
+ * :meth:`~pathlib.Path.open`
331
+ * :meth:`~pathlib.Path.owner`
332
+ * :meth:`~pathlib.Path.read_bytes`
333
+ * :meth:`~pathlib.Path.read_text`
334
+ * :meth:`~pathlib.Path.readlink`
335
+ * :meth:`~pathlib.Path.rename`
336
+ * :meth:`~pathlib.Path.replace`
337
+ * :meth:`~pathlib.Path.resolve`
338
+ * :meth:`~pathlib.Path.rmdir`
339
+ * :meth:`~pathlib.Path.samefile`
340
+ * :meth:`~pathlib.Path.stat`
341
+ * :meth:`~pathlib.Path.symlink_to`
342
+ * :meth:`~pathlib.Path.touch`
343
+ * :meth:`~pathlib.Path.unlink`
344
+ * :meth:`~pathlib.Path.walk`
345
+ * :meth:`~pathlib.Path.write_bytes`
346
+ * :meth:`~pathlib.Path.write_text`
347
+
348
+ Additionally, the following methods return an async iterator yielding
349
+ :class:`~.Path` objects:
350
+
351
+ * :meth:`~pathlib.Path.glob`
352
+ * :meth:`~pathlib.Path.iterdir`
353
+ * :meth:`~pathlib.Path.rglob`
354
+
355
+ .. versionchanged:: 4.14.0
356
+ Added the ``limiter`` keyword argument.
357
+ """
358
+
359
+ __slots__ = "_path", "_limiter", "__weakref__"
360
+
361
+ __weakref__: Any
362
+
363
+ def __init__(
364
+ self, *args: str | PathLike[str], limiter: CapacityLimiter | None = None
365
+ ) -> None:
366
+ if limiter is not None and not isinstance(limiter, CapacityLimiter):
367
+ raise TypeError(
368
+ f"limiter must be a CapacityLimiter or None, not "
369
+ f"{limiter.__class__.__name__}"
370
+ )
371
+
372
+ self._path: Final[pathlib.Path] = pathlib.Path(*args)
373
+ self._limiter = limiter
374
+
375
+ def __fspath__(self) -> str:
376
+ return self._path.__fspath__()
377
+
378
+ if sys.version_info >= (3, 15):
379
+
380
+ def __vfspath__(self) -> str:
381
+ return self._path.__vfspath__()
382
+
383
+ def __str__(self) -> str:
384
+ return self._path.__str__()
385
+
386
+ def __repr__(self) -> str:
387
+ return f"{self.__class__.__name__}({self.as_posix()!r})"
388
+
389
+ def __bytes__(self) -> bytes:
390
+ return self._path.__bytes__()
391
+
392
+ def __hash__(self) -> int:
393
+ return self._path.__hash__()
394
+
395
+ def __eq__(self, other: object) -> bool:
396
+ target = other._path if isinstance(other, Path) else other
397
+ return self._path.__eq__(target)
398
+
399
+ def __lt__(self, other: pathlib.PurePath | Path) -> bool:
400
+ target = other._path if isinstance(other, Path) else other
401
+ return self._path.__lt__(target)
402
+
403
+ def __le__(self, other: pathlib.PurePath | Path) -> bool:
404
+ target = other._path if isinstance(other, Path) else other
405
+ return self._path.__le__(target)
406
+
407
+ def __gt__(self, other: pathlib.PurePath | Path) -> bool:
408
+ target = other._path if isinstance(other, Path) else other
409
+ return self._path.__gt__(target)
410
+
411
+ def __ge__(self, other: pathlib.PurePath | Path) -> bool:
412
+ target = other._path if isinstance(other, Path) else other
413
+ return self._path.__ge__(target)
414
+
415
+ def __truediv__(self, other: str | PathLike[str]) -> Self:
416
+ return type(self)(self._path / other, limiter=self._limiter)
417
+
418
+ def __rtruediv__(self, other: str | PathLike[str]) -> Self:
419
+ return type(self)(other, limiter=self._limiter) / self
420
+
421
+ @property
422
+ def limiter(self) -> CapacityLimiter | None:
423
+ """The capacity limiter used by this path, if not the global limiter."""
424
+ return self._limiter
425
+
426
+ @property
427
+ def parts(self) -> tuple[str, ...]:
428
+ return self._path.parts
429
+
430
+ @property
431
+ def drive(self) -> str:
432
+ return self._path.drive
433
+
434
+ @property
435
+ def root(self) -> str:
436
+ return self._path.root
437
+
438
+ @property
439
+ def anchor(self) -> str:
440
+ return self._path.anchor
441
+
442
+ @property
443
+ def parents(self) -> Sequence[Self]:
444
+ return tuple(type(self)(p, limiter=self._limiter) for p in self._path.parents)
445
+
446
+ @property
447
+ def parent(self) -> Self:
448
+ return type(self)(self._path.parent, limiter=self._limiter)
449
+
450
+ @property
451
+ def name(self) -> str:
452
+ return self._path.name
453
+
454
+ @property
455
+ def suffix(self) -> str:
456
+ return self._path.suffix
457
+
458
+ @property
459
+ def suffixes(self) -> list[str]:
460
+ return self._path.suffixes
461
+
462
+ @property
463
+ def stem(self) -> str:
464
+ return self._path.stem
465
+
466
+ async def absolute(self) -> Self:
467
+ path = await to_thread.run_sync(self._path.absolute, limiter=self._limiter)
468
+ return type(self)(path, limiter=self._limiter)
469
+
470
+ def as_posix(self) -> str:
471
+ return self._path.as_posix()
472
+
473
+ def as_uri(self) -> str:
474
+ return self._path.as_uri()
475
+
476
+ if sys.version_info >= (3, 13):
477
+ parser: ClassVar[ModuleType] = pathlib.Path.parser
478
+
479
+ @classmethod
480
+ def from_uri(cls, uri: str, *, limiter: CapacityLimiter | None = None) -> Self:
481
+ return cls(pathlib.Path.from_uri(uri), limiter=limiter)
482
+
483
+ def full_match(
484
+ self, path_pattern: str, *, case_sensitive: bool | None = None
485
+ ) -> bool:
486
+ return self._path.full_match(path_pattern, case_sensitive=case_sensitive)
487
+
488
+ def match(
489
+ self, path_pattern: str, *, case_sensitive: bool | None = None
490
+ ) -> bool:
491
+ return self._path.match(path_pattern, case_sensitive=case_sensitive)
492
+ else:
493
+
494
+ def match(self, path_pattern: str) -> bool:
495
+ return self._path.match(path_pattern)
496
+
497
+ if sys.version_info >= (3, 14):
498
+
499
+ @property
500
+ def info(self) -> PathInfo:
501
+ return self._path.info
502
+
503
+ async def copy(
504
+ self,
505
+ target: str | os.PathLike[str],
506
+ *,
507
+ follow_symlinks: bool = True,
508
+ preserve_metadata: bool = False,
509
+ ) -> Self:
510
+ func = partial(
511
+ self._path.copy,
512
+ follow_symlinks=follow_symlinks,
513
+ preserve_metadata=preserve_metadata,
514
+ )
515
+ return type(self)(
516
+ await to_thread.run_sync(
517
+ func, pathlib.Path(target), limiter=self._limiter
518
+ ),
519
+ limiter=self._limiter,
520
+ )
521
+
522
+ async def copy_into(
523
+ self,
524
+ target_dir: str | os.PathLike[str],
525
+ *,
526
+ follow_symlinks: bool = True,
527
+ preserve_metadata: bool = False,
528
+ ) -> Self:
529
+ func = partial(
530
+ self._path.copy_into,
531
+ follow_symlinks=follow_symlinks,
532
+ preserve_metadata=preserve_metadata,
533
+ )
534
+ return type(self)(
535
+ await to_thread.run_sync(
536
+ func, pathlib.Path(target_dir), limiter=self._limiter
537
+ ),
538
+ limiter=self._limiter,
539
+ )
540
+
541
+ async def move(self, target: str | os.PathLike[str]) -> Self:
542
+ # Upstream does not handle anyio.Path properly as a PathLike
543
+ target = pathlib.Path(target)
544
+ return type(self)(
545
+ await to_thread.run_sync(
546
+ self._path.move, target, limiter=self._limiter
547
+ ),
548
+ limiter=self._limiter,
549
+ )
550
+
551
+ async def move_into(
552
+ self,
553
+ target_dir: str | os.PathLike[str],
554
+ ) -> Self:
555
+ return type(self)(
556
+ await to_thread.run_sync(
557
+ self._path.move_into, target_dir, limiter=self._limiter
558
+ ),
559
+ limiter=self._limiter,
560
+ )
561
+
562
+ def is_relative_to(self, other: str | PathLike[str]) -> bool:
563
+ try:
564
+ self.relative_to(other)
565
+ return True
566
+ except ValueError:
567
+ return False
568
+
569
+ async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None:
570
+ func = partial(os.chmod, follow_symlinks=follow_symlinks)
571
+ return await to_thread.run_sync(func, self._path, mode, limiter=self._limiter)
572
+
573
+ @classmethod
574
+ async def cwd(cls, *, limiter: CapacityLimiter | None = None) -> Self:
575
+ path = await to_thread.run_sync(pathlib.Path.cwd, limiter=limiter)
576
+ return cls(path, limiter=limiter)
577
+
578
+ async def exists(self) -> bool:
579
+ return await to_thread.run_sync(
580
+ self._path.exists, abandon_on_cancel=True, limiter=self._limiter
581
+ )
582
+
583
+ async def expanduser(self) -> Self:
584
+ return type(self)(
585
+ await to_thread.run_sync(
586
+ self._path.expanduser, abandon_on_cancel=True, limiter=self._limiter
587
+ ),
588
+ limiter=self._limiter,
589
+ )
590
+
591
+ if sys.version_info < (3, 12):
592
+ # Python 3.11 and earlier
593
+ def glob(self, pattern: str) -> AsyncIterator[Self]:
594
+ gen = self._path.glob(pattern)
595
+ return _PathIterator(gen, self._limiter, type(self))
596
+ elif (3, 12) <= sys.version_info < (3, 13):
597
+ # changed in Python 3.12:
598
+ # - The case_sensitive parameter was added.
599
+ def glob(
600
+ self,
601
+ pattern: str,
602
+ *,
603
+ case_sensitive: bool | None = None,
604
+ ) -> AsyncIterator[Self]:
605
+ gen = self._path.glob(pattern, case_sensitive=case_sensitive)
606
+ return _PathIterator(gen, self._limiter, type(self))
607
+ elif sys.version_info >= (3, 13):
608
+ # Changed in Python 3.13:
609
+ # - The recurse_symlinks parameter was added.
610
+ # - The pattern parameter accepts a path-like object.
611
+ def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block
612
+ self,
613
+ pattern: str | PathLike[str],
614
+ *,
615
+ case_sensitive: bool | None = None,
616
+ recurse_symlinks: bool = False,
617
+ ) -> AsyncIterator[Self]:
618
+ gen = self._path.glob(
619
+ pattern, # type: ignore[arg-type]
620
+ case_sensitive=case_sensitive,
621
+ recurse_symlinks=recurse_symlinks,
622
+ )
623
+ return _PathIterator(gen, self._limiter, type(self))
624
+
625
+ async def group(self) -> str:
626
+ return await to_thread.run_sync(
627
+ self._path.group, abandon_on_cancel=True, limiter=self._limiter
628
+ )
629
+
630
+ async def hardlink_to(
631
+ self, target: str | bytes | PathLike[str] | PathLike[bytes]
632
+ ) -> None:
633
+ if isinstance(target, Path):
634
+ target = target._path
635
+
636
+ await to_thread.run_sync(os.link, target, self, limiter=self._limiter)
637
+
638
+ @classmethod
639
+ async def home(cls, *, limiter: CapacityLimiter | None = None) -> Self:
640
+ home_path = await to_thread.run_sync(pathlib.Path.home, limiter=limiter)
641
+ return cls(home_path, limiter=limiter)
642
+
643
+ def is_absolute(self) -> bool:
644
+ return self._path.is_absolute()
645
+
646
+ async def is_block_device(self) -> bool:
647
+ return await to_thread.run_sync(
648
+ self._path.is_block_device, abandon_on_cancel=True, limiter=self._limiter
649
+ )
650
+
651
+ async def is_char_device(self) -> bool:
652
+ return await to_thread.run_sync(
653
+ self._path.is_char_device, abandon_on_cancel=True, limiter=self._limiter
654
+ )
655
+
656
+ async def is_dir(self) -> bool:
657
+ return await to_thread.run_sync(
658
+ self._path.is_dir, abandon_on_cancel=True, limiter=self._limiter
659
+ )
660
+
661
+ async def is_fifo(self) -> bool:
662
+ return await to_thread.run_sync(
663
+ self._path.is_fifo, abandon_on_cancel=True, limiter=self._limiter
664
+ )
665
+
666
+ async def is_file(self) -> bool:
667
+ return await to_thread.run_sync(
668
+ self._path.is_file, abandon_on_cancel=True, limiter=self._limiter
669
+ )
670
+
671
+ if sys.version_info >= (3, 12):
672
+
673
+ async def is_junction(self) -> bool:
674
+ return await to_thread.run_sync(
675
+ self._path.is_junction, limiter=self._limiter
676
+ )
677
+
678
+ async def is_mount(self) -> bool:
679
+ return await to_thread.run_sync(
680
+ os.path.ismount, self._path, abandon_on_cancel=True, limiter=self._limiter
681
+ )
682
+
683
+ if sys.version_info < (3, 15):
684
+
685
+ def is_reserved(self) -> bool:
686
+ return self._path.is_reserved()
687
+
688
+ async def is_socket(self) -> bool:
689
+ return await to_thread.run_sync(
690
+ self._path.is_socket, abandon_on_cancel=True, limiter=self._limiter
691
+ )
692
+
693
+ async def is_symlink(self) -> bool:
694
+ return await to_thread.run_sync(
695
+ self._path.is_symlink, abandon_on_cancel=True, limiter=self._limiter
696
+ )
697
+
698
+ async def iterdir(self) -> AsyncIterator[Self]:
699
+ gen = (
700
+ self._path.iterdir()
701
+ if sys.version_info < (3, 13)
702
+ else await to_thread.run_sync(
703
+ self._path.iterdir, abandon_on_cancel=True, limiter=self._limiter
704
+ )
705
+ )
706
+ async for path in _PathIterator(gen, self._limiter, type(self)):
707
+ yield path
708
+
709
+ def joinpath(self, *args: str | PathLike[str]) -> Self:
710
+ return type(self)(self._path.joinpath(*args), limiter=self._limiter)
711
+
712
+ async def lchmod(self, mode: int) -> None:
713
+ await to_thread.run_sync(self._path.lchmod, mode, limiter=self._limiter)
714
+
715
+ async def lstat(self) -> os.stat_result:
716
+ return await to_thread.run_sync(
717
+ self._path.lstat, abandon_on_cancel=True, limiter=self._limiter
718
+ )
719
+
720
+ async def mkdir(
721
+ self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
722
+ ) -> None:
723
+ await to_thread.run_sync(
724
+ self._path.mkdir, mode, parents, exist_ok, limiter=self._limiter
725
+ )
726
+
727
+ @overload
728
+ async def open(
729
+ self,
730
+ mode: OpenBinaryMode,
731
+ buffering: int = ...,
732
+ encoding: str | None = ...,
733
+ errors: str | None = ...,
734
+ newline: str | None = ...,
735
+ ) -> AsyncFile[bytes]: ...
736
+
737
+ @overload
738
+ async def open(
739
+ self,
740
+ mode: OpenTextMode = ...,
741
+ buffering: int = ...,
742
+ encoding: str | None = ...,
743
+ errors: str | None = ...,
744
+ newline: str | None = ...,
745
+ ) -> AsyncFile[str]: ...
746
+
747
+ async def open(
748
+ self,
749
+ mode: str = "r",
750
+ buffering: int = -1,
751
+ encoding: str | None = None,
752
+ errors: str | None = None,
753
+ newline: str | None = None,
754
+ ) -> AsyncFile[Any]:
755
+ fp = await to_thread.run_sync(
756
+ self._path.open,
757
+ mode,
758
+ buffering,
759
+ encoding,
760
+ errors,
761
+ newline,
762
+ limiter=self._limiter,
763
+ )
764
+ return AsyncFile(fp, limiter=self._limiter)
765
+
766
+ async def owner(self) -> str:
767
+ return await to_thread.run_sync(
768
+ self._path.owner, abandon_on_cancel=True, limiter=self._limiter
769
+ )
770
+
771
+ async def read_bytes(self) -> bytes:
772
+ return await to_thread.run_sync(self._path.read_bytes, limiter=self._limiter)
773
+
774
+ async def read_text(
775
+ self, encoding: str | None = None, errors: str | None = None
776
+ ) -> str:
777
+ return await to_thread.run_sync(
778
+ self._path.read_text, encoding, errors, limiter=self._limiter
779
+ )
780
+
781
+ if sys.version_info >= (3, 12):
782
+
783
+ def relative_to(
784
+ self, *other: str | PathLike[str], walk_up: bool = False
785
+ ) -> Self:
786
+ # relative_to() should work with any PathLike but it doesn't
787
+ others = [pathlib.Path(other) for other in other]
788
+ return type(self)(
789
+ self._path.relative_to(*others, walk_up=walk_up), limiter=self._limiter
790
+ )
791
+
792
+ else:
793
+
794
+ def relative_to(self, *other: str | PathLike[str]) -> Self:
795
+ return type(self)(self._path.relative_to(*other), limiter=self._limiter)
796
+
797
+ async def readlink(self) -> Self:
798
+ target = await to_thread.run_sync(
799
+ os.readlink, self._path, limiter=self._limiter
800
+ )
801
+ return type(self)(target, limiter=self._limiter)
802
+
803
+ async def rename(self, target: str | pathlib.PurePath | Path) -> Self:
804
+ if isinstance(target, Path):
805
+ target = target._path
806
+
807
+ await to_thread.run_sync(self._path.rename, target, limiter=self._limiter)
808
+ return type(self)(target, limiter=self._limiter)
809
+
810
+ async def replace(self, target: str | pathlib.PurePath | Path) -> Self:
811
+ if isinstance(target, Path):
812
+ target = target._path
813
+
814
+ await to_thread.run_sync(self._path.replace, target, limiter=self._limiter)
815
+ return type(self)(target, limiter=self._limiter)
816
+
817
+ async def resolve(self, strict: bool = False) -> Self:
818
+ func = partial(self._path.resolve, strict=strict)
819
+ return type(self)(
820
+ await to_thread.run_sync(
821
+ func, abandon_on_cancel=True, limiter=self._limiter
822
+ ),
823
+ limiter=self._limiter,
824
+ )
825
+
826
+ if sys.version_info < (3, 12):
827
+ # Pre Python 3.12
828
+ def rglob(self, pattern: str) -> AsyncIterator[Self]:
829
+ gen = self._path.rglob(pattern)
830
+ return _PathIterator(gen, self._limiter, type(self))
831
+ elif (3, 12) <= sys.version_info < (3, 13):
832
+ # Changed in Python 3.12:
833
+ # - The case_sensitive parameter was added.
834
+ def rglob(
835
+ self, pattern: str, *, case_sensitive: bool | None = None
836
+ ) -> AsyncIterator[Self]:
837
+ gen = self._path.rglob(pattern, case_sensitive=case_sensitive)
838
+ return _PathIterator(gen, self._limiter, type(self))
839
+ elif sys.version_info >= (3, 13):
840
+ # Changed in Python 3.13:
841
+ # - The recurse_symlinks parameter was added.
842
+ # - The pattern parameter accepts a path-like object.
843
+ def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block
844
+ self,
845
+ pattern: str | PathLike[str],
846
+ *,
847
+ case_sensitive: bool | None = None,
848
+ recurse_symlinks: bool = False,
849
+ ) -> AsyncIterator[Self]:
850
+ gen = self._path.rglob(
851
+ pattern, # type: ignore[arg-type]
852
+ case_sensitive=case_sensitive,
853
+ recurse_symlinks=recurse_symlinks,
854
+ )
855
+ return _PathIterator(gen, self._limiter, type(self))
856
+
857
+ async def rmdir(self) -> None:
858
+ await to_thread.run_sync(self._path.rmdir, limiter=self._limiter)
859
+
860
+ async def samefile(self, other_path: str | PathLike[str]) -> bool:
861
+ if isinstance(other_path, Path):
862
+ other_path = other_path._path
863
+
864
+ return await to_thread.run_sync(
865
+ self._path.samefile,
866
+ other_path,
867
+ abandon_on_cancel=True,
868
+ limiter=self._limiter,
869
+ )
870
+
871
+ async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result:
872
+ func = partial(os.stat, follow_symlinks=follow_symlinks)
873
+ return await to_thread.run_sync(
874
+ func, self._path, abandon_on_cancel=True, limiter=self._limiter
875
+ )
876
+
877
+ async def symlink_to(
878
+ self,
879
+ target: str | bytes | PathLike[str] | PathLike[bytes],
880
+ target_is_directory: bool = False,
881
+ ) -> None:
882
+ if isinstance(target, Path):
883
+ target = target._path
884
+
885
+ await to_thread.run_sync(
886
+ self._path.symlink_to, target, target_is_directory, limiter=self._limiter
887
+ )
888
+
889
+ async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
890
+ await to_thread.run_sync(
891
+ self._path.touch, mode, exist_ok, limiter=self._limiter
892
+ )
893
+
894
+ async def unlink(self, missing_ok: bool = False) -> None:
895
+ try:
896
+ await to_thread.run_sync(self._path.unlink, limiter=self._limiter)
897
+ except FileNotFoundError:
898
+ if not missing_ok:
899
+ raise
900
+
901
+ if sys.version_info >= (3, 12):
902
+
903
+ async def walk(
904
+ self,
905
+ top_down: bool = True,
906
+ on_error: Callable[[OSError], object] | None = None,
907
+ follow_symlinks: bool = False,
908
+ ) -> AsyncIterator[tuple[Self, list[str], list[str]]]:
909
+ def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None:
910
+ try:
911
+ return next(gen)
912
+ except StopIteration:
913
+ return None
914
+
915
+ gen = self._path.walk(top_down, on_error, follow_symlinks)
916
+ while True:
917
+ value = await to_thread.run_sync(get_next_value, limiter=self._limiter)
918
+ if value is None:
919
+ return
920
+
921
+ root, dirs, paths = value
922
+ yield type(self)(root, limiter=self._limiter), dirs, paths
923
+
924
+ def with_name(self, name: str) -> Self:
925
+ return type(self)(self._path.with_name(name), limiter=self._limiter)
926
+
927
+ def with_stem(self, stem: str) -> Self:
928
+ return type(self)(
929
+ self._path.with_name(stem + self._path.suffix), limiter=self._limiter
930
+ )
931
+
932
+ def with_suffix(self, suffix: str) -> Self:
933
+ return type(self)(self._path.with_suffix(suffix), limiter=self._limiter)
934
+
935
+ def with_segments(self, *pathsegments: str | PathLike[str]) -> Self:
936
+ return type(self)(*pathsegments, limiter=self._limiter)
937
+
938
+ async def write_bytes(self, data: ReadableBuffer) -> int:
939
+ return await to_thread.run_sync(
940
+ self._path.write_bytes, data, limiter=self._limiter
941
+ )
942
+
943
+ async def write_text(
944
+ self,
945
+ data: str,
946
+ encoding: str | None = None,
947
+ errors: str | None = None,
948
+ newline: str | None = None,
949
+ ) -> int:
950
+ return await to_thread.run_sync(
951
+ self._path.write_text,
952
+ data,
953
+ encoding,
954
+ errors,
955
+ newline,
956
+ limiter=self._limiter,
957
+ )
958
+
959
+
960
+ PathLike.register(Path)
.venv/Lib/site-packages/anyio/_core/_resources.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from ..abc import AsyncResource
4
+ from ._tasks import CancelScope
5
+
6
+
7
+ async def aclose_forcefully(resource: AsyncResource) -> None:
8
+ """
9
+ Close an asynchronous resource in a cancelled scope.
10
+
11
+ Doing this closes the resource without waiting on anything.
12
+
13
+ :param resource: the resource to close
14
+
15
+ """
16
+ with CancelScope() as scope:
17
+ scope.cancel()
18
+ await resource.aclose()
.venv/Lib/site-packages/anyio/_core/_signals.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator
4
+ from contextlib import AbstractContextManager
5
+ from signal import Signals
6
+
7
+ from ._eventloop import get_async_backend
8
+
9
+
10
+ def open_signal_receiver(
11
+ *signals: Signals,
12
+ ) -> AbstractContextManager[AsyncIterator[Signals]]:
13
+ """
14
+ Start receiving operating system signals.
15
+
16
+ :param signals: signals to receive (e.g. ``signal.SIGINT``)
17
+ :return: an asynchronous context manager for an asynchronous iterator which yields
18
+ signal numbers
19
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
20
+ current thread
21
+
22
+ .. warning:: Windows does not support signals natively so it is best to avoid
23
+ relying on this in cross-platform applications.
24
+
25
+ .. warning:: On asyncio, this permanently replaces any previous signal handler for
26
+ the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`.
27
+
28
+ """
29
+ return get_async_backend().open_signal_receiver(*signals)
.venv/Lib/site-packages/anyio/_core/_sockets.py ADDED
@@ -0,0 +1,1011 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import errno
4
+ import os
5
+ import socket
6
+ import ssl
7
+ import stat
8
+ import sys
9
+ from collections.abc import Awaitable
10
+ from dataclasses import dataclass
11
+ from ipaddress import IPv4Address, IPv6Address, ip_address
12
+ from os import PathLike, chmod
13
+ from socket import AddressFamily, SocketKind
14
+ from typing import TYPE_CHECKING, Any, Literal, cast, overload
15
+
16
+ from .. import ConnectionFailed, to_thread
17
+ from ..abc import (
18
+ ByteStreamConnectable,
19
+ ConnectedUDPSocket,
20
+ ConnectedUNIXDatagramSocket,
21
+ IPAddressType,
22
+ IPSockAddrType,
23
+ SocketListener,
24
+ SocketStream,
25
+ UDPSocket,
26
+ UNIXDatagramSocket,
27
+ UNIXSocketStream,
28
+ )
29
+ from ..streams.stapled import MultiListener
30
+ from ..streams.tls import TLSConnectable, TLSStream
31
+ from ._eventloop import get_async_backend
32
+ from ._resources import aclose_forcefully
33
+ from ._synchronization import Event
34
+ from ._tasks import create_task_group, move_on_after
35
+
36
+ if TYPE_CHECKING:
37
+ from _typeshed import FileDescriptorLike
38
+ else:
39
+ FileDescriptorLike = object
40
+
41
+ if sys.version_info < (3, 11):
42
+ from exceptiongroup import ExceptionGroup
43
+
44
+ if sys.version_info >= (3, 12):
45
+ from typing import override
46
+ else:
47
+ from typing_extensions import override
48
+
49
+ if sys.version_info < (3, 13):
50
+ from typing_extensions import deprecated
51
+ else:
52
+ from warnings import deprecated
53
+
54
+ IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515
55
+
56
+ AnyIPAddressFamily = Literal[
57
+ AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6
58
+ ]
59
+ IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6]
60
+
61
+
62
+ # tls_hostname given
63
+ @overload
64
+ async def connect_tcp(
65
+ remote_host: IPAddressType,
66
+ remote_port: int,
67
+ *,
68
+ local_host: IPAddressType | None = ...,
69
+ local_port: int | None = ...,
70
+ ssl_context: ssl.SSLContext | None = ...,
71
+ tls_standard_compatible: bool = ...,
72
+ tls_hostname: str,
73
+ happy_eyeballs_delay: float = ...,
74
+ ) -> TLSStream: ...
75
+
76
+
77
+ # ssl_context given
78
+ @overload
79
+ async def connect_tcp(
80
+ remote_host: IPAddressType,
81
+ remote_port: int,
82
+ *,
83
+ local_host: IPAddressType | None = ...,
84
+ local_port: int | None = ...,
85
+ ssl_context: ssl.SSLContext,
86
+ tls_standard_compatible: bool = ...,
87
+ tls_hostname: str | None = ...,
88
+ happy_eyeballs_delay: float = ...,
89
+ ) -> TLSStream: ...
90
+
91
+
92
+ # tls=True
93
+ @overload
94
+ async def connect_tcp(
95
+ remote_host: IPAddressType,
96
+ remote_port: int,
97
+ *,
98
+ local_host: IPAddressType | None = ...,
99
+ local_port: int | None = ...,
100
+ tls: Literal[True],
101
+ ssl_context: ssl.SSLContext | None = ...,
102
+ tls_standard_compatible: bool = ...,
103
+ tls_hostname: str | None = ...,
104
+ happy_eyeballs_delay: float = ...,
105
+ ) -> TLSStream: ...
106
+
107
+
108
+ # tls=False
109
+ @overload
110
+ async def connect_tcp(
111
+ remote_host: IPAddressType,
112
+ remote_port: int,
113
+ *,
114
+ local_host: IPAddressType | None = ...,
115
+ local_port: int | None = ...,
116
+ tls: Literal[False],
117
+ ssl_context: ssl.SSLContext | None = ...,
118
+ tls_standard_compatible: bool = ...,
119
+ tls_hostname: str | None = ...,
120
+ happy_eyeballs_delay: float = ...,
121
+ ) -> SocketStream: ...
122
+
123
+
124
+ # No TLS arguments
125
+ @overload
126
+ async def connect_tcp(
127
+ remote_host: IPAddressType,
128
+ remote_port: int,
129
+ *,
130
+ local_host: IPAddressType | None = ...,
131
+ local_port: int | None = ...,
132
+ happy_eyeballs_delay: float = ...,
133
+ ) -> SocketStream: ...
134
+
135
+
136
+ async def connect_tcp(
137
+ remote_host: IPAddressType,
138
+ remote_port: int,
139
+ *,
140
+ local_host: IPAddressType | None = None,
141
+ local_port: int | None = None,
142
+ tls: bool = False,
143
+ ssl_context: ssl.SSLContext | None = None,
144
+ tls_standard_compatible: bool = True,
145
+ tls_hostname: str | None = None,
146
+ happy_eyeballs_delay: float = 0.25,
147
+ ) -> SocketStream | TLSStream:
148
+ """
149
+ Connect to a host using the TCP protocol.
150
+
151
+ This function implements the stateless version of the Happy Eyeballs algorithm (RFC
152
+ 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses,
153
+ each one is tried until one connection attempt succeeds. If the first attempt does
154
+ not connected within 250 milliseconds, a second attempt is started using the next
155
+ address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if
156
+ available) is tried first.
157
+
158
+ When the connection has been established, a TLS handshake will be done if either
159
+ ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``.
160
+
161
+ :param remote_host: the IP address or host name to connect to
162
+ :param remote_port: port on the target host to connect to
163
+ :param local_host: the interface address or name to bind the socket to before
164
+ connecting
165
+ :param local_port: the local port to bind to (requires ``local_host`` to also be
166
+ set)
167
+ :param tls: ``True`` to do a TLS handshake with the connected stream and return a
168
+ :class:`~anyio.streams.tls.TLSStream` instead
169
+ :param ssl_context: the SSL context object to use (if omitted, a default context is
170
+ created)
171
+ :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake
172
+ before closing the stream and requires that the server does this as well.
173
+ Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream.
174
+ Some protocols, such as HTTP, require this option to be ``False``.
175
+ See :meth:`~ssl.SSLContext.wrap_socket` for details.
176
+ :param tls_hostname: host name to check the server certificate against (defaults to
177
+ the value of ``remote_host``)
178
+ :param happy_eyeballs_delay: delay (in seconds) before starting the next connection
179
+ attempt
180
+ :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream
181
+ :raises ConnectionFailed: if the connection fails
182
+
183
+ """
184
+ # Placed here due to https://github.com/python/mypy/issues/7057
185
+ connected_stream: SocketStream | None = None
186
+
187
+ async def try_connect(remote_host: str, event: Event) -> None:
188
+ nonlocal connected_stream
189
+ try:
190
+ stream = await asynclib.connect_tcp(remote_host, remote_port, local_address)
191
+ except OSError as exc:
192
+ oserrors.append(exc)
193
+ return
194
+ else:
195
+ if connected_stream is None:
196
+ connected_stream = stream
197
+ tg.cancel_scope.cancel()
198
+ else:
199
+ await stream.aclose()
200
+ finally:
201
+ event.set()
202
+
203
+ asynclib = get_async_backend()
204
+ local_address: IPSockAddrType | None = None
205
+ family = socket.AF_UNSPEC
206
+ if local_host:
207
+ gai_res = await getaddrinfo(str(local_host), local_port)
208
+ family, *_, local_address = gai_res[0]
209
+
210
+ target_host = str(remote_host)
211
+ try:
212
+ addr_obj = ip_address(remote_host)
213
+ except ValueError:
214
+ addr_obj = None
215
+
216
+ if addr_obj is not None:
217
+ if isinstance(addr_obj, IPv6Address):
218
+ target_addrs = [(socket.AF_INET6, addr_obj.compressed)]
219
+ else:
220
+ target_addrs = [(socket.AF_INET, addr_obj.compressed)]
221
+ else:
222
+ # getaddrinfo() will raise an exception if name resolution fails
223
+ gai_res = await getaddrinfo(
224
+ target_host, remote_port, family=family, type=socket.SOCK_STREAM
225
+ )
226
+
227
+ # Organize the list so that the first address is an IPv6 address (if available)
228
+ # and the second one is an IPv4 addresses. The rest can be in whatever order.
229
+ v6_found = v4_found = False
230
+ target_addrs = []
231
+ for af, *_, sa in gai_res:
232
+ if af == socket.AF_INET6 and not v6_found:
233
+ v6_found = True
234
+ target_addrs.insert(0, (af, sa[0]))
235
+ elif af == socket.AF_INET and not v4_found and v6_found:
236
+ v4_found = True
237
+ target_addrs.insert(1, (af, sa[0]))
238
+ else:
239
+ target_addrs.append((af, sa[0]))
240
+
241
+ oserrors: list[OSError] = []
242
+ try:
243
+ async with create_task_group() as tg:
244
+ for _af, addr in target_addrs:
245
+ event = Event()
246
+ tg.start_soon(try_connect, addr, event)
247
+ with move_on_after(happy_eyeballs_delay):
248
+ await event.wait()
249
+
250
+ if connected_stream is None:
251
+ cause = (
252
+ oserrors[0]
253
+ if len(oserrors) == 1
254
+ else ExceptionGroup("multiple connection attempts failed", oserrors)
255
+ )
256
+ raise OSError("All connection attempts failed") from cause
257
+ finally:
258
+ oserrors.clear()
259
+
260
+ if tls or tls_hostname or ssl_context:
261
+ try:
262
+ return await TLSStream.wrap(
263
+ connected_stream,
264
+ server_side=False,
265
+ hostname=tls_hostname or str(remote_host),
266
+ ssl_context=ssl_context,
267
+ standard_compatible=tls_standard_compatible,
268
+ )
269
+ except BaseException:
270
+ await aclose_forcefully(connected_stream)
271
+ raise
272
+
273
+ return connected_stream
274
+
275
+
276
+ async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream:
277
+ """
278
+ Connect to the given UNIX socket.
279
+
280
+ Not available on Windows.
281
+
282
+ :param path: path to the socket
283
+ :return: a socket stream object
284
+ :raises ConnectionFailed: if the connection fails
285
+
286
+ """
287
+ path = os.fspath(path)
288
+ return await get_async_backend().connect_unix(path)
289
+
290
+
291
+ async def create_tcp_listener(
292
+ *,
293
+ local_host: IPAddressType | None = None,
294
+ local_port: int = 0,
295
+ family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC,
296
+ backlog: int = 65536,
297
+ reuse_port: bool = False,
298
+ ) -> MultiListener[SocketStream]:
299
+ """
300
+ Create a TCP socket listener.
301
+
302
+ :param local_port: port number to listen on
303
+ :param local_host: IP address of the interface to listen on. If omitted, listen on
304
+ all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address
305
+ family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6.
306
+ :param family: address family (used if ``local_host`` was omitted)
307
+ :param backlog: maximum number of queued incoming connections (up to a maximum of
308
+ 2**16, or 65536)
309
+ :param reuse_port: ``True`` to allow multiple sockets to bind to the same
310
+ address/port (not supported on Windows)
311
+ :return: a multi-listener object containing one or more socket listeners
312
+ :raises OSError: if there's an error creating a socket, or binding to one or more
313
+ interfaces failed
314
+
315
+ """
316
+ asynclib = get_async_backend()
317
+ backlog = min(backlog, 65536)
318
+ local_host = str(local_host) if local_host is not None else None
319
+
320
+ def setup_raw_socket(
321
+ fam: AddressFamily,
322
+ bind_addr: tuple[str, int] | tuple[str, int, int, int],
323
+ *,
324
+ v6only: bool = True,
325
+ ) -> socket.socket:
326
+ sock = socket.socket(fam)
327
+ try:
328
+ sock.setblocking(False)
329
+
330
+ if fam == AddressFamily.AF_INET6:
331
+ sock.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, v6only)
332
+
333
+ # For Windows, enable exclusive address use. For others, enable address
334
+ # reuse.
335
+ if sys.platform == "win32":
336
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
337
+ else:
338
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
339
+
340
+ if reuse_port:
341
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
342
+
343
+ # Workaround for #554
344
+ if fam == socket.AF_INET6 and "%" in bind_addr[0]:
345
+ addr, scope_id = bind_addr[0].split("%", 1)
346
+ bind_addr = (addr, bind_addr[1], 0, int(scope_id))
347
+
348
+ sock.bind(bind_addr)
349
+ sock.listen(backlog)
350
+ except BaseException:
351
+ sock.close()
352
+ raise
353
+
354
+ return sock
355
+
356
+ # We passing type=0 on non-Windows platforms as a workaround for a uvloop bug
357
+ # where we don't get the correct scope ID for IPv6 link-local addresses when passing
358
+ # type=socket.SOCK_STREAM to getaddrinfo():
359
+ # https://github.com/MagicStack/uvloop/issues/539
360
+ gai_res = await getaddrinfo(
361
+ local_host,
362
+ local_port,
363
+ family=family,
364
+ type=socket.SOCK_STREAM if sys.platform == "win32" else 0,
365
+ flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG,
366
+ )
367
+
368
+ # The set comprehension is here to work around a glibc bug:
369
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=14969
370
+ sockaddrs = sorted({res for res in gai_res if res[1] == SocketKind.SOCK_STREAM})
371
+
372
+ # Special case for dual-stack binding on the "any" interface
373
+ if (
374
+ local_host is None
375
+ and family == AddressFamily.AF_UNSPEC
376
+ and socket.has_dualstack_ipv6()
377
+ and any(fam == AddressFamily.AF_INET6 for fam, *_ in gai_res)
378
+ ):
379
+ raw_socket = setup_raw_socket(
380
+ AddressFamily.AF_INET6, ("::", local_port), v6only=False
381
+ )
382
+ listener = asynclib.create_tcp_listener(raw_socket)
383
+ return MultiListener([listener])
384
+
385
+ errors: list[OSError] = []
386
+ try:
387
+ for _ in range(len(sockaddrs)):
388
+ listeners: list[SocketListener] = []
389
+ bound_ephemeral_port = local_port
390
+ try:
391
+ for fam, *_, sockaddr in sockaddrs:
392
+ sockaddr = sockaddr[0], bound_ephemeral_port, *sockaddr[2:]
393
+ raw_socket = setup_raw_socket(fam, sockaddr)
394
+
395
+ # Store the assigned port if an ephemeral port was requested, so
396
+ # we'll bind to the same port on all interfaces
397
+ if local_port == 0 and len(gai_res) > 1:
398
+ bound_ephemeral_port = raw_socket.getsockname()[1]
399
+
400
+ listeners.append(asynclib.create_tcp_listener(raw_socket))
401
+ except BaseException as exc:
402
+ for listener in listeners:
403
+ await listener.aclose()
404
+
405
+ # If an ephemeral port was requested but binding the assigned port
406
+ # failed for another interface, rotate the address list and try again
407
+ if (
408
+ isinstance(exc, OSError)
409
+ and exc.errno == errno.EADDRINUSE
410
+ and local_port == 0
411
+ and bound_ephemeral_port
412
+ ):
413
+ errors.append(exc)
414
+ sockaddrs.append(sockaddrs.pop(0))
415
+ continue
416
+
417
+ raise
418
+
419
+ return MultiListener(listeners)
420
+
421
+ raise OSError(
422
+ f"Could not create {len(sockaddrs)} listeners with a consistent port"
423
+ ) from ExceptionGroup("Several bind attempts failed", errors)
424
+ finally:
425
+ del errors # Prevent reference cycles
426
+
427
+
428
+ async def create_unix_listener(
429
+ path: str | bytes | PathLike[Any],
430
+ *,
431
+ mode: int | None = None,
432
+ backlog: int = 65536,
433
+ ) -> SocketListener:
434
+ """
435
+ Create a UNIX socket listener.
436
+
437
+ Not available on Windows.
438
+
439
+ :param path: path of the socket
440
+ :param mode: permissions to set on the socket
441
+ :param backlog: maximum number of queued incoming connections (up to a maximum of
442
+ 2**16, or 65536)
443
+ :return: a listener object
444
+
445
+ .. versionchanged:: 3.0
446
+ If a socket already exists on the file system in the given path, it will be
447
+ removed first.
448
+
449
+ """
450
+ backlog = min(backlog, 65536)
451
+ raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM)
452
+ try:
453
+ raw_socket.listen(backlog)
454
+ return get_async_backend().create_unix_listener(raw_socket)
455
+ except BaseException:
456
+ raw_socket.close()
457
+ raise
458
+
459
+
460
+ async def create_udp_socket(
461
+ family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC,
462
+ *,
463
+ local_host: IPAddressType | None = None,
464
+ local_port: int = 0,
465
+ reuse_port: bool = False,
466
+ ) -> UDPSocket:
467
+ """
468
+ Create a UDP socket.
469
+
470
+ If ``port`` has been given, the socket will be bound to this port on the local
471
+ machine, making this socket suitable for providing UDP based services.
472
+
473
+ :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically
474
+ determined from ``local_host`` if omitted
475
+ :param local_host: IP address or host name of the local interface to bind to
476
+ :param local_port: local port to bind to
477
+ :param reuse_port: ``True`` to allow multiple sockets to bind to the same
478
+ address/port (not supported on Windows)
479
+ :return: a UDP socket
480
+
481
+ """
482
+ if family is AddressFamily.AF_UNSPEC and not local_host:
483
+ raise ValueError('Either "family" or "local_host" must be given')
484
+
485
+ if local_host:
486
+ gai_res = await getaddrinfo(
487
+ str(local_host),
488
+ local_port,
489
+ family=family,
490
+ type=socket.SOCK_DGRAM,
491
+ flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG,
492
+ )
493
+ family = cast(AnyIPAddressFamily, gai_res[0][0])
494
+ local_address = gai_res[0][-1]
495
+ elif family is AddressFamily.AF_INET6:
496
+ local_address = ("::", 0)
497
+ else:
498
+ local_address = ("0.0.0.0", 0)
499
+
500
+ sock = await get_async_backend().create_udp_socket(
501
+ family, local_address, None, reuse_port
502
+ )
503
+ return cast(UDPSocket, sock)
504
+
505
+
506
+ async def create_connected_udp_socket(
507
+ remote_host: IPAddressType,
508
+ remote_port: int,
509
+ *,
510
+ family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC,
511
+ local_host: IPAddressType | None = None,
512
+ local_port: int = 0,
513
+ reuse_port: bool = False,
514
+ ) -> ConnectedUDPSocket:
515
+ """
516
+ Create a connected UDP socket.
517
+
518
+ Connected UDP sockets can only communicate with the specified remote host/port, an
519
+ any packets sent from other sources are dropped.
520
+
521
+ :param remote_host: remote host to set as the default target
522
+ :param remote_port: port on the remote host to set as the default target
523
+ :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically
524
+ determined from ``local_host`` or ``remote_host`` if omitted
525
+ :param local_host: IP address or host name of the local interface to bind to
526
+ :param local_port: local port to bind to
527
+ :param reuse_port: ``True`` to allow multiple sockets to bind to the same
528
+ address/port (not supported on Windows)
529
+ :return: a connected UDP socket
530
+
531
+ """
532
+ local_address = None
533
+ if local_host:
534
+ gai_res = await getaddrinfo(
535
+ str(local_host),
536
+ local_port,
537
+ family=family,
538
+ type=socket.SOCK_DGRAM,
539
+ flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG,
540
+ )
541
+ family = cast(AnyIPAddressFamily, gai_res[0][0])
542
+ local_address = gai_res[0][-1]
543
+
544
+ gai_res = await getaddrinfo(
545
+ str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM
546
+ )
547
+ family = cast(AnyIPAddressFamily, gai_res[0][0])
548
+ remote_address = gai_res[0][-1]
549
+
550
+ sock = await get_async_backend().create_udp_socket(
551
+ family, local_address, remote_address, reuse_port
552
+ )
553
+ return cast(ConnectedUDPSocket, sock)
554
+
555
+
556
+ async def create_unix_datagram_socket(
557
+ *,
558
+ local_path: None | str | bytes | PathLike[Any] = None,
559
+ local_mode: int | None = None,
560
+ ) -> UNIXDatagramSocket:
561
+ """
562
+ Create a UNIX datagram socket.
563
+
564
+ Not available on Windows.
565
+
566
+ If ``local_path`` has been given, the socket will be bound to this path, making this
567
+ socket suitable for receiving datagrams from other processes. Other processes can
568
+ send datagrams to this socket only if ``local_path`` is set.
569
+
570
+ If a socket already exists on the file system in the ``local_path``, it will be
571
+ removed first.
572
+
573
+ :param local_path: the path on which to bind to
574
+ :param local_mode: permissions to set on the local socket
575
+ :return: a UNIX datagram socket
576
+
577
+ """
578
+ raw_socket = await setup_unix_local_socket(
579
+ local_path, local_mode, socket.SOCK_DGRAM
580
+ )
581
+ return await get_async_backend().create_unix_datagram_socket(raw_socket, None)
582
+
583
+
584
+ async def create_connected_unix_datagram_socket(
585
+ remote_path: str | bytes | PathLike[Any],
586
+ *,
587
+ local_path: None | str | bytes | PathLike[Any] = None,
588
+ local_mode: int | None = None,
589
+ ) -> ConnectedUNIXDatagramSocket:
590
+ """
591
+ Create a connected UNIX datagram socket.
592
+
593
+ Connected datagram sockets can only communicate with the specified remote path.
594
+
595
+ If ``local_path`` has been given, the socket will be bound to this path, making
596
+ this socket suitable for receiving datagrams from other processes. Other processes
597
+ can send datagrams to this socket only if ``local_path`` is set.
598
+
599
+ If a socket already exists on the file system in the ``local_path``, it will be
600
+ removed first.
601
+
602
+ :param remote_path: the path to set as the default target
603
+ :param local_path: the path on which to bind to
604
+ :param local_mode: permissions to set on the local socket
605
+ :return: a connected UNIX datagram socket
606
+
607
+ """
608
+ remote_path = os.fspath(remote_path)
609
+ raw_socket = await setup_unix_local_socket(
610
+ local_path, local_mode, socket.SOCK_DGRAM
611
+ )
612
+ return await get_async_backend().create_unix_datagram_socket(
613
+ raw_socket, remote_path
614
+ )
615
+
616
+
617
+ async def getaddrinfo(
618
+ host: bytes | str | None,
619
+ port: str | int | None,
620
+ *,
621
+ family: int | AddressFamily = 0,
622
+ type: int | SocketKind = 0,
623
+ proto: int = 0,
624
+ flags: int = 0,
625
+ ) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]:
626
+ """
627
+ Look up a numeric IP address given a host name.
628
+
629
+ Internationalized domain names are translated according to the (non-transitional)
630
+ IDNA 2008 standard.
631
+
632
+ .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of
633
+ (host, port), unlike what :func:`socket.getaddrinfo` does.
634
+
635
+ :param host: host name
636
+ :param port: port number
637
+ :param family: socket family (`'AF_INET``, ...)
638
+ :param type: socket type (``SOCK_STREAM``, ...)
639
+ :param proto: protocol number
640
+ :param flags: flags to pass to upstream ``getaddrinfo()``
641
+ :return: list of tuples containing (family, type, proto, canonname, sockaddr)
642
+
643
+ .. seealso:: :func:`socket.getaddrinfo`
644
+
645
+ """
646
+ # Handle unicode hostnames
647
+ if isinstance(host, str):
648
+ try:
649
+ encoded_host: bytes | None = host.encode("ascii")
650
+ except UnicodeEncodeError:
651
+ import idna
652
+
653
+ encoded_host = idna.encode(host, uts46=True)
654
+ else:
655
+ encoded_host = host
656
+
657
+ gai_res = await get_async_backend().getaddrinfo(
658
+ encoded_host, port, family=family, type=type, proto=proto, flags=flags
659
+ )
660
+ return [
661
+ (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr))
662
+ for family, type, proto, canonname, sockaddr in gai_res
663
+ # filter out IPv6 results when IPv6 is disabled
664
+ if not isinstance(sockaddr[0], int)
665
+ ]
666
+
667
+
668
+ def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]:
669
+ """
670
+ Look up the host name of an IP address.
671
+
672
+ :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4)
673
+ :param flags: flags to pass to upstream ``getnameinfo()``
674
+ :return: a tuple of (host name, service name)
675
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
676
+ current thread
677
+
678
+ .. seealso:: :func:`socket.getnameinfo`
679
+
680
+ """
681
+ return get_async_backend().getnameinfo(sockaddr, flags)
682
+
683
+
684
+ @deprecated("This function is deprecated; use `wait_readable` instead")
685
+ def wait_socket_readable(sock: socket.socket) -> Awaitable[None]:
686
+ """
687
+ .. deprecated:: 4.7.0
688
+ Use :func:`wait_readable` instead.
689
+
690
+ Wait until the given socket has data to be read.
691
+
692
+ .. warning:: Only use this on raw sockets that have not been wrapped by any higher
693
+ level constructs like socket streams!
694
+
695
+ :param sock: a socket object
696
+ :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the
697
+ socket to become readable
698
+ :raises ~anyio.BusyResourceError: if another task is already waiting for the socket
699
+ to become readable
700
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
701
+ current thread
702
+
703
+ """
704
+ return get_async_backend().wait_readable(sock.fileno())
705
+
706
+
707
+ @deprecated("This function is deprecated; use `wait_writable` instead")
708
+ def wait_socket_writable(sock: socket.socket) -> Awaitable[None]:
709
+ """
710
+ .. deprecated:: 4.7.0
711
+ Use :func:`wait_writable` instead.
712
+
713
+ Wait until the given socket can be written to.
714
+
715
+ This does **NOT** work on Windows when using the asyncio backend with a proactor
716
+ event loop (default on py3.8+).
717
+
718
+ .. warning:: Only use this on raw sockets that have not been wrapped by any higher
719
+ level constructs like socket streams!
720
+
721
+ :param sock: a socket object
722
+ :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the
723
+ socket to become writable
724
+ :raises ~anyio.BusyResourceError: if another task is already waiting for the socket
725
+ to become writable
726
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
727
+ current thread
728
+
729
+ """
730
+ return get_async_backend().wait_writable(sock.fileno())
731
+
732
+
733
+ def wait_readable(obj: FileDescriptorLike) -> Awaitable[None]:
734
+ """
735
+ Wait until the given object has data to be read.
736
+
737
+ On Unix systems, ``obj`` must either be an integer file descriptor, or else an
738
+ object with a ``.fileno()`` method which returns an integer file descriptor. Any
739
+ kind of file descriptor can be passed, though the exact semantics will depend on
740
+ your kernel. For example, this probably won't do anything useful for on-disk files.
741
+
742
+ On Windows systems, ``obj`` must either be an integer ``SOCKET`` handle, or else an
743
+ object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File
744
+ descriptors aren't supported, and neither are handles that refer to anything besides
745
+ a ``SOCKET``.
746
+
747
+ On backends where this functionality is not natively provided (asyncio
748
+ ``ProactorEventLoop`` on Windows), it is provided using a separate selector thread
749
+ which is set to shut down when the interpreter shuts down.
750
+
751
+ .. warning:: Don't use this on raw sockets that have been wrapped by any higher
752
+ level constructs like socket streams!
753
+
754
+ :param obj: an object with a ``.fileno()`` method or an integer handle
755
+ :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the
756
+ object to become readable
757
+ :raises ~anyio.BusyResourceError: if another task is already waiting for the object
758
+ to become readable
759
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
760
+ current thread
761
+
762
+ """
763
+ return get_async_backend().wait_readable(obj)
764
+
765
+
766
+ def wait_writable(obj: FileDescriptorLike) -> Awaitable[None]:
767
+ """
768
+ Wait until the given object can be written to.
769
+
770
+ :param obj: an object with a ``.fileno()`` method or an integer handle
771
+ :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the
772
+ object to become writable
773
+ :raises ~anyio.BusyResourceError: if another task is already waiting for the object
774
+ to become writable
775
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
776
+ current thread
777
+
778
+ .. seealso:: See the documentation of :func:`wait_readable` for the definition of
779
+ ``obj`` and notes on backend compatibility.
780
+
781
+ .. warning:: Don't use this on raw sockets that have been wrapped by any higher
782
+ level constructs like socket streams!
783
+
784
+ """
785
+ return get_async_backend().wait_writable(obj)
786
+
787
+
788
+ def notify_closing(obj: FileDescriptorLike) -> None:
789
+ """
790
+ Call this before closing a file descriptor (on Unix) or socket (on
791
+ Windows). This will cause any `wait_readable` or `wait_writable`
792
+ calls on the given object to immediately wake up and raise
793
+ `~anyio.ClosedResourceError`.
794
+
795
+ This doesn't actually close the object – you still have to do that
796
+ yourself afterwards. Also, you want to be careful to make sure no
797
+ new tasks start waiting on the object in between when you call this
798
+ and when it's actually closed. So to close something properly, you
799
+ usually want to do these steps in order:
800
+
801
+ 1. Explicitly mark the object as closed, so that any new attempts
802
+ to use it will abort before they start.
803
+ 2. Call `notify_closing` to wake up any already-existing users.
804
+ 3. Actually close the object.
805
+
806
+ It's also possible to do them in a different order if that's more
807
+ convenient, *but only if* you make sure not to have any checkpoints in
808
+ between the steps. This way they all happen in a single atomic
809
+ step, so other tasks won't be able to tell what order they happened
810
+ in anyway.
811
+
812
+ :param obj: an object with a ``.fileno()`` method or an integer handle
813
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
814
+ current thread
815
+
816
+ """
817
+ get_async_backend().notify_closing(obj)
818
+
819
+
820
+ #
821
+ # Private API
822
+ #
823
+
824
+
825
+ def convert_ipv6_sockaddr(
826
+ sockaddr: tuple[str, int, int, int] | tuple[str, int],
827
+ ) -> tuple[str, int]:
828
+ """
829
+ Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format.
830
+
831
+ If the scope ID is nonzero, it is added to the address, separated with ``%``.
832
+ Otherwise the flow id and scope id are simply cut off from the tuple.
833
+ Any other kinds of socket addresses are returned as-is.
834
+
835
+ :param sockaddr: the result of :meth:`~socket.socket.getsockname`
836
+ :return: the converted socket address
837
+
838
+ """
839
+ # This is more complicated than it should be because of MyPy
840
+ if isinstance(sockaddr, tuple) and len(sockaddr) == 4:
841
+ host, port, flowinfo, scope_id = sockaddr
842
+ if scope_id:
843
+ # PyPy (as of v7.3.11) leaves the interface name in the result, so
844
+ # we discard it and only get the scope ID from the end
845
+ # (https://foss.heptapod.net/pypy/pypy/-/issues/3938)
846
+ host = host.split("%")[0]
847
+
848
+ # Add scope_id to the address
849
+ return f"{host}%{scope_id}", port
850
+ else:
851
+ return host, port
852
+ else:
853
+ return sockaddr
854
+
855
+
856
+ async def setup_unix_local_socket(
857
+ path: None | str | bytes | PathLike[Any],
858
+ mode: int | None,
859
+ socktype: int,
860
+ ) -> socket.socket:
861
+ """
862
+ Create a UNIX local socket object, deleting the socket at the given path if it
863
+ exists.
864
+
865
+ Not available on Windows.
866
+
867
+ :param path: path of the socket
868
+ :param mode: permissions to set on the socket
869
+ :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM
870
+
871
+ """
872
+ path_str: str | None
873
+ if path is not None:
874
+ path_str = os.fsdecode(path)
875
+
876
+ # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call
877
+ if not path_str.startswith("\0"):
878
+ # Copied from pathlib...
879
+ try:
880
+ stat_result = os.stat(path)
881
+ except OSError as e:
882
+ if e.errno not in (
883
+ errno.ENOENT,
884
+ errno.ENOTDIR,
885
+ errno.EBADF,
886
+ errno.ELOOP,
887
+ ):
888
+ raise
889
+ else:
890
+ if stat.S_ISSOCK(stat_result.st_mode):
891
+ os.unlink(path)
892
+ else:
893
+ path_str = None
894
+
895
+ raw_socket = socket.socket(socket.AF_UNIX, socktype)
896
+ raw_socket.setblocking(False)
897
+
898
+ if path_str is not None:
899
+ try:
900
+ await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True)
901
+ if mode is not None:
902
+ await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True)
903
+ except BaseException:
904
+ raw_socket.close()
905
+ raise
906
+
907
+ return raw_socket
908
+
909
+
910
+ @dataclass
911
+ class TCPConnectable(ByteStreamConnectable):
912
+ """
913
+ Connects to a TCP server at the given host and port.
914
+
915
+ :param host: host name or IP address of the server
916
+ :param port: TCP port number of the server
917
+ """
918
+
919
+ host: str | IPv4Address | IPv6Address
920
+ port: int
921
+
922
+ def __post_init__(self) -> None:
923
+ if self.port < 1 or self.port > 65535:
924
+ raise ValueError("TCP port number out of range")
925
+
926
+ @override
927
+ async def connect(self) -> SocketStream:
928
+ try:
929
+ return await connect_tcp(self.host, self.port)
930
+ except OSError as exc:
931
+ raise ConnectionFailed(
932
+ f"error connecting to {self.host}:{self.port}: {exc}"
933
+ ) from exc
934
+
935
+
936
+ @dataclass
937
+ class UNIXConnectable(ByteStreamConnectable):
938
+ """
939
+ Connects to a UNIX domain socket at the given path.
940
+
941
+ :param path: the file system path of the socket
942
+ """
943
+
944
+ path: str | bytes | PathLike[str] | PathLike[bytes]
945
+
946
+ @override
947
+ async def connect(self) -> UNIXSocketStream:
948
+ try:
949
+ return await connect_unix(self.path)
950
+ except OSError as exc:
951
+ raise ConnectionFailed(f"error connecting to {self.path!r}: {exc}") from exc
952
+
953
+
954
+ def as_connectable(
955
+ remote: ByteStreamConnectable
956
+ | tuple[str | IPv4Address | IPv6Address, int]
957
+ | str
958
+ | bytes
959
+ | PathLike[str],
960
+ /,
961
+ *,
962
+ tls: bool = False,
963
+ ssl_context: ssl.SSLContext | None = None,
964
+ tls_hostname: str | None = None,
965
+ tls_standard_compatible: bool = True,
966
+ ) -> ByteStreamConnectable:
967
+ """
968
+ Return a byte stream connectable from the given object.
969
+
970
+ If a bytestream connectable is given, it is returned unchanged.
971
+ If a tuple of (host, port) is given, a TCP connectable is returned.
972
+ If a string or bytes path is given, a UNIX connectable is returned.
973
+
974
+ If ``tls=True``, the connectable will be wrapped in a
975
+ :class:`~.streams.tls.TLSConnectable`.
976
+
977
+ :param remote: a connectable, a tuple of (host, port) or a path to a UNIX socket
978
+ :param tls: if ``True``, wrap the plaintext connectable in a
979
+ :class:`~.streams.tls.TLSConnectable`, using the provided TLS settings)
980
+ :param ssl_context: if ``tls=True``, the SSLContext object to use (if not provided,
981
+ a secure default will be created)
982
+ :param tls_hostname: if ``tls=True``, host name of the server to use for checking
983
+ the server certificate (defaults to the host portion of the address for TCP
984
+ connectables)
985
+ :param tls_standard_compatible: if ``False`` and ``tls=True``, makes the TLS stream
986
+ skip the closing handshake when closing the connection, so it won't raise an
987
+ exception if the server does the same
988
+
989
+ """
990
+ connectable: TCPConnectable | UNIXConnectable | TLSConnectable
991
+ if isinstance(remote, ByteStreamConnectable):
992
+ return remote
993
+ elif isinstance(remote, tuple) and len(remote) == 2:
994
+ connectable = TCPConnectable(*remote)
995
+ elif isinstance(remote, (str, bytes, PathLike)):
996
+ connectable = UNIXConnectable(remote)
997
+ else:
998
+ raise TypeError(f"cannot convert {remote!r} to a connectable")
999
+
1000
+ if tls:
1001
+ if not tls_hostname and isinstance(connectable, TCPConnectable):
1002
+ tls_hostname = str(connectable.host)
1003
+
1004
+ connectable = TLSConnectable(
1005
+ connectable,
1006
+ ssl_context=ssl_context,
1007
+ hostname=tls_hostname,
1008
+ standard_compatible=tls_standard_compatible,
1009
+ )
1010
+
1011
+ return connectable
.venv/Lib/site-packages/anyio/_core/_streams.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from typing import TypeVar
5
+ from warnings import warn
6
+
7
+ from ..streams.memory import (
8
+ MemoryObjectReceiveStream,
9
+ MemoryObjectSendStream,
10
+ _MemoryObjectStreamState,
11
+ )
12
+
13
+ T_Item = TypeVar("T_Item")
14
+
15
+
16
+ class create_memory_object_stream(
17
+ tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]],
18
+ ):
19
+ """
20
+ Create a memory object stream.
21
+
22
+ The stream's item type can be annotated like
23
+ :func:`create_memory_object_stream[T_Item]`.
24
+
25
+ :param max_buffer_size: number of items held in the buffer until ``send()`` starts
26
+ blocking
27
+ :param item_type: old way of marking the streams with the right generic type for
28
+ static typing (does nothing on AnyIO 4)
29
+
30
+ .. deprecated:: 4.0
31
+ Use ``create_memory_object_stream[YourItemType](...)`` instead.
32
+ :return: a tuple of (send stream, receive stream)
33
+
34
+ """
35
+
36
+ def __new__( # type: ignore[misc]
37
+ cls, max_buffer_size: float = 0, item_type: object = None
38
+ ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]:
39
+ if max_buffer_size != math.inf and not isinstance(max_buffer_size, int):
40
+ raise ValueError("max_buffer_size must be either an integer or math.inf")
41
+ if max_buffer_size < 0:
42
+ raise ValueError("max_buffer_size cannot be negative")
43
+ if item_type is not None:
44
+ warn(
45
+ "The item_type argument has been deprecated in AnyIO 4.0. "
46
+ "Use create_memory_object_stream[YourItemType](...) instead.",
47
+ DeprecationWarning,
48
+ stacklevel=2,
49
+ )
50
+
51
+ state = _MemoryObjectStreamState[T_Item](max_buffer_size)
52
+ return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state))
.venv/Lib/site-packages/anyio/_core/_subprocesses.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterable, Iterable, Mapping, Sequence
4
+ from io import BytesIO
5
+ from os import PathLike
6
+ from subprocess import PIPE, CalledProcessError, CompletedProcess
7
+ from typing import IO, Any, TypeAlias, cast
8
+
9
+ from ..abc import Process
10
+ from ._eventloop import get_async_backend
11
+ from ._tasks import create_task_group
12
+
13
+ StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
14
+
15
+
16
+ async def run_process(
17
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
18
+ *,
19
+ input: bytes | None = None,
20
+ stdin: int | IO[Any] | None = None,
21
+ stdout: int | IO[Any] | None = PIPE,
22
+ stderr: int | IO[Any] | None = PIPE,
23
+ check: bool = True,
24
+ cwd: StrOrBytesPath | None = None,
25
+ env: Mapping[str, str] | None = None,
26
+ startupinfo: Any = None,
27
+ creationflags: int = 0,
28
+ start_new_session: bool = False,
29
+ pass_fds: Sequence[int] = (),
30
+ user: str | int | None = None,
31
+ group: str | int | None = None,
32
+ extra_groups: Iterable[str | int] | None = None,
33
+ umask: int = -1,
34
+ ) -> CompletedProcess[bytes]:
35
+ """
36
+ Run an external command in a subprocess and wait until it completes.
37
+
38
+ .. seealso:: :func:`subprocess.run`
39
+
40
+ :param command: either a string to pass to the shell, or an iterable of strings
41
+ containing the executable name or path and its arguments
42
+ :param input: bytes passed to the standard input of the subprocess
43
+ :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
44
+ a file-like object, or `None`; ``input`` overrides this
45
+ :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
46
+ a file-like object, or `None`
47
+ :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
48
+ :data:`subprocess.STDOUT`, a file-like object, or `None`
49
+ :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the
50
+ process terminates with a return code other than 0
51
+ :param cwd: If not ``None``, change the working directory to this before running the
52
+ command
53
+ :param env: if not ``None``, this mapping replaces the inherited environment
54
+ variables from the parent process
55
+ :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used
56
+ to specify process startup parameters (Windows only)
57
+ :param creationflags: flags that can be used to control the creation of the
58
+ subprocess (see :class:`subprocess.Popen` for the specifics)
59
+ :param start_new_session: if ``true`` the setsid() system call will be made in the
60
+ child process prior to the execution of the subprocess. (POSIX only)
61
+ :param pass_fds: sequence of file descriptors to keep open between the parent and
62
+ child processes. (POSIX only)
63
+ :param user: effective user to run the process as (Python >= 3.9, POSIX only)
64
+ :param group: effective group to run the process as (Python >= 3.9, POSIX only)
65
+ :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9,
66
+ POSIX only)
67
+ :param umask: if not negative, this umask is applied in the child process before
68
+ running the given command (Python >= 3.9, POSIX only)
69
+ :return: an object representing the completed process
70
+ :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process
71
+ exits with a nonzero return code
72
+
73
+ """
74
+
75
+ async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None:
76
+ buffer = BytesIO()
77
+ async for chunk in stream:
78
+ buffer.write(chunk)
79
+
80
+ stream_contents[index] = buffer.getvalue()
81
+
82
+ if stdin is not None and input is not None:
83
+ raise ValueError("only one of stdin and input is allowed")
84
+
85
+ async with await open_process(
86
+ command,
87
+ stdin=PIPE if input else stdin,
88
+ stdout=stdout,
89
+ stderr=stderr,
90
+ cwd=cwd,
91
+ env=env,
92
+ startupinfo=startupinfo,
93
+ creationflags=creationflags,
94
+ start_new_session=start_new_session,
95
+ pass_fds=pass_fds,
96
+ user=user,
97
+ group=group,
98
+ extra_groups=extra_groups,
99
+ umask=umask,
100
+ ) as process:
101
+ stream_contents: list[bytes | None] = [None, None]
102
+ async with create_task_group() as tg:
103
+ if process.stdout:
104
+ tg.start_soon(drain_stream, process.stdout, 0)
105
+
106
+ if process.stderr:
107
+ tg.start_soon(drain_stream, process.stderr, 1)
108
+
109
+ if process.stdin and input:
110
+ await process.stdin.send(input)
111
+ await process.stdin.aclose()
112
+
113
+ await process.wait()
114
+
115
+ output, errors = stream_contents
116
+ if check and process.returncode != 0:
117
+ raise CalledProcessError(cast(int, process.returncode), command, output, errors)
118
+
119
+ return CompletedProcess(command, cast(int, process.returncode), output, errors)
120
+
121
+
122
+ async def open_process(
123
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
124
+ *,
125
+ stdin: int | IO[Any] | None = PIPE,
126
+ stdout: int | IO[Any] | None = PIPE,
127
+ stderr: int | IO[Any] | None = PIPE,
128
+ cwd: StrOrBytesPath | None = None,
129
+ env: Mapping[str, str] | None = None,
130
+ startupinfo: Any = None,
131
+ creationflags: int = 0,
132
+ start_new_session: bool = False,
133
+ pass_fds: Sequence[int] = (),
134
+ user: str | int | None = None,
135
+ group: str | int | None = None,
136
+ extra_groups: Iterable[str | int] | None = None,
137
+ umask: int = -1,
138
+ ) -> Process:
139
+ """
140
+ Start an external command in a subprocess.
141
+
142
+ .. seealso:: :class:`subprocess.Popen`
143
+
144
+ :param command: either a string to pass to the shell, or an iterable of strings
145
+ containing the executable name or path and its arguments
146
+ :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a
147
+ file-like object, or ``None``
148
+ :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
149
+ a file-like object, or ``None``
150
+ :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
151
+ :data:`subprocess.STDOUT`, a file-like object, or ``None``
152
+ :param cwd: If not ``None``, the working directory is changed before executing
153
+ :param env: If env is not ``None``, it must be a mapping that defines the
154
+ environment variables for the new process
155
+ :param creationflags: flags that can be used to control the creation of the
156
+ subprocess (see :class:`subprocess.Popen` for the specifics)
157
+ :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used
158
+ to specify process startup parameters (Windows only)
159
+ :param start_new_session: if ``true`` the setsid() system call will be made in the
160
+ child process prior to the execution of the subprocess. (POSIX only)
161
+ :param pass_fds: sequence of file descriptors to keep open between the parent and
162
+ child processes. (POSIX only)
163
+ :param user: effective user to run the process as (POSIX only)
164
+ :param group: effective group to run the process as (POSIX only)
165
+ :param extra_groups: supplementary groups to set in the subprocess (POSIX only)
166
+ :param umask: if not negative, this umask is applied in the child process before
167
+ running the given command (POSIX only)
168
+ :return: an asynchronous process object
169
+
170
+ """
171
+ kwargs: dict[str, Any] = {}
172
+ if user is not None:
173
+ kwargs["user"] = user
174
+
175
+ if group is not None:
176
+ kwargs["group"] = group
177
+
178
+ if extra_groups is not None:
179
+ kwargs["extra_groups"] = group
180
+
181
+ if umask >= 0:
182
+ kwargs["umask"] = umask
183
+
184
+ return await get_async_backend().open_process(
185
+ command,
186
+ stdin=stdin,
187
+ stdout=stdout,
188
+ stderr=stderr,
189
+ cwd=cwd,
190
+ env=env,
191
+ startupinfo=startupinfo,
192
+ creationflags=creationflags,
193
+ start_new_session=start_new_session,
194
+ pass_fds=pass_fds,
195
+ **kwargs,
196
+ )
.venv/Lib/site-packages/anyio/_core/_synchronization.py ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from collections import deque
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from types import TracebackType
8
+ from typing import TypeVar
9
+
10
+ from ..lowlevel import checkpoint_if_cancelled
11
+ from ._eventloop import get_async_backend
12
+ from ._exceptions import BusyResourceError, NoEventLoopError
13
+ from ._tasks import CancelScope
14
+ from ._testing import TaskInfo, get_current_task
15
+
16
+ T = TypeVar("T")
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class EventStatistics:
21
+ """
22
+ :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait`
23
+ """
24
+
25
+ tasks_waiting: int
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class CapacityLimiterStatistics:
30
+ """
31
+ :ivar int borrowed_tokens: number of tokens currently borrowed by tasks
32
+ :ivar float total_tokens: total number of available tokens
33
+ :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from
34
+ this limiter
35
+ :ivar int tasks_waiting: number of tasks waiting on
36
+ :meth:`~.CapacityLimiter.acquire` or
37
+ :meth:`~.CapacityLimiter.acquire_on_behalf_of`
38
+ """
39
+
40
+ borrowed_tokens: int
41
+ total_tokens: float
42
+ borrowers: tuple[object, ...]
43
+ tasks_waiting: int
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class LockStatistics:
48
+ """
49
+ :ivar bool locked: flag indicating if this lock is locked or not
50
+ :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the
51
+ lock is not held by any task)
52
+ :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire`
53
+ """
54
+
55
+ locked: bool
56
+ owner: TaskInfo | None
57
+ tasks_waiting: int
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class ConditionStatistics:
62
+ """
63
+ :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait`
64
+ :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying
65
+ :class:`~.Lock`
66
+ """
67
+
68
+ tasks_waiting: int
69
+ lock_statistics: LockStatistics
70
+
71
+
72
+ @dataclass(frozen=True)
73
+ class SemaphoreStatistics:
74
+ """
75
+ :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire`
76
+
77
+ """
78
+
79
+ tasks_waiting: int
80
+
81
+
82
+ class Event:
83
+ __slots__ = ("__weakref__",)
84
+
85
+ def __new__(cls) -> Event:
86
+ try:
87
+ return get_async_backend().create_event()
88
+ except NoEventLoopError:
89
+ return EventAdapter()
90
+
91
+ def set(self) -> None:
92
+ """Set the flag, notifying all listeners."""
93
+ raise NotImplementedError
94
+
95
+ def is_set(self) -> bool:
96
+ """Return ``True`` if the flag is set, ``False`` if not."""
97
+ raise NotImplementedError
98
+
99
+ async def wait(self) -> None:
100
+ """
101
+ Wait until the flag has been set.
102
+
103
+ If the flag has already been set when this method is called, it returns
104
+ immediately.
105
+
106
+ """
107
+ raise NotImplementedError
108
+
109
+ def statistics(self) -> EventStatistics:
110
+ """Return statistics about the current state of this event."""
111
+ raise NotImplementedError
112
+
113
+
114
+ class EventAdapter(Event):
115
+ __slots__ = "_internal_event", "_is_set"
116
+
117
+ def __new__(cls) -> EventAdapter:
118
+ return object.__new__(cls)
119
+
120
+ def __init__(self) -> None:
121
+ self._internal_event: Event | None = None
122
+ self._is_set = False
123
+
124
+ @property
125
+ def _event(self) -> Event:
126
+ if self._internal_event is None:
127
+ self._internal_event = get_async_backend().create_event()
128
+ if self._is_set:
129
+ self._internal_event.set()
130
+
131
+ return self._internal_event
132
+
133
+ def set(self) -> None:
134
+ if self._internal_event is None:
135
+ self._is_set = True
136
+ else:
137
+ self._event.set()
138
+
139
+ def is_set(self) -> bool:
140
+ if self._internal_event is None:
141
+ return self._is_set
142
+
143
+ return self._internal_event.is_set()
144
+
145
+ async def wait(self) -> None:
146
+ await self._event.wait()
147
+
148
+ def statistics(self) -> EventStatistics:
149
+ if self._internal_event is None:
150
+ return EventStatistics(tasks_waiting=0)
151
+
152
+ return self._internal_event.statistics()
153
+
154
+
155
+ class Lock:
156
+ __slots__ = ("__weakref__",)
157
+
158
+ def __new__(cls, *, fast_acquire: bool = False) -> Lock:
159
+ try:
160
+ return get_async_backend().create_lock(fast_acquire=fast_acquire)
161
+ except NoEventLoopError:
162
+ return LockAdapter(fast_acquire=fast_acquire)
163
+
164
+ async def __aenter__(self) -> None:
165
+ await self.acquire()
166
+
167
+ async def __aexit__(
168
+ self,
169
+ exc_type: type[BaseException] | None,
170
+ exc_val: BaseException | None,
171
+ exc_tb: TracebackType | None,
172
+ ) -> None:
173
+ self.release()
174
+
175
+ async def acquire(self) -> None:
176
+ """Acquire the lock."""
177
+ raise NotImplementedError
178
+
179
+ def acquire_nowait(self) -> None:
180
+ """
181
+ Acquire the lock, without blocking.
182
+
183
+ :raises ~anyio.WouldBlock: if the operation would block
184
+
185
+ """
186
+ raise NotImplementedError
187
+
188
+ def release(self) -> None:
189
+ """Release the lock."""
190
+ raise NotImplementedError
191
+
192
+ def locked(self) -> bool:
193
+ """Return True if the lock is currently held."""
194
+ raise NotImplementedError
195
+
196
+ def statistics(self) -> LockStatistics:
197
+ """
198
+ Return statistics about the current state of this lock.
199
+
200
+ .. versionadded:: 3.0
201
+ """
202
+ raise NotImplementedError
203
+
204
+
205
+ class LockAdapter(Lock):
206
+ __slots__ = "_internal_lock", "_fast_acquire"
207
+
208
+ def __new__(cls, *, fast_acquire: bool = False) -> LockAdapter:
209
+ return object.__new__(cls)
210
+
211
+ def __init__(self, *, fast_acquire: bool = False):
212
+ self._internal_lock: Lock | None = None
213
+ self._fast_acquire = fast_acquire
214
+
215
+ @property
216
+ def _lock(self) -> Lock:
217
+ if self._internal_lock is None:
218
+ self._internal_lock = get_async_backend().create_lock(
219
+ fast_acquire=self._fast_acquire
220
+ )
221
+
222
+ return self._internal_lock
223
+
224
+ async def __aenter__(self) -> None:
225
+ await self._lock.acquire()
226
+
227
+ async def __aexit__(
228
+ self,
229
+ exc_type: type[BaseException] | None,
230
+ exc_val: BaseException | None,
231
+ exc_tb: TracebackType | None,
232
+ ) -> None:
233
+ if self._internal_lock is not None:
234
+ self._internal_lock.release()
235
+
236
+ async def acquire(self) -> None:
237
+ """Acquire the lock."""
238
+ await self._lock.acquire()
239
+
240
+ def acquire_nowait(self) -> None:
241
+ """
242
+ Acquire the lock, without blocking.
243
+
244
+ :raises ~anyio.WouldBlock: if the operation would block
245
+
246
+ """
247
+ self._lock.acquire_nowait()
248
+
249
+ def release(self) -> None:
250
+ """Release the lock."""
251
+ self._lock.release()
252
+
253
+ def locked(self) -> bool:
254
+ """Return True if the lock is currently held."""
255
+ return self._lock.locked()
256
+
257
+ def statistics(self) -> LockStatistics:
258
+ """
259
+ Return statistics about the current state of this lock.
260
+
261
+ .. versionadded:: 3.0
262
+
263
+ """
264
+ if self._internal_lock is None:
265
+ return LockStatistics(False, None, 0)
266
+
267
+ return self._internal_lock.statistics()
268
+
269
+
270
+ class Condition:
271
+ __slots__ = "__weakref__", "_owner_task", "_lock", "_waiters"
272
+
273
+ def __init__(self, lock: Lock | None = None):
274
+ self._owner_task: TaskInfo | None = None
275
+ self._lock = lock or Lock()
276
+ self._waiters: deque[Event] = deque()
277
+
278
+ async def __aenter__(self) -> None:
279
+ await self.acquire()
280
+
281
+ async def __aexit__(
282
+ self,
283
+ exc_type: type[BaseException] | None,
284
+ exc_val: BaseException | None,
285
+ exc_tb: TracebackType | None,
286
+ ) -> None:
287
+ self.release()
288
+
289
+ def _check_acquired(self) -> None:
290
+ if self._owner_task != get_current_task():
291
+ raise RuntimeError("The current task is not holding the underlying lock")
292
+
293
+ async def acquire(self) -> None:
294
+ """Acquire the underlying lock."""
295
+ await self._lock.acquire()
296
+ self._owner_task = get_current_task()
297
+
298
+ def acquire_nowait(self) -> None:
299
+ """
300
+ Acquire the underlying lock, without blocking.
301
+
302
+ :raises ~anyio.WouldBlock: if the operation would block
303
+
304
+ """
305
+ self._lock.acquire_nowait()
306
+ self._owner_task = get_current_task()
307
+
308
+ def release(self) -> None:
309
+ """Release the underlying lock."""
310
+ self._lock.release()
311
+
312
+ def locked(self) -> bool:
313
+ """Return True if the lock is set."""
314
+ return self._lock.locked()
315
+
316
+ def notify(self, n: int = 1) -> None:
317
+ """Notify exactly n listeners."""
318
+ self._check_acquired()
319
+ for _ in range(n):
320
+ try:
321
+ event = self._waiters.popleft()
322
+ except IndexError:
323
+ break
324
+
325
+ event.set()
326
+
327
+ def notify_all(self) -> None:
328
+ """Notify all the listeners."""
329
+ self._check_acquired()
330
+ for event in self._waiters:
331
+ event.set()
332
+
333
+ self._waiters.clear()
334
+
335
+ async def wait(self) -> None:
336
+ """Wait for a notification."""
337
+ await checkpoint_if_cancelled()
338
+ self._check_acquired()
339
+ event = Event()
340
+ self._waiters.append(event)
341
+ self.release()
342
+ try:
343
+ await event.wait()
344
+ except BaseException:
345
+ if not event.is_set():
346
+ self._waiters.remove(event)
347
+ elif self._waiters:
348
+ # This task was notified by could not act on it, so pass
349
+ # it on to the next task
350
+ self._waiters.popleft().set()
351
+
352
+ raise
353
+ finally:
354
+ with CancelScope(shield=True):
355
+ await self.acquire()
356
+
357
+ async def wait_for(self, predicate: Callable[[], T]) -> T:
358
+ """
359
+ Wait until a predicate becomes true.
360
+
361
+ :param predicate: a callable that returns a truthy value when the condition is
362
+ met
363
+ :return: the result of the predicate
364
+
365
+ .. versionadded:: 4.11.0
366
+
367
+ """
368
+ while not (result := predicate()):
369
+ await self.wait()
370
+
371
+ return result
372
+
373
+ def statistics(self) -> ConditionStatistics:
374
+ """
375
+ Return statistics about the current state of this condition.
376
+
377
+ .. versionadded:: 3.0
378
+ """
379
+ return ConditionStatistics(len(self._waiters), self._lock.statistics())
380
+
381
+
382
+ class Semaphore:
383
+ __slots__ = "__weakref__", "_fast_acquire"
384
+
385
+ def __new__(
386
+ cls,
387
+ initial_value: int,
388
+ *,
389
+ max_value: int | None = None,
390
+ fast_acquire: bool = False,
391
+ ) -> Semaphore:
392
+ try:
393
+ return get_async_backend().create_semaphore(
394
+ initial_value, max_value=max_value, fast_acquire=fast_acquire
395
+ )
396
+ except NoEventLoopError:
397
+ return SemaphoreAdapter(initial_value, max_value=max_value)
398
+
399
+ def __init__(
400
+ self,
401
+ initial_value: int,
402
+ *,
403
+ max_value: int | None = None,
404
+ fast_acquire: bool = False,
405
+ ):
406
+ if not isinstance(initial_value, int):
407
+ raise TypeError("initial_value must be an integer")
408
+ if initial_value < 0:
409
+ raise ValueError("initial_value must be >= 0")
410
+ if max_value is not None:
411
+ if not isinstance(max_value, int):
412
+ raise TypeError("max_value must be an integer or None")
413
+ if max_value < initial_value:
414
+ raise ValueError(
415
+ "max_value must be equal to or higher than initial_value"
416
+ )
417
+
418
+ self._fast_acquire = fast_acquire
419
+
420
+ async def __aenter__(self) -> Semaphore:
421
+ await self.acquire()
422
+ return self
423
+
424
+ async def __aexit__(
425
+ self,
426
+ exc_type: type[BaseException] | None,
427
+ exc_val: BaseException | None,
428
+ exc_tb: TracebackType | None,
429
+ ) -> None:
430
+ self.release()
431
+
432
+ async def acquire(self) -> None:
433
+ """Decrement the semaphore value, blocking if necessary."""
434
+ raise NotImplementedError
435
+
436
+ def acquire_nowait(self) -> None:
437
+ """
438
+ Acquire the underlying lock, without blocking.
439
+
440
+ :raises ~anyio.WouldBlock: if the operation would block
441
+
442
+ """
443
+ raise NotImplementedError
444
+
445
+ def release(self) -> None:
446
+ """Increment the semaphore value."""
447
+ raise NotImplementedError
448
+
449
+ @property
450
+ def value(self) -> int:
451
+ """The current value of the semaphore."""
452
+ raise NotImplementedError
453
+
454
+ @property
455
+ def max_value(self) -> int | None:
456
+ """The maximum value of the semaphore."""
457
+ raise NotImplementedError
458
+
459
+ def statistics(self) -> SemaphoreStatistics:
460
+ """
461
+ Return statistics about the current state of this semaphore.
462
+
463
+ .. versionadded:: 3.0
464
+ """
465
+ raise NotImplementedError
466
+
467
+
468
+ class SemaphoreAdapter(Semaphore):
469
+ __slots__ = "_internal_semaphore", "_initial_value", "_max_value"
470
+
471
+ def __new__(
472
+ cls,
473
+ initial_value: int,
474
+ *,
475
+ max_value: int | None = None,
476
+ fast_acquire: bool = False,
477
+ ) -> SemaphoreAdapter:
478
+ return object.__new__(cls)
479
+
480
+ def __init__(
481
+ self,
482
+ initial_value: int,
483
+ *,
484
+ max_value: int | None = None,
485
+ fast_acquire: bool = False,
486
+ ) -> None:
487
+ super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire)
488
+ self._internal_semaphore: Semaphore | None = None
489
+ self._initial_value = initial_value
490
+ self._max_value = max_value
491
+
492
+ @property
493
+ def _semaphore(self) -> Semaphore:
494
+ if self._internal_semaphore is None:
495
+ self._internal_semaphore = get_async_backend().create_semaphore(
496
+ self._initial_value, max_value=self._max_value
497
+ )
498
+
499
+ return self._internal_semaphore
500
+
501
+ async def acquire(self) -> None:
502
+ await self._semaphore.acquire()
503
+
504
+ def acquire_nowait(self) -> None:
505
+ self._semaphore.acquire_nowait()
506
+
507
+ def release(self) -> None:
508
+ self._semaphore.release()
509
+
510
+ @property
511
+ def value(self) -> int:
512
+ if self._internal_semaphore is None:
513
+ return self._initial_value
514
+
515
+ return self._semaphore.value
516
+
517
+ @property
518
+ def max_value(self) -> int | None:
519
+ return self._max_value
520
+
521
+ def statistics(self) -> SemaphoreStatistics:
522
+ if self._internal_semaphore is None:
523
+ return SemaphoreStatistics(tasks_waiting=0)
524
+
525
+ return self._semaphore.statistics()
526
+
527
+
528
+ class CapacityLimiter:
529
+ __slots__ = ("__weakref__",)
530
+
531
+ def __new__(cls, total_tokens: float) -> CapacityLimiter:
532
+ try:
533
+ return get_async_backend().create_capacity_limiter(total_tokens)
534
+ except NoEventLoopError:
535
+ return CapacityLimiterAdapter(total_tokens)
536
+
537
+ async def __aenter__(self) -> None:
538
+ raise NotImplementedError
539
+
540
+ async def __aexit__(
541
+ self,
542
+ exc_type: type[BaseException] | None,
543
+ exc_val: BaseException | None,
544
+ exc_tb: TracebackType | None,
545
+ ) -> None:
546
+ raise NotImplementedError
547
+
548
+ @property
549
+ def total_tokens(self) -> float:
550
+ """
551
+ The total number of tokens available for borrowing.
552
+
553
+ This is a read-write property. If the total number of tokens is increased, the
554
+ proportionate number of tasks waiting on this limiter will be granted their
555
+ tokens.
556
+
557
+ .. versionchanged:: 3.0
558
+ The property is now writable.
559
+ .. versionchanged:: 4.12
560
+ The value can now be set to 0.
561
+
562
+ """
563
+ raise NotImplementedError
564
+
565
+ @total_tokens.setter
566
+ def total_tokens(self, value: float) -> None:
567
+ raise NotImplementedError
568
+
569
+ @property
570
+ def borrowed_tokens(self) -> int:
571
+ """The number of tokens that have currently been borrowed."""
572
+ raise NotImplementedError
573
+
574
+ @property
575
+ def available_tokens(self) -> float:
576
+ """The number of tokens currently available to be borrowed"""
577
+ raise NotImplementedError
578
+
579
+ def acquire_nowait(self) -> None:
580
+ """
581
+ Acquire a token for the current task without waiting for one to become
582
+ available.
583
+
584
+ :raises ~anyio.WouldBlock: if there are no tokens available for borrowing
585
+
586
+ """
587
+ raise NotImplementedError
588
+
589
+ def acquire_on_behalf_of_nowait(self, borrower: object) -> None:
590
+ """
591
+ Acquire a token without waiting for one to become available.
592
+
593
+ :param borrower: the entity borrowing a token
594
+ :raises ~anyio.WouldBlock: if there are no tokens available for borrowing
595
+
596
+ """
597
+ raise NotImplementedError
598
+
599
+ async def acquire(self) -> None:
600
+ """
601
+ Acquire a token for the current task, waiting if necessary for one to become
602
+ available.
603
+
604
+ """
605
+ raise NotImplementedError
606
+
607
+ async def acquire_on_behalf_of(self, borrower: object) -> None:
608
+ """
609
+ Acquire a token, waiting if necessary for one to become available.
610
+
611
+ :param borrower: the entity borrowing a token
612
+
613
+ """
614
+ raise NotImplementedError
615
+
616
+ def release(self) -> None:
617
+ """
618
+ Release the token held by the current task.
619
+
620
+ :raises RuntimeError: if the current task has not borrowed a token from this
621
+ limiter.
622
+
623
+ """
624
+ raise NotImplementedError
625
+
626
+ def release_on_behalf_of(self, borrower: object) -> None:
627
+ """
628
+ Release the token held by the given borrower.
629
+
630
+ :raises RuntimeError: if the borrower has not borrowed a token from this
631
+ limiter.
632
+
633
+ """
634
+ raise NotImplementedError
635
+
636
+ def statistics(self) -> CapacityLimiterStatistics:
637
+ """
638
+ Return statistics about the current state of this limiter.
639
+
640
+ .. versionadded:: 3.0
641
+
642
+ """
643
+ raise NotImplementedError
644
+
645
+
646
+ class CapacityLimiterAdapter(CapacityLimiter):
647
+ __slots__ = "_internal_limiter", "_total_tokens"
648
+
649
+ def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter:
650
+ return object.__new__(cls)
651
+
652
+ def __init__(self, total_tokens: float) -> None:
653
+ self._internal_limiter: CapacityLimiter | None = None
654
+ self.total_tokens = total_tokens
655
+
656
+ @property
657
+ def _limiter(self) -> CapacityLimiter:
658
+ if self._internal_limiter is None:
659
+ self._internal_limiter = get_async_backend().create_capacity_limiter(
660
+ self._total_tokens
661
+ )
662
+
663
+ return self._internal_limiter
664
+
665
+ async def __aenter__(self) -> None:
666
+ await self._limiter.__aenter__()
667
+
668
+ async def __aexit__(
669
+ self,
670
+ exc_type: type[BaseException] | None,
671
+ exc_val: BaseException | None,
672
+ exc_tb: TracebackType | None,
673
+ ) -> None:
674
+ return await self._limiter.__aexit__(exc_type, exc_val, exc_tb)
675
+
676
+ @property
677
+ def total_tokens(self) -> float:
678
+ if self._internal_limiter is None:
679
+ return self._total_tokens
680
+
681
+ return self._internal_limiter.total_tokens
682
+
683
+ @total_tokens.setter
684
+ def total_tokens(self, value: float) -> None:
685
+ if not isinstance(value, int) and value is not math.inf:
686
+ raise TypeError("total_tokens must be an int or math.inf")
687
+ elif value < 0:
688
+ raise ValueError("total_tokens must be >= 0")
689
+
690
+ if self._internal_limiter is None:
691
+ self._total_tokens = value
692
+ return
693
+
694
+ self._limiter.total_tokens = value
695
+
696
+ @property
697
+ def borrowed_tokens(self) -> int:
698
+ if self._internal_limiter is None:
699
+ return 0
700
+
701
+ return self._internal_limiter.borrowed_tokens
702
+
703
+ @property
704
+ def available_tokens(self) -> float:
705
+ if self._internal_limiter is None:
706
+ return self._total_tokens
707
+
708
+ return self._internal_limiter.available_tokens
709
+
710
+ def acquire_nowait(self) -> None:
711
+ self._limiter.acquire_nowait()
712
+
713
+ def acquire_on_behalf_of_nowait(self, borrower: object) -> None:
714
+ self._limiter.acquire_on_behalf_of_nowait(borrower)
715
+
716
+ async def acquire(self) -> None:
717
+ await self._limiter.acquire()
718
+
719
+ async def acquire_on_behalf_of(self, borrower: object) -> None:
720
+ await self._limiter.acquire_on_behalf_of(borrower)
721
+
722
+ def release(self) -> None:
723
+ self._limiter.release()
724
+
725
+ def release_on_behalf_of(self, borrower: object) -> None:
726
+ self._limiter.release_on_behalf_of(borrower)
727
+
728
+ def statistics(self) -> CapacityLimiterStatistics:
729
+ if self._internal_limiter is None:
730
+ return CapacityLimiterStatistics(
731
+ borrowed_tokens=0,
732
+ total_tokens=self.total_tokens,
733
+ borrowers=(),
734
+ tasks_waiting=0,
735
+ )
736
+
737
+ return self._internal_limiter.statistics()
738
+
739
+
740
+ class ResourceGuard:
741
+ """
742
+ A context manager for ensuring that a resource is only used by a single task at a
743
+ time.
744
+
745
+ Entering this context manager while the previous has not exited it yet will trigger
746
+ :exc:`BusyResourceError`.
747
+
748
+ :param action: the action to guard against (visible in the :exc:`BusyResourceError`
749
+ when triggered, e.g. "Another task is already {action} this resource")
750
+
751
+ .. versionadded:: 4.1
752
+ """
753
+
754
+ __slots__ = "__weakref__", "action", "_guarded"
755
+
756
+ def __init__(self, action: str = "using"):
757
+ self.action: str = action
758
+ self._guarded = False
759
+
760
+ def __enter__(self) -> None:
761
+ if self._guarded:
762
+ raise BusyResourceError(self.action)
763
+
764
+ self._guarded = True
765
+
766
+ def __exit__(
767
+ self,
768
+ exc_type: type[BaseException] | None,
769
+ exc_val: BaseException | None,
770
+ exc_tb: TracebackType | None,
771
+ ) -> None:
772
+ self._guarded = False
.venv/Lib/site-packages/anyio/_core/_tasks.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import sys
5
+ from collections.abc import (
6
+ Coroutine,
7
+ Generator,
8
+ )
9
+ from contextlib import (
10
+ contextmanager,
11
+ )
12
+ from contextvars import ContextVar
13
+ from enum import Enum, auto
14
+ from inspect import iscoroutine
15
+ from types import TracebackType
16
+ from typing import Any, Generic, final
17
+
18
+ from ..abc import TaskGroup, TaskStatus
19
+ from ._eventloop import get_async_backend, get_cancelled_exc_class
20
+ from ._exceptions import TaskCancelled, TaskFailed, TaskNotFinished
21
+
22
+ if sys.version_info >= (3, 13):
23
+ from typing import TypeVar
24
+ else:
25
+ from typing_extensions import TypeVar
26
+
27
+ if sys.version_info >= (3, 11):
28
+ from typing import Never, TypeVarTuple
29
+ else:
30
+ from typing_extensions import Never, TypeVarTuple
31
+
32
+ T = TypeVar("T")
33
+ T_co = TypeVar("T_co", covariant=True)
34
+ T_startval = TypeVar("T_startval", covariant=True, default=Never)
35
+ PosArgsT = TypeVarTuple("PosArgsT")
36
+
37
+ _current_task_handle: ContextVar[TaskHandle] = ContextVar("_current_task_handle")
38
+
39
+
40
+ class _IgnoredTaskStatus(TaskStatus[object]):
41
+ def started(self, value: object = None) -> None:
42
+ pass
43
+
44
+
45
+ TASK_STATUS_IGNORED = _IgnoredTaskStatus()
46
+
47
+
48
+ class CancelScope:
49
+ """
50
+ Wraps a unit of work that can be made separately cancellable.
51
+
52
+ :param deadline: The time (clock value) when this scope is cancelled automatically
53
+ :param shield: ``True`` to shield the cancel scope from external cancellation
54
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
55
+ current thread
56
+ """
57
+
58
+ __slots__ = ("__weakref__",)
59
+
60
+ def __new__(
61
+ cls, *, deadline: float = math.inf, shield: bool = False
62
+ ) -> CancelScope:
63
+ return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline)
64
+
65
+ def cancel(self, reason: str | None = None) -> None:
66
+ """
67
+ Cancel this scope immediately.
68
+
69
+ :param reason: a message describing the reason for the cancellation
70
+
71
+ """
72
+ raise NotImplementedError
73
+
74
+ @property
75
+ def deadline(self) -> float:
76
+ """
77
+ The time (clock value) when this scope is cancelled automatically.
78
+
79
+ Will be ``float('inf')`` if no timeout has been set.
80
+
81
+ """
82
+ raise NotImplementedError
83
+
84
+ @deadline.setter
85
+ def deadline(self, value: float) -> None:
86
+ raise NotImplementedError
87
+
88
+ @property
89
+ def cancel_called(self) -> bool:
90
+ """``True`` if :meth:`cancel` has been called."""
91
+ raise NotImplementedError
92
+
93
+ @property
94
+ def cancelled_caught(self) -> bool:
95
+ """
96
+ ``True`` if this scope suppressed a cancellation exception it itself raised.
97
+
98
+ This is typically used to check if any work was interrupted, or to see if the
99
+ scope was cancelled due to its deadline being reached. The value will, however,
100
+ only be ``True`` if the cancellation was triggered by the scope itself (and not
101
+ an outer scope).
102
+
103
+ """
104
+ raise NotImplementedError
105
+
106
+ @property
107
+ def shield(self) -> bool:
108
+ """
109
+ ``True`` if this scope is shielded from external cancellation.
110
+
111
+ While a scope is shielded, it will not receive cancellations from outside.
112
+
113
+ """
114
+ raise NotImplementedError
115
+
116
+ @shield.setter
117
+ def shield(self, value: bool) -> None:
118
+ raise NotImplementedError
119
+
120
+ def __enter__(self) -> CancelScope:
121
+ raise NotImplementedError
122
+
123
+ def __exit__(
124
+ self,
125
+ exc_type: type[BaseException] | None,
126
+ exc_val: BaseException | None,
127
+ exc_tb: TracebackType | None,
128
+ ) -> bool:
129
+ raise NotImplementedError
130
+
131
+
132
+ @contextmanager
133
+ def fail_after(
134
+ delay: float | None, shield: bool = False
135
+ ) -> Generator[CancelScope, None, None]:
136
+ """
137
+ Create a context manager which raises a :class:`TimeoutError` if does not finish in
138
+ time.
139
+
140
+ :param delay: maximum allowed time (in seconds) before raising the exception, or
141
+ ``None`` to disable the timeout
142
+ :param shield: ``True`` to shield the cancel scope from external cancellation
143
+ :return: a context manager that yields a cancel scope
144
+ :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\]
145
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
146
+ current thread
147
+
148
+ """
149
+ current_time = get_async_backend().current_time
150
+ deadline = (current_time() + delay) if delay is not None else math.inf
151
+ with get_async_backend().create_cancel_scope(
152
+ deadline=deadline, shield=shield
153
+ ) as cancel_scope:
154
+ yield cancel_scope
155
+
156
+ if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline:
157
+ raise TimeoutError
158
+
159
+
160
+ def move_on_after(delay: float | None, shield: bool = False) -> CancelScope:
161
+ """
162
+ Create a cancel scope with a deadline that expires after the given delay.
163
+
164
+ :param delay: maximum allowed time (in seconds) before exiting the context block, or
165
+ ``None`` to disable the timeout
166
+ :param shield: ``True`` to shield the cancel scope from external cancellation
167
+ :return: a cancel scope
168
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
169
+ current thread
170
+
171
+ """
172
+ deadline = (
173
+ (get_async_backend().current_time() + delay) if delay is not None else math.inf
174
+ )
175
+ return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield)
176
+
177
+
178
+ def current_effective_deadline() -> float:
179
+ """
180
+ Return the nearest deadline among all the cancel scopes effective for the current
181
+ task.
182
+
183
+ :return: a clock value from the event loop's internal clock (or ``float('inf')`` if
184
+ there is no deadline in effect, or ``float('-inf')`` if the current scope has
185
+ been cancelled)
186
+ :rtype: float
187
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
188
+ current thread
189
+
190
+ """
191
+ return get_async_backend().current_effective_deadline()
192
+
193
+
194
+ def create_task_group() -> TaskGroup:
195
+ """
196
+ Create a task group.
197
+
198
+ :return: a task group
199
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
200
+ current thread
201
+
202
+ """
203
+ return get_async_backend().create_task_group()
204
+
205
+
206
+ @final
207
+ class TaskHandle(Generic[T_co, T_startval]):
208
+ """
209
+ Returned from the task-spawning methods of :class:`TaskGroup`. Can be awaited on to
210
+ get the return value of the task (or the raised exception). If the task was
211
+ terminated by a :exc:`BaseException`, :exc:`TaskFailed` will be raised (or its
212
+ subclass :exc:`TaskCancelled` if the task was cancelled).
213
+
214
+ .. versionadded:: 4.14.0
215
+ """
216
+
217
+ class Status(Enum):
218
+ """
219
+ The status of a task handle.
220
+
221
+ .. attribute:: PENDING
222
+
223
+ The task has not finished yet.
224
+ .. attribute:: FINISHED
225
+
226
+ The task has finished with a return value.
227
+ .. attribute:: CANCELLING
228
+
229
+ The task has been cancelled but has not finished yet.
230
+ .. attribute:: CANCELLED
231
+
232
+ The task was cancelled and has finished since.
233
+ .. attribute:: FAILED
234
+
235
+ The task raised an exception.
236
+ """
237
+
238
+ PENDING = auto()
239
+ FINISHED = auto()
240
+ CANCELLING = auto()
241
+ CANCELLED = auto()
242
+ FAILED = auto()
243
+
244
+ __slots__ = (
245
+ "__weakref__",
246
+ "_coro",
247
+ "_name",
248
+ "_cancel_scope",
249
+ "_finished_event",
250
+ "_return_value",
251
+ "_start_value",
252
+ "_exception",
253
+ )
254
+
255
+ _return_value: T_co
256
+ _start_value: T_startval
257
+
258
+ def __init__(self, coro: Coroutine[Any, Any, T_co], name: object) -> None:
259
+ from ._synchronization import Event
260
+
261
+ self._coro = coro
262
+ self._cancel_scope = CancelScope()
263
+ self._finished_event = Event()
264
+ self._exception: BaseException | None = None
265
+
266
+ if name is not None:
267
+ self._name = str(name)
268
+ elif iscoroutine(coro):
269
+ self._name = coro.__qualname__
270
+ else:
271
+ self._name = str(coro) # coroutine-like object (e.g. asend() objects)
272
+
273
+ async def _run_coro(self) -> None:
274
+ __tracebackhide__ = True
275
+
276
+ with self._cancel_scope:
277
+ try:
278
+ retval = await self._coro
279
+ except BaseException as exc:
280
+ self._exception = exc
281
+ raise
282
+ else:
283
+ self._return_value = retval
284
+ finally:
285
+ self._finished_event.set()
286
+ del self # Break the reference cycle
287
+
288
+ def cancel(self) -> None:
289
+ """
290
+ Set the task to a cancelled state.
291
+
292
+ This will interrupt any interruptible asynchronous operation, and will cause
293
+ any further awaits on this task to get immediately cancelled, unless done in
294
+ a shielded cancel scope.
295
+
296
+ If the task has already finished, this method has no effect.
297
+ """
298
+ if not self._finished_event.is_set():
299
+ self._cancel_scope.cancel()
300
+
301
+ @property
302
+ def coro(self) -> Coroutine[Any, Any, T_co]:
303
+ """
304
+ The coroutine object that was passed to one of the task-spawning methods in
305
+ :class:`TaskGroup`.
306
+ """
307
+ return self._coro
308
+
309
+ @property
310
+ def status(self) -> TaskHandle.Status:
311
+ """
312
+ The current status of the task.
313
+
314
+ Every task starts in the :attr:`~TaskHandle.Status.PENDING` state.
315
+ If a task is cancelled while in this state, it will transition to the
316
+ :attr:`~TaskHandle.Status.CANCELLING` state. When the task finishes, it will
317
+ transition to one of the three final states (
318
+ :attr:`~TaskHandle.Status.FINISHED`, :attr:`~TaskHandle.Status.FAILED`, or
319
+ :attr:`~TaskHandle.Status.CANCELLING`) depending on the exception the task
320
+ raised, if any. No other status transitions will happen.
321
+ """
322
+ if not self._finished_event.is_set():
323
+ if self._cancel_scope.cancel_called:
324
+ return TaskHandle.Status.CANCELLING
325
+ else:
326
+ return TaskHandle.Status.PENDING
327
+ elif self._exception is not None:
328
+ if isinstance(self._exception, get_cancelled_exc_class()):
329
+ return TaskHandle.Status.CANCELLED
330
+ else:
331
+ return TaskHandle.Status.FAILED
332
+ else:
333
+ return TaskHandle.Status.FINISHED
334
+
335
+ @property
336
+ def name(self) -> str:
337
+ """The name of the task."""
338
+ return self._name
339
+
340
+ @property
341
+ def exception(self) -> BaseException | None:
342
+ """
343
+ The exception raised by the task, or ``None`` if it finished without raising.
344
+
345
+ :raises TaskNotFinished: if the task has not finished yet
346
+ :raises TaskCancelled: if the task was cancelled
347
+
348
+ """
349
+ match self.status:
350
+ case TaskHandle.Status.PENDING:
351
+ raise TaskNotFinished("the task has not finished yet")
352
+ case TaskHandle.Status.FINISHED:
353
+ return None
354
+ case TaskHandle.Status.CANCELLING:
355
+ raise TaskCancelled("the task was cancelled")
356
+ case TaskHandle.Status.CANCELLED:
357
+ raise TaskCancelled("the task was cancelled") from self._exception
358
+ case TaskHandle.Status.FAILED:
359
+ return self._exception
360
+
361
+ @property
362
+ def return_value(self) -> T_co:
363
+ """
364
+ The return value of the task.
365
+
366
+ :raises TaskNotFinished: if the task has not finished yet
367
+ :raises TaskCancelled: if the task was cancelled
368
+ :raises TaskFailed: if the task raised an exception
369
+
370
+ """
371
+ match self.status:
372
+ case TaskHandle.Status.PENDING:
373
+ raise TaskNotFinished("the task has not finished yet")
374
+ case TaskHandle.Status.FINISHED:
375
+ return self._return_value
376
+ case TaskHandle.Status.CANCELLING:
377
+ raise TaskCancelled("the task was cancelled")
378
+ case TaskHandle.Status.CANCELLED:
379
+ raise TaskCancelled("the task was cancelled") from self._exception
380
+ case TaskHandle.Status.FAILED:
381
+ raise TaskFailed("the task raised an exception") from self._exception
382
+
383
+ @property
384
+ def start_value(self) -> T_startval:
385
+ """
386
+ The value passed to :meth:`task_status.started() <.abc.TaskStatus.started>`,
387
+
388
+ :raises RuntimeError: if the task was not started with :meth:`TaskGroup.start()
389
+ <.abc.TaskGroup.start>`
390
+ """
391
+ try:
392
+ return self._start_value
393
+ except AttributeError:
394
+ raise RuntimeError(
395
+ "the task was not started with TaskGroup.start()"
396
+ ) from None
397
+
398
+ async def wait(self) -> None:
399
+ """
400
+ Wait for the task to finish.
401
+
402
+ This method will return as soon as the task has finished, no matter how it
403
+ happened.
404
+ """
405
+ await self._finished_event.wait()
406
+
407
+ def __await__(self) -> Generator[Any, Any, T_co]:
408
+ yield from self._finished_event.wait().__await__()
409
+ return self.return_value
410
+
411
+ def __repr__(self) -> str:
412
+ return (
413
+ f"<{self.__class__.__name__} {self.status.name.lower()} "
414
+ f"name={self._name!r} coro={self._coro!r}>"
415
+ )
.venv/Lib/site-packages/anyio/_core/_tempfile.py ADDED
@@ -0,0 +1,613 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ import tempfile
6
+ from collections.abc import Iterable
7
+ from io import BytesIO, TextIOWrapper
8
+ from types import TracebackType
9
+ from typing import (
10
+ TYPE_CHECKING,
11
+ Any,
12
+ AnyStr,
13
+ Generic,
14
+ overload,
15
+ )
16
+
17
+ from .. import to_thread
18
+ from .._core._fileio import AsyncFile
19
+ from ..lowlevel import checkpoint_if_cancelled
20
+
21
+ if TYPE_CHECKING:
22
+ from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
23
+
24
+
25
+ class TemporaryFile(Generic[AnyStr]):
26
+ """
27
+ An asynchronous temporary file that is automatically created and cleaned up.
28
+
29
+ This class provides an asynchronous context manager interface to a temporary file.
30
+ The file is created using Python's standard `tempfile.TemporaryFile` function in a
31
+ background thread, and is wrapped as an asynchronous file using `AsyncFile`.
32
+
33
+ :param mode: The mode in which the file is opened. Defaults to "w+b".
34
+ :param buffering: The buffering policy (-1 means the default buffering).
35
+ :param encoding: The encoding used to decode or encode the file. Only applicable in
36
+ text mode.
37
+ :param newline: Controls how universal newlines mode works (only applicable in text
38
+ mode).
39
+ :param suffix: The suffix for the temporary file name.
40
+ :param prefix: The prefix for the temporary file name.
41
+ :param dir: The directory in which the temporary file is created.
42
+ :param errors: The error handling scheme used for encoding/decoding errors.
43
+ """
44
+
45
+ _async_file: AsyncFile[AnyStr]
46
+
47
+ @overload
48
+ def __init__(
49
+ self: TemporaryFile[bytes],
50
+ mode: OpenBinaryMode = ...,
51
+ buffering: int = ...,
52
+ encoding: str | None = ...,
53
+ newline: str | None = ...,
54
+ suffix: str | None = ...,
55
+ prefix: str | None = ...,
56
+ dir: str | None = ...,
57
+ *,
58
+ errors: str | None = ...,
59
+ ): ...
60
+ @overload
61
+ def __init__(
62
+ self: TemporaryFile[str],
63
+ mode: OpenTextMode,
64
+ buffering: int = ...,
65
+ encoding: str | None = ...,
66
+ newline: str | None = ...,
67
+ suffix: str | None = ...,
68
+ prefix: str | None = ...,
69
+ dir: str | None = ...,
70
+ *,
71
+ errors: str | None = ...,
72
+ ): ...
73
+
74
+ def __init__(
75
+ self,
76
+ mode: OpenTextMode | OpenBinaryMode = "w+b",
77
+ buffering: int = -1,
78
+ encoding: str | None = None,
79
+ newline: str | None = None,
80
+ suffix: str | None = None,
81
+ prefix: str | None = None,
82
+ dir: str | None = None,
83
+ *,
84
+ errors: str | None = None,
85
+ ) -> None:
86
+ self.mode = mode
87
+ self.buffering = buffering
88
+ self.encoding = encoding
89
+ self.newline = newline
90
+ self.suffix: str | None = suffix
91
+ self.prefix: str | None = prefix
92
+ self.dir: str | None = dir
93
+ self.errors = errors
94
+
95
+ async def __aenter__(self) -> AsyncFile[AnyStr]:
96
+ fp = await to_thread.run_sync(
97
+ lambda: tempfile.TemporaryFile(
98
+ self.mode,
99
+ self.buffering,
100
+ self.encoding,
101
+ self.newline,
102
+ self.suffix,
103
+ self.prefix,
104
+ self.dir,
105
+ errors=self.errors,
106
+ )
107
+ )
108
+ self._async_file = AsyncFile(fp)
109
+ return self._async_file
110
+
111
+ async def __aexit__(
112
+ self,
113
+ exc_type: type[BaseException] | None,
114
+ exc_value: BaseException | None,
115
+ traceback: TracebackType | None,
116
+ ) -> None:
117
+ await self._async_file.aclose()
118
+
119
+
120
+ class NamedTemporaryFile(Generic[AnyStr]):
121
+ """
122
+ An asynchronous named temporary file that is automatically created and cleaned up.
123
+
124
+ This class provides an asynchronous context manager for a temporary file with a
125
+ visible name in the file system. It uses Python's standard
126
+ :func:`~tempfile.NamedTemporaryFile` function and wraps the file object with
127
+ :class:`AsyncFile` for asynchronous operations.
128
+
129
+ :param mode: The mode in which the file is opened. Defaults to "w+b".
130
+ :param buffering: The buffering policy (-1 means the default buffering).
131
+ :param encoding: The encoding used to decode or encode the file. Only applicable in
132
+ text mode.
133
+ :param newline: Controls how universal newlines mode works (only applicable in text
134
+ mode).
135
+ :param suffix: The suffix for the temporary file name.
136
+ :param prefix: The prefix for the temporary file name.
137
+ :param dir: The directory in which the temporary file is created.
138
+ :param delete: Whether to delete the file when it is closed.
139
+ :param errors: The error handling scheme used for encoding/decoding errors.
140
+ :param delete_on_close: (Python 3.12+) Whether to delete the file on close.
141
+ """
142
+
143
+ _async_file: AsyncFile[AnyStr]
144
+
145
+ @overload
146
+ def __init__(
147
+ self: NamedTemporaryFile[bytes],
148
+ mode: OpenBinaryMode = ...,
149
+ buffering: int = ...,
150
+ encoding: str | None = ...,
151
+ newline: str | None = ...,
152
+ suffix: str | None = ...,
153
+ prefix: str | None = ...,
154
+ dir: str | None = ...,
155
+ delete: bool = ...,
156
+ *,
157
+ errors: str | None = ...,
158
+ delete_on_close: bool = ...,
159
+ ): ...
160
+ @overload
161
+ def __init__(
162
+ self: NamedTemporaryFile[str],
163
+ mode: OpenTextMode,
164
+ buffering: int = ...,
165
+ encoding: str | None = ...,
166
+ newline: str | None = ...,
167
+ suffix: str | None = ...,
168
+ prefix: str | None = ...,
169
+ dir: str | None = ...,
170
+ delete: bool = ...,
171
+ *,
172
+ errors: str | None = ...,
173
+ delete_on_close: bool = ...,
174
+ ): ...
175
+
176
+ def __init__(
177
+ self,
178
+ mode: OpenBinaryMode | OpenTextMode = "w+b",
179
+ buffering: int = -1,
180
+ encoding: str | None = None,
181
+ newline: str | None = None,
182
+ suffix: str | None = None,
183
+ prefix: str | None = None,
184
+ dir: str | None = None,
185
+ delete: bool = True,
186
+ *,
187
+ errors: str | None = None,
188
+ delete_on_close: bool = True,
189
+ ) -> None:
190
+ self._params: dict[str, Any] = {
191
+ "mode": mode,
192
+ "buffering": buffering,
193
+ "encoding": encoding,
194
+ "newline": newline,
195
+ "suffix": suffix,
196
+ "prefix": prefix,
197
+ "dir": dir,
198
+ "delete": delete,
199
+ "errors": errors,
200
+ }
201
+ if sys.version_info >= (3, 12):
202
+ self._params["delete_on_close"] = delete_on_close
203
+
204
+ async def __aenter__(self) -> AsyncFile[AnyStr]:
205
+ fp = await to_thread.run_sync(
206
+ lambda: tempfile.NamedTemporaryFile(**self._params)
207
+ )
208
+ self._async_file = AsyncFile(fp)
209
+ return self._async_file
210
+
211
+ async def __aexit__(
212
+ self,
213
+ exc_type: type[BaseException] | None,
214
+ exc_value: BaseException | None,
215
+ traceback: TracebackType | None,
216
+ ) -> None:
217
+ await self._async_file.aclose()
218
+
219
+
220
+ class SpooledTemporaryFile(AsyncFile[AnyStr]):
221
+ """
222
+ An asynchronous spooled temporary file that starts in memory and is spooled to disk.
223
+
224
+ This class provides an asynchronous interface to a spooled temporary file, much like
225
+ Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous
226
+ write operations and provides a method to force a rollover to disk.
227
+
228
+ :param max_size: Maximum size in bytes before the file is rolled over to disk.
229
+ :param mode: The mode in which the file is opened. Defaults to "w+b".
230
+ :param buffering: The buffering policy (-1 means the default buffering).
231
+ :param encoding: The encoding used to decode or encode the file (text mode only).
232
+ :param newline: Controls how universal newlines mode works (text mode only).
233
+ :param suffix: The suffix for the temporary file name.
234
+ :param prefix: The prefix for the temporary file name.
235
+ :param dir: The directory in which the temporary file is created.
236
+ :param errors: The error handling scheme used for encoding/decoding errors.
237
+ """
238
+
239
+ _rolled: bool = False
240
+
241
+ @overload
242
+ def __init__(
243
+ self: SpooledTemporaryFile[bytes],
244
+ max_size: int = ...,
245
+ mode: OpenBinaryMode = ...,
246
+ buffering: int = ...,
247
+ encoding: str | None = ...,
248
+ newline: str | None = ...,
249
+ suffix: str | None = ...,
250
+ prefix: str | None = ...,
251
+ dir: str | None = ...,
252
+ *,
253
+ errors: str | None = ...,
254
+ ): ...
255
+ @overload
256
+ def __init__(
257
+ self: SpooledTemporaryFile[str],
258
+ max_size: int = ...,
259
+ mode: OpenTextMode = ...,
260
+ buffering: int = ...,
261
+ encoding: str | None = ...,
262
+ newline: str | None = ...,
263
+ suffix: str | None = ...,
264
+ prefix: str | None = ...,
265
+ dir: str | None = ...,
266
+ *,
267
+ errors: str | None = ...,
268
+ ): ...
269
+
270
+ def __init__(
271
+ self,
272
+ max_size: int = 0,
273
+ mode: OpenBinaryMode | OpenTextMode = "w+b",
274
+ buffering: int = -1,
275
+ encoding: str | None = None,
276
+ newline: str | None = None,
277
+ suffix: str | None = None,
278
+ prefix: str | None = None,
279
+ dir: str | None = None,
280
+ *,
281
+ errors: str | None = None,
282
+ ) -> None:
283
+ self._tempfile_params: dict[str, Any] = {
284
+ "mode": mode,
285
+ "buffering": buffering,
286
+ "encoding": encoding,
287
+ "newline": newline,
288
+ "suffix": suffix,
289
+ "prefix": prefix,
290
+ "dir": dir,
291
+ "errors": errors,
292
+ }
293
+ self._max_size = max_size
294
+ if "b" in mode:
295
+ super().__init__(BytesIO()) # type: ignore[arg-type]
296
+ else:
297
+ super().__init__(
298
+ TextIOWrapper( # type: ignore[arg-type]
299
+ BytesIO(),
300
+ encoding=encoding,
301
+ errors=errors,
302
+ newline=newline,
303
+ write_through=True,
304
+ )
305
+ )
306
+
307
+ async def aclose(self) -> None:
308
+ if not self._rolled:
309
+ self._fp.close()
310
+ return
311
+
312
+ await super().aclose()
313
+
314
+ async def _check(self) -> None:
315
+ if self._rolled or self._fp.tell() <= self._max_size:
316
+ return
317
+
318
+ await self.rollover()
319
+
320
+ async def rollover(self) -> None:
321
+ if self._rolled:
322
+ return
323
+
324
+ self._rolled = True
325
+ buffer = self._fp
326
+ buffer.seek(0)
327
+ self._fp = await to_thread.run_sync(
328
+ lambda: tempfile.TemporaryFile(**self._tempfile_params)
329
+ )
330
+ await self.write(buffer.read())
331
+ buffer.close()
332
+
333
+ @property
334
+ def closed(self) -> bool:
335
+ return self._fp.closed
336
+
337
+ async def read(self, size: int = -1) -> AnyStr:
338
+ if not self._rolled:
339
+ await checkpoint_if_cancelled()
340
+ return self._fp.read(size)
341
+
342
+ return await super().read(size) # type: ignore[return-value]
343
+
344
+ async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes:
345
+ if not self._rolled:
346
+ await checkpoint_if_cancelled()
347
+ return self._fp.read1(size)
348
+
349
+ return await super().read1(size)
350
+
351
+ async def readline(self) -> AnyStr:
352
+ if not self._rolled:
353
+ await checkpoint_if_cancelled()
354
+ return self._fp.readline()
355
+
356
+ return await super().readline() # type: ignore[return-value]
357
+
358
+ async def readlines(self) -> list[AnyStr]:
359
+ if not self._rolled:
360
+ await checkpoint_if_cancelled()
361
+ return self._fp.readlines()
362
+
363
+ return await super().readlines() # type: ignore[return-value]
364
+
365
+ async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int:
366
+ if not self._rolled:
367
+ await checkpoint_if_cancelled()
368
+ self._fp.readinto(b)
369
+
370
+ return await super().readinto(b)
371
+
372
+ async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int:
373
+ if not self._rolled:
374
+ await checkpoint_if_cancelled()
375
+ self._fp.readinto(b)
376
+
377
+ return await super().readinto1(b)
378
+
379
+ async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int:
380
+ if not self._rolled:
381
+ await checkpoint_if_cancelled()
382
+ return self._fp.seek(offset, whence)
383
+
384
+ return await super().seek(offset, whence)
385
+
386
+ async def tell(self) -> int:
387
+ if not self._rolled:
388
+ await checkpoint_if_cancelled()
389
+ return self._fp.tell()
390
+
391
+ return await super().tell()
392
+
393
+ async def truncate(self, size: int | None = None) -> int:
394
+ if not self._rolled:
395
+ await checkpoint_if_cancelled()
396
+ return self._fp.truncate(size)
397
+
398
+ return await super().truncate(size)
399
+
400
+ @overload
401
+ async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ...
402
+ @overload
403
+ async def write(self: SpooledTemporaryFile[str], b: str) -> int: ...
404
+
405
+ async def write(self, b: ReadableBuffer | str) -> int:
406
+ """
407
+ Asynchronously write data to the spooled temporary file.
408
+
409
+ If the file has not yet been rolled over, the data is written synchronously,
410
+ and a rollover is triggered if the size exceeds the maximum size.
411
+
412
+ :param s: The data to write.
413
+ :return: The number of bytes written.
414
+ :raises RuntimeError: If the underlying file is not initialized.
415
+
416
+ """
417
+ if not self._rolled:
418
+ await checkpoint_if_cancelled()
419
+ result = self._fp.write(b)
420
+ await self._check()
421
+ return result
422
+
423
+ return await super().write(b) # type: ignore[misc]
424
+
425
+ @overload
426
+ async def writelines(
427
+ self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer]
428
+ ) -> None: ...
429
+ @overload
430
+ async def writelines(
431
+ self: SpooledTemporaryFile[str], lines: Iterable[str]
432
+ ) -> None: ...
433
+
434
+ async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None:
435
+ """
436
+ Asynchronously write a list of lines to the spooled temporary file.
437
+
438
+ If the file has not yet been rolled over, the lines are written synchronously,
439
+ and a rollover is triggered if the size exceeds the maximum size.
440
+
441
+ :param lines: An iterable of lines to write.
442
+ :raises RuntimeError: If the underlying file is not initialized.
443
+
444
+ """
445
+ if not self._rolled:
446
+ await checkpoint_if_cancelled()
447
+ result = self._fp.writelines(lines)
448
+ await self._check()
449
+ return result
450
+
451
+ return await super().writelines(lines) # type: ignore[misc]
452
+
453
+
454
+ class TemporaryDirectory(Generic[AnyStr]):
455
+ """
456
+ An asynchronous temporary directory that is created and cleaned up automatically.
457
+
458
+ This class provides an asynchronous context manager for creating a temporary
459
+ directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to
460
+ perform directory creation and cleanup operations in a background thread.
461
+
462
+ :param suffix: Suffix to be added to the temporary directory name.
463
+ :param prefix: Prefix to be added to the temporary directory name.
464
+ :param dir: The parent directory where the temporary directory is created.
465
+ :param ignore_cleanup_errors: Whether to ignore errors during cleanup
466
+ :param delete: Whether to delete the directory upon closing (Python 3.12+).
467
+ """
468
+
469
+ def __init__(
470
+ self,
471
+ suffix: AnyStr | None = None,
472
+ prefix: AnyStr | None = None,
473
+ dir: AnyStr | None = None,
474
+ *,
475
+ ignore_cleanup_errors: bool = False,
476
+ delete: bool = True,
477
+ ) -> None:
478
+ self.suffix: AnyStr | None = suffix
479
+ self.prefix: AnyStr | None = prefix
480
+ self.dir: AnyStr | None = dir
481
+ self.ignore_cleanup_errors = ignore_cleanup_errors
482
+ self.delete = delete
483
+
484
+ self._tempdir: tempfile.TemporaryDirectory | None = None
485
+
486
+ async def __aenter__(self) -> str:
487
+ params: dict[str, Any] = {
488
+ "suffix": self.suffix,
489
+ "prefix": self.prefix,
490
+ "dir": self.dir,
491
+ "ignore_cleanup_errors": self.ignore_cleanup_errors,
492
+ }
493
+ if sys.version_info >= (3, 12):
494
+ params["delete"] = self.delete
495
+
496
+ self._tempdir = await to_thread.run_sync(
497
+ lambda: tempfile.TemporaryDirectory(**params)
498
+ )
499
+ return await to_thread.run_sync(self._tempdir.__enter__)
500
+
501
+ async def __aexit__(
502
+ self,
503
+ exc_type: type[BaseException] | None,
504
+ exc_value: BaseException | None,
505
+ traceback: TracebackType | None,
506
+ ) -> None:
507
+ if self._tempdir is not None:
508
+ await to_thread.run_sync(
509
+ self._tempdir.__exit__, exc_type, exc_value, traceback
510
+ )
511
+
512
+ async def cleanup(self) -> None:
513
+ if self._tempdir is not None:
514
+ await to_thread.run_sync(self._tempdir.cleanup)
515
+
516
+
517
+ @overload
518
+ async def mkstemp(
519
+ suffix: str | None = None,
520
+ prefix: str | None = None,
521
+ dir: str | None = None,
522
+ text: bool = False,
523
+ ) -> tuple[int, str]: ...
524
+
525
+
526
+ @overload
527
+ async def mkstemp(
528
+ suffix: bytes | None = None,
529
+ prefix: bytes | None = None,
530
+ dir: bytes | None = None,
531
+ text: bool = False,
532
+ ) -> tuple[int, bytes]: ...
533
+
534
+
535
+ async def mkstemp(
536
+ suffix: AnyStr | None = None,
537
+ prefix: AnyStr | None = None,
538
+ dir: AnyStr | None = None,
539
+ text: bool = False,
540
+ ) -> tuple[int, str | bytes]:
541
+ """
542
+ Asynchronously create a temporary file and return an OS-level handle and the file
543
+ name.
544
+
545
+ This function wraps `tempfile.mkstemp` and executes it in a background thread.
546
+
547
+ :param suffix: Suffix to be added to the file name.
548
+ :param prefix: Prefix to be added to the file name.
549
+ :param dir: Directory in which the temporary file is created.
550
+ :param text: Whether the file is opened in text mode.
551
+ :return: A tuple containing the file descriptor and the file name.
552
+
553
+ """
554
+ return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text)
555
+
556
+
557
+ @overload
558
+ async def mkdtemp(
559
+ suffix: str | None = None,
560
+ prefix: str | None = None,
561
+ dir: str | None = None,
562
+ ) -> str: ...
563
+
564
+
565
+ @overload
566
+ async def mkdtemp(
567
+ suffix: bytes | None = None,
568
+ prefix: bytes | None = None,
569
+ dir: bytes | None = None,
570
+ ) -> bytes: ...
571
+
572
+
573
+ async def mkdtemp(
574
+ suffix: AnyStr | None = None,
575
+ prefix: AnyStr | None = None,
576
+ dir: AnyStr | None = None,
577
+ ) -> str | bytes:
578
+ """
579
+ Asynchronously create a temporary directory and return its path.
580
+
581
+ This function wraps `tempfile.mkdtemp` and executes it in a background thread.
582
+
583
+ :param suffix: Suffix to be added to the directory name.
584
+ :param prefix: Prefix to be added to the directory name.
585
+ :param dir: Parent directory where the temporary directory is created.
586
+ :return: The path of the created temporary directory.
587
+
588
+ """
589
+ return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir)
590
+
591
+
592
+ async def gettempdir() -> str:
593
+ """
594
+ Asynchronously return the name of the directory used for temporary files.
595
+
596
+ This function wraps `tempfile.gettempdir` and executes it in a background thread.
597
+
598
+ :return: The path of the temporary directory as a string.
599
+
600
+ """
601
+ return await to_thread.run_sync(tempfile.gettempdir)
602
+
603
+
604
+ async def gettempdirb() -> bytes:
605
+ """
606
+ Asynchronously return the name of the directory used for temporary files in bytes.
607
+
608
+ This function wraps `tempfile.gettempdirb` and executes it in a background thread.
609
+
610
+ :return: The path of the temporary directory as bytes.
611
+
612
+ """
613
+ return await to_thread.run_sync(tempfile.gettempdirb)
.venv/Lib/site-packages/anyio/_core/_testing.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Generator
4
+ from typing import Any, cast
5
+
6
+ from ._eventloop import get_async_backend
7
+
8
+
9
+ class TaskInfo:
10
+ """
11
+ Represents an asynchronous task.
12
+
13
+ :ivar int id: the unique identifier of the task
14
+ :ivar parent_id: the identifier of the parent task, if any
15
+ :vartype parent_id: Optional[int]
16
+ :ivar str name: the description of the task (if any)
17
+ :ivar ~collections.abc.Coroutine coro: the coroutine object of the task
18
+ """
19
+
20
+ __slots__ = "_name", "id", "parent_id", "name", "coro"
21
+
22
+ def __init__(
23
+ self,
24
+ id: int,
25
+ parent_id: int | None,
26
+ name: str | None,
27
+ coro: Generator[Any, Any, Any] | Awaitable[Any],
28
+ ):
29
+ func = get_current_task
30
+ self._name = f"{func.__module__}.{func.__qualname__}"
31
+ self.id: int = id
32
+ self.parent_id: int | None = parent_id
33
+ self.name: str | None = name
34
+ self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro
35
+
36
+ def __eq__(self, other: object) -> bool:
37
+ if isinstance(other, TaskInfo):
38
+ return self.id == other.id
39
+
40
+ return NotImplemented
41
+
42
+ def __hash__(self) -> int:
43
+ return hash(self.id)
44
+
45
+ def __repr__(self) -> str:
46
+ return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})"
47
+
48
+ def has_pending_cancellation(self) -> bool:
49
+ """
50
+ Return ``True`` if the task has a cancellation pending, ``False`` otherwise.
51
+
52
+ """
53
+ return False
54
+
55
+
56
+ def get_current_task() -> TaskInfo:
57
+ """
58
+ Return the current task.
59
+
60
+ :return: a representation of the current task
61
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
62
+ current thread
63
+
64
+ """
65
+ return get_async_backend().get_current_task()
66
+
67
+
68
+ def get_running_tasks() -> list[TaskInfo]:
69
+ """
70
+ Return a list of running tasks in the current event loop.
71
+
72
+ :return: a list of task info objects
73
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
74
+ current thread
75
+
76
+ """
77
+ return cast("list[TaskInfo]", get_async_backend().get_running_tasks())
78
+
79
+
80
+ async def wait_all_tasks_blocked() -> None:
81
+ """Wait until all other tasks are waiting for something."""
82
+ await get_async_backend().wait_all_tasks_blocked()
.venv/Lib/site-packages/anyio/_core/_typedattr.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Mapping
4
+ from typing import Any, TypeVar, final, overload
5
+
6
+ from ._exceptions import TypedAttributeLookupError
7
+
8
+ T_Attr = TypeVar("T_Attr")
9
+ T_Default = TypeVar("T_Default")
10
+ undefined = object()
11
+
12
+
13
+ def typed_attribute() -> Any:
14
+ """Return a unique object, used to mark typed attributes."""
15
+ return object()
16
+
17
+
18
+ class TypedAttributeSet:
19
+ """
20
+ Superclass for typed attribute collections.
21
+
22
+ Checks that every public attribute of every subclass has a type annotation.
23
+ """
24
+
25
+ def __init_subclass__(cls) -> None:
26
+ annotations: dict[str, Any] = getattr(cls, "__annotations__", {})
27
+ for attrname in dir(cls):
28
+ if not attrname.startswith("_") and attrname not in annotations:
29
+ raise TypeError(
30
+ f"Attribute {attrname!r} is missing its type annotation"
31
+ )
32
+
33
+ super().__init_subclass__()
34
+
35
+
36
+ class TypedAttributeProvider:
37
+ """Base class for classes that wish to provide typed extra attributes."""
38
+
39
+ @property
40
+ def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]:
41
+ """
42
+ A mapping of the extra attributes to callables that return the corresponding
43
+ values.
44
+
45
+ If the provider wraps another provider, the attributes from that wrapper should
46
+ also be included in the returned mapping (but the wrapper may override the
47
+ callables from the wrapped instance).
48
+
49
+ """
50
+ return {}
51
+
52
+ @overload
53
+ def extra(self, attribute: T_Attr) -> T_Attr: ...
54
+
55
+ @overload
56
+ def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ...
57
+
58
+ @final
59
+ def extra(self, attribute: Any, default: object = undefined) -> object:
60
+ """
61
+ extra(attribute, default=undefined)
62
+
63
+ Return the value of the given typed extra attribute.
64
+
65
+ :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to
66
+ look for
67
+ :param default: the value that should be returned if no value is found for the
68
+ attribute
69
+ :raises ~anyio.TypedAttributeLookupError: if the search failed and no default
70
+ value was given
71
+
72
+ """
73
+ try:
74
+ getter = self.extra_attributes[attribute]
75
+ except KeyError:
76
+ if default is undefined:
77
+ raise TypedAttributeLookupError("Attribute not found") from None
78
+ else:
79
+ return default
80
+
81
+ return getter()
.venv/Lib/site-packages/anyio/abc/__init__.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from ._eventloop import AsyncBackend as AsyncBackend
4
+ from ._resources import AsyncResource as AsyncResource
5
+ from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket
6
+ from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket
7
+ from ._sockets import IPAddressType as IPAddressType
8
+ from ._sockets import IPSockAddrType as IPSockAddrType
9
+ from ._sockets import SocketAttribute as SocketAttribute
10
+ from ._sockets import SocketListener as SocketListener
11
+ from ._sockets import SocketStream as SocketStream
12
+ from ._sockets import UDPPacketType as UDPPacketType
13
+ from ._sockets import UDPSocket as UDPSocket
14
+ from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType
15
+ from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket
16
+ from ._sockets import UNIXSocketStream as UNIXSocketStream
17
+ from ._streams import AnyByteReceiveStream as AnyByteReceiveStream
18
+ from ._streams import AnyByteSendStream as AnyByteSendStream
19
+ from ._streams import AnyByteStream as AnyByteStream
20
+ from ._streams import AnyByteStreamConnectable as AnyByteStreamConnectable
21
+ from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream
22
+ from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream
23
+ from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream
24
+ from ._streams import ByteReceiveStream as ByteReceiveStream
25
+ from ._streams import ByteSendStream as ByteSendStream
26
+ from ._streams import ByteStream as ByteStream
27
+ from ._streams import ByteStreamConnectable as ByteStreamConnectable
28
+ from ._streams import Listener as Listener
29
+ from ._streams import ObjectReceiveStream as ObjectReceiveStream
30
+ from ._streams import ObjectSendStream as ObjectSendStream
31
+ from ._streams import ObjectStream as ObjectStream
32
+ from ._streams import ObjectStreamConnectable as ObjectStreamConnectable
33
+ from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream
34
+ from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream
35
+ from ._streams import UnreliableObjectStream as UnreliableObjectStream
36
+ from ._subprocesses import Process as Process
37
+ from ._tasks import TaskGroup as TaskGroup
38
+ from ._tasks import TaskStatus as TaskStatus
39
+ from ._testing import TestRunner as TestRunner
40
+
41
+ # Re-exported here, for backwards compatibility
42
+ # isort: off
43
+ from .._core._synchronization import (
44
+ CapacityLimiter as CapacityLimiter,
45
+ Condition as Condition,
46
+ Event as Event,
47
+ Lock as Lock,
48
+ Semaphore as Semaphore,
49
+ )
50
+ from .._core._tasks import CancelScope as CancelScope
51
+ from ..from_thread import BlockingPortal as BlockingPortal
52
+
53
+ # Re-export imports so they look like they live directly in this package
54
+ for __value in list(locals().values()):
55
+ if getattr(__value, "__module__", "").startswith("anyio.abc."):
56
+ __value.__module__ = __name__
57
+
58
+ del __value
.venv/Lib/site-packages/anyio/abc/_eventloop.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import sys
5
+ from abc import ABCMeta, abstractmethod
6
+ from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Sequence
7
+ from contextlib import AbstractContextManager
8
+ from os import PathLike
9
+ from signal import Signals
10
+ from socket import AddressFamily, SocketKind, socket
11
+ from typing import (
12
+ IO,
13
+ TYPE_CHECKING,
14
+ Any,
15
+ TypeAlias,
16
+ TypeVar,
17
+ overload,
18
+ )
19
+
20
+ if sys.version_info >= (3, 11):
21
+ from typing import TypeVarTuple, Unpack
22
+ else:
23
+ from typing_extensions import TypeVarTuple, Unpack
24
+
25
+ if TYPE_CHECKING:
26
+ from _typeshed import FileDescriptorLike
27
+
28
+ from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore
29
+ from .._core._tasks import CancelScope
30
+ from .._core._testing import TaskInfo
31
+ from ._sockets import (
32
+ ConnectedUDPSocket,
33
+ ConnectedUNIXDatagramSocket,
34
+ IPSockAddrType,
35
+ SocketListener,
36
+ SocketStream,
37
+ UDPSocket,
38
+ UNIXDatagramSocket,
39
+ UNIXSocketStream,
40
+ )
41
+ from ._subprocesses import Process
42
+ from ._tasks import TaskGroup
43
+ from ._testing import TestRunner
44
+
45
+ T_Retval = TypeVar("T_Retval")
46
+ T_co = TypeVar("T_co", covariant=True)
47
+ PosArgsT = TypeVarTuple("PosArgsT")
48
+ StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
49
+
50
+
51
+ class AsyncBackend(metaclass=ABCMeta):
52
+ @classmethod
53
+ @abstractmethod
54
+ def run(
55
+ cls,
56
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
57
+ args: tuple[Unpack[PosArgsT]],
58
+ kwargs: dict[str, Any],
59
+ options: dict[str, Any],
60
+ ) -> T_Retval:
61
+ """
62
+ Run the given coroutine function in an asynchronous event loop.
63
+
64
+ The current thread must not be already running an event loop.
65
+
66
+ :param func: a coroutine function
67
+ :param args: positional arguments to ``func``
68
+ :param kwargs: positional arguments to ``func``
69
+ :param options: keyword arguments to call the backend ``run()`` implementation
70
+ with
71
+ :return: the return value of the coroutine function
72
+ """
73
+
74
+ @classmethod
75
+ @abstractmethod
76
+ def current_token(cls) -> object:
77
+ """
78
+ Return an object that allows other threads to run code inside the event loop.
79
+
80
+ :return: a token object, specific to the event loop running in the current
81
+ thread
82
+ """
83
+
84
+ @classmethod
85
+ @abstractmethod
86
+ def current_time(cls) -> float:
87
+ """
88
+ Return the current value of the event loop's internal clock.
89
+
90
+ :return: the clock value (seconds)
91
+ """
92
+
93
+ @classmethod
94
+ @abstractmethod
95
+ def cancelled_exception_class(cls) -> type[BaseException]:
96
+ """Return the exception class that is raised in a task if it's cancelled."""
97
+
98
+ @classmethod
99
+ @abstractmethod
100
+ async def checkpoint(cls) -> None:
101
+ """
102
+ Check if the task has been cancelled, and allow rescheduling of other tasks.
103
+
104
+ This is effectively the same as running :meth:`checkpoint_if_cancelled` and then
105
+ :meth:`cancel_shielded_checkpoint`.
106
+ """
107
+
108
+ @classmethod
109
+ async def checkpoint_if_cancelled(cls) -> None:
110
+ """
111
+ Check if the current task group has been cancelled.
112
+
113
+ This will check if the task has been cancelled, but will not allow other tasks
114
+ to be scheduled if not.
115
+
116
+ """
117
+ if cls.current_effective_deadline() == -math.inf:
118
+ await cls.checkpoint()
119
+
120
+ @classmethod
121
+ async def cancel_shielded_checkpoint(cls) -> None:
122
+ """
123
+ Allow the rescheduling of other tasks.
124
+
125
+ This will give other tasks the opportunity to run, but without checking if the
126
+ current task group has been cancelled, unlike with :meth:`checkpoint`.
127
+
128
+ """
129
+ with cls.create_cancel_scope(shield=True):
130
+ await cls.sleep(0)
131
+
132
+ @classmethod
133
+ @abstractmethod
134
+ async def sleep(cls, delay: float) -> None:
135
+ """
136
+ Pause the current task for the specified duration.
137
+
138
+ :param delay: the duration, in seconds
139
+ """
140
+
141
+ @classmethod
142
+ @abstractmethod
143
+ def create_cancel_scope(
144
+ cls, *, deadline: float = math.inf, shield: bool = False
145
+ ) -> CancelScope:
146
+ pass
147
+
148
+ @classmethod
149
+ @abstractmethod
150
+ def current_effective_deadline(cls) -> float:
151
+ """
152
+ Return the nearest deadline among all the cancel scopes effective for the
153
+ current task.
154
+
155
+ :return:
156
+ - a clock value from the event loop's internal clock
157
+ - ``inf`` if there is no deadline in effect
158
+ - ``-inf`` if the current scope has been cancelled
159
+ :rtype: float
160
+ """
161
+
162
+ @classmethod
163
+ @abstractmethod
164
+ def create_task_group(cls) -> TaskGroup:
165
+ pass
166
+
167
+ @classmethod
168
+ @abstractmethod
169
+ def create_event(cls) -> Event:
170
+ pass
171
+
172
+ @classmethod
173
+ @abstractmethod
174
+ def create_lock(cls, *, fast_acquire: bool) -> Lock:
175
+ pass
176
+
177
+ @classmethod
178
+ @abstractmethod
179
+ def create_semaphore(
180
+ cls,
181
+ initial_value: int,
182
+ *,
183
+ max_value: int | None = None,
184
+ fast_acquire: bool = False,
185
+ ) -> Semaphore:
186
+ pass
187
+
188
+ @classmethod
189
+ @abstractmethod
190
+ def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter:
191
+ pass
192
+
193
+ @classmethod
194
+ @abstractmethod
195
+ async def run_sync_in_worker_thread(
196
+ cls,
197
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
198
+ args: tuple[Unpack[PosArgsT]],
199
+ abandon_on_cancel: bool = False,
200
+ limiter: CapacityLimiter | None = None,
201
+ ) -> T_Retval:
202
+ pass
203
+
204
+ @classmethod
205
+ @abstractmethod
206
+ def check_cancelled(cls) -> None:
207
+ pass
208
+
209
+ @classmethod
210
+ @abstractmethod
211
+ def run_async_from_thread(
212
+ cls,
213
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
214
+ args: tuple[Unpack[PosArgsT]],
215
+ token: object,
216
+ ) -> T_co:
217
+ pass
218
+
219
+ @classmethod
220
+ @abstractmethod
221
+ def run_sync_from_thread(
222
+ cls,
223
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
224
+ args: tuple[Unpack[PosArgsT]],
225
+ token: object,
226
+ ) -> T_Retval:
227
+ pass
228
+
229
+ @classmethod
230
+ @abstractmethod
231
+ async def open_process(
232
+ cls,
233
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
234
+ *,
235
+ stdin: int | IO[Any] | None,
236
+ stdout: int | IO[Any] | None,
237
+ stderr: int | IO[Any] | None,
238
+ **kwargs: Any,
239
+ ) -> Process:
240
+ pass
241
+
242
+ @classmethod
243
+ @abstractmethod
244
+ def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None:
245
+ pass
246
+
247
+ @classmethod
248
+ @abstractmethod
249
+ async def connect_tcp(
250
+ cls, host: str, port: int, local_address: IPSockAddrType | None = None
251
+ ) -> SocketStream:
252
+ pass
253
+
254
+ @classmethod
255
+ @abstractmethod
256
+ async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream:
257
+ pass
258
+
259
+ @classmethod
260
+ @abstractmethod
261
+ def create_tcp_listener(cls, sock: socket) -> SocketListener:
262
+ pass
263
+
264
+ @classmethod
265
+ @abstractmethod
266
+ def create_unix_listener(cls, sock: socket) -> SocketListener:
267
+ pass
268
+
269
+ @classmethod
270
+ @abstractmethod
271
+ async def create_udp_socket(
272
+ cls,
273
+ family: AddressFamily,
274
+ local_address: IPSockAddrType | None,
275
+ remote_address: IPSockAddrType | None,
276
+ reuse_port: bool,
277
+ ) -> UDPSocket | ConnectedUDPSocket:
278
+ pass
279
+
280
+ @classmethod
281
+ @overload
282
+ async def create_unix_datagram_socket(
283
+ cls, raw_socket: socket, remote_path: None
284
+ ) -> UNIXDatagramSocket: ...
285
+
286
+ @classmethod
287
+ @overload
288
+ async def create_unix_datagram_socket(
289
+ cls, raw_socket: socket, remote_path: str | bytes
290
+ ) -> ConnectedUNIXDatagramSocket: ...
291
+
292
+ @classmethod
293
+ @abstractmethod
294
+ async def create_unix_datagram_socket(
295
+ cls, raw_socket: socket, remote_path: str | bytes | None
296
+ ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket:
297
+ pass
298
+
299
+ @classmethod
300
+ @abstractmethod
301
+ async def getaddrinfo(
302
+ cls,
303
+ host: bytes | str | None,
304
+ port: str | int | None,
305
+ *,
306
+ family: int | AddressFamily = 0,
307
+ type: int | SocketKind = 0,
308
+ proto: int = 0,
309
+ flags: int = 0,
310
+ ) -> Sequence[
311
+ tuple[
312
+ AddressFamily,
313
+ SocketKind,
314
+ int,
315
+ str,
316
+ tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],
317
+ ]
318
+ ]:
319
+ pass
320
+
321
+ @classmethod
322
+ @abstractmethod
323
+ async def getnameinfo(
324
+ cls, sockaddr: IPSockAddrType, flags: int = 0
325
+ ) -> tuple[str, str]:
326
+ pass
327
+
328
+ @classmethod
329
+ @abstractmethod
330
+ async def wait_readable(cls, obj: FileDescriptorLike) -> None:
331
+ pass
332
+
333
+ @classmethod
334
+ @abstractmethod
335
+ async def wait_writable(cls, obj: FileDescriptorLike) -> None:
336
+ pass
337
+
338
+ @classmethod
339
+ @abstractmethod
340
+ def notify_closing(cls, obj: FileDescriptorLike) -> None:
341
+ pass
342
+
343
+ @classmethod
344
+ @abstractmethod
345
+ async def wrap_listener_socket(cls, sock: socket) -> SocketListener:
346
+ pass
347
+
348
+ @classmethod
349
+ @abstractmethod
350
+ async def wrap_stream_socket(cls, sock: socket) -> SocketStream:
351
+ pass
352
+
353
+ @classmethod
354
+ @abstractmethod
355
+ async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream:
356
+ pass
357
+
358
+ @classmethod
359
+ @abstractmethod
360
+ async def wrap_udp_socket(cls, sock: socket) -> UDPSocket:
361
+ pass
362
+
363
+ @classmethod
364
+ @abstractmethod
365
+ async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket:
366
+ pass
367
+
368
+ @classmethod
369
+ @abstractmethod
370
+ async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket:
371
+ pass
372
+
373
+ @classmethod
374
+ @abstractmethod
375
+ async def wrap_connected_unix_datagram_socket(
376
+ cls, sock: socket
377
+ ) -> ConnectedUNIXDatagramSocket:
378
+ pass
379
+
380
+ @classmethod
381
+ @abstractmethod
382
+ def current_default_thread_limiter(cls) -> CapacityLimiter:
383
+ pass
384
+
385
+ @classmethod
386
+ @abstractmethod
387
+ def open_signal_receiver(
388
+ cls, *signals: Signals
389
+ ) -> AbstractContextManager[AsyncIterator[Signals]]:
390
+ pass
391
+
392
+ @classmethod
393
+ @abstractmethod
394
+ def get_current_task(cls) -> TaskInfo:
395
+ pass
396
+
397
+ @classmethod
398
+ @abstractmethod
399
+ def get_running_tasks(cls) -> Sequence[TaskInfo]:
400
+ pass
401
+
402
+ @classmethod
403
+ @abstractmethod
404
+ async def wait_all_tasks_blocked(cls) -> None:
405
+ pass
406
+
407
+ @classmethod
408
+ @abstractmethod
409
+ def create_test_runner(cls, options: dict[str, Any]) -> TestRunner:
410
+ pass
.venv/Lib/site-packages/anyio/abc/_resources.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from types import TracebackType
5
+ from typing import TypeVar
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ class AsyncResource(metaclass=ABCMeta):
11
+ """
12
+ Abstract base class for all closeable asynchronous resources.
13
+
14
+ Works as an asynchronous context manager which returns the instance itself on enter,
15
+ and calls :meth:`aclose` on exit.
16
+ """
17
+
18
+ __slots__ = ()
19
+
20
+ async def __aenter__(self: T) -> T:
21
+ return self
22
+
23
+ async def __aexit__(
24
+ self,
25
+ exc_type: type[BaseException] | None,
26
+ exc_val: BaseException | None,
27
+ exc_tb: TracebackType | None,
28
+ ) -> None:
29
+ await self.aclose()
30
+
31
+ @abstractmethod
32
+ async def aclose(self) -> None:
33
+ """Close the resource."""
.venv/Lib/site-packages/anyio/abc/_sockets.py ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import errno
4
+ import socket
5
+ from abc import abstractmethod
6
+ from collections.abc import Callable, Collection, Mapping
7
+ from contextlib import AsyncExitStack
8
+ from io import IOBase
9
+ from ipaddress import IPv4Address, IPv6Address
10
+ from socket import AddressFamily
11
+ from typing import Any, TypeAlias, TypeVar
12
+
13
+ from .._core._eventloop import get_async_backend
14
+ from .._core._typedattr import (
15
+ TypedAttributeProvider,
16
+ TypedAttributeSet,
17
+ typed_attribute,
18
+ )
19
+ from ._streams import ByteStream, Listener, UnreliableObjectStream
20
+ from ._tasks import TaskGroup
21
+
22
+ IPAddressType: TypeAlias = str | IPv4Address | IPv6Address
23
+ IPSockAddrType: TypeAlias = tuple[str, int]
24
+ SockAddrType: TypeAlias = IPSockAddrType | str
25
+ UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType]
26
+ UNIXDatagramPacketType: TypeAlias = tuple[bytes, str]
27
+ T_Retval = TypeVar("T_Retval")
28
+
29
+
30
+ def _validate_socket(
31
+ sock_or_fd: socket.socket | int,
32
+ sock_type: socket.SocketKind,
33
+ addr_family: socket.AddressFamily = socket.AF_UNSPEC,
34
+ *,
35
+ require_connected: bool = False,
36
+ require_bound: bool = False,
37
+ ) -> socket.socket:
38
+ if isinstance(sock_or_fd, int):
39
+ try:
40
+ sock = socket.socket(fileno=sock_or_fd)
41
+ except OSError as exc:
42
+ if exc.errno == errno.ENOTSOCK:
43
+ raise ValueError(
44
+ "the file descriptor does not refer to a socket"
45
+ ) from exc
46
+ elif require_connected:
47
+ raise ValueError("the socket must be connected") from exc
48
+ elif require_bound:
49
+ raise ValueError("the socket must be bound to a local address") from exc
50
+ else:
51
+ raise
52
+ elif isinstance(sock_or_fd, socket.socket):
53
+ sock = sock_or_fd
54
+ else:
55
+ raise TypeError(
56
+ f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead"
57
+ )
58
+
59
+ try:
60
+ if require_connected:
61
+ try:
62
+ sock.getpeername()
63
+ except OSError as exc:
64
+ raise ValueError("the socket must be connected") from exc
65
+
66
+ if require_bound:
67
+ try:
68
+ if sock.family in (socket.AF_INET, socket.AF_INET6):
69
+ bound_addr = sock.getsockname()[1]
70
+ else:
71
+ bound_addr = sock.getsockname()
72
+ except OSError:
73
+ bound_addr = None
74
+
75
+ if not bound_addr:
76
+ raise ValueError("the socket must be bound to a local address")
77
+
78
+ if addr_family != socket.AF_UNSPEC and sock.family != addr_family:
79
+ raise ValueError(
80
+ f"address family mismatch: expected {addr_family.name}, got "
81
+ f"{sock.family.name}"
82
+ )
83
+
84
+ if sock.type != sock_type:
85
+ raise ValueError(
86
+ f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}"
87
+ )
88
+ except BaseException:
89
+ # Avoid ResourceWarning from the locally constructed socket object
90
+ if isinstance(sock_or_fd, int):
91
+ sock.detach()
92
+
93
+ raise
94
+
95
+ sock.setblocking(False)
96
+ return sock
97
+
98
+
99
+ class SocketAttribute(TypedAttributeSet):
100
+ """
101
+ .. attribute:: family
102
+ :type: socket.AddressFamily
103
+
104
+ the address family of the underlying socket
105
+
106
+ .. attribute:: local_address
107
+ :type: tuple[str, int] | str
108
+
109
+ the local address the underlying socket is connected to
110
+
111
+ .. attribute:: local_port
112
+ :type: int
113
+
114
+ for IP based sockets, the local port the underlying socket is bound to
115
+
116
+ .. attribute:: raw_socket
117
+ :type: socket.socket
118
+
119
+ the underlying stdlib socket object
120
+
121
+ .. attribute:: remote_address
122
+ :type: tuple[str, int] | str
123
+
124
+ the remote address the underlying socket is connected to
125
+
126
+ .. attribute:: remote_port
127
+ :type: int
128
+
129
+ for IP based sockets, the remote port the underlying socket is connected to
130
+ """
131
+
132
+ family: AddressFamily = typed_attribute()
133
+ local_address: SockAddrType = typed_attribute()
134
+ local_port: int = typed_attribute()
135
+ raw_socket: socket.socket = typed_attribute()
136
+ remote_address: SockAddrType = typed_attribute()
137
+ remote_port: int = typed_attribute()
138
+
139
+
140
+ class _SocketProvider(TypedAttributeProvider):
141
+ @property
142
+ def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
143
+ from .._core._sockets import convert_ipv6_sockaddr as convert
144
+
145
+ attributes: dict[Any, Callable[[], Any]] = {
146
+ SocketAttribute.family: lambda: self._raw_socket.family,
147
+ SocketAttribute.local_address: lambda: convert(
148
+ self._raw_socket.getsockname()
149
+ ),
150
+ SocketAttribute.raw_socket: lambda: self._raw_socket,
151
+ }
152
+ try:
153
+ peername: tuple[str, int] | None = convert(self._raw_socket.getpeername())
154
+ except OSError:
155
+ peername = None
156
+
157
+ # Provide the remote address for connected sockets
158
+ if peername is not None:
159
+ attributes[SocketAttribute.remote_address] = lambda: peername
160
+
161
+ # Provide local and remote ports for IP based sockets
162
+ if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6):
163
+ attributes[SocketAttribute.local_port] = lambda: (
164
+ self._raw_socket.getsockname()[1]
165
+ )
166
+ if peername is not None:
167
+ remote_port = peername[1]
168
+ attributes[SocketAttribute.remote_port] = lambda: remote_port
169
+
170
+ return attributes
171
+
172
+ @property
173
+ @abstractmethod
174
+ def _raw_socket(self) -> socket.socket:
175
+ pass
176
+
177
+
178
+ class SocketStream(ByteStream, _SocketProvider):
179
+ """
180
+ Transports bytes over a socket.
181
+
182
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
183
+ """
184
+
185
+ @classmethod
186
+ async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream:
187
+ """
188
+ Wrap an existing socket object or file descriptor as a socket stream.
189
+
190
+ The newly created socket wrapper takes ownership of the socket being passed in.
191
+ The existing socket must already be connected.
192
+
193
+ :param sock_or_fd: a socket object or file descriptor
194
+ :return: a socket stream
195
+
196
+ """
197
+ sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True)
198
+ return await get_async_backend().wrap_stream_socket(sock)
199
+
200
+
201
+ class UNIXSocketStream(SocketStream):
202
+ @classmethod
203
+ async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream:
204
+ """
205
+ Wrap an existing socket object or file descriptor as a UNIX socket stream.
206
+
207
+ The newly created socket wrapper takes ownership of the socket being passed in.
208
+ The existing socket must already be connected.
209
+
210
+ :param sock_or_fd: a socket object or file descriptor
211
+ :return: a UNIX socket stream
212
+
213
+ """
214
+ sock = _validate_socket(
215
+ sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True
216
+ )
217
+ return await get_async_backend().wrap_unix_stream_socket(sock)
218
+
219
+ @abstractmethod
220
+ async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None:
221
+ """
222
+ Send file descriptors along with a message to the peer.
223
+
224
+ :param message: a non-empty bytestring
225
+ :param fds: a collection of files (either numeric file descriptors or open file
226
+ or socket objects)
227
+ """
228
+
229
+ @abstractmethod
230
+ async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]:
231
+ """
232
+ Receive file descriptors along with a message from the peer.
233
+
234
+ :param msglen: length of the message to expect from the peer
235
+ :param maxfds: maximum number of file descriptors to expect from the peer
236
+ :return: a tuple of (message, file descriptors)
237
+ """
238
+
239
+
240
+ class SocketListener(Listener[SocketStream], _SocketProvider):
241
+ """
242
+ Listens to incoming socket connections.
243
+
244
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
245
+ """
246
+
247
+ @classmethod
248
+ async def from_socket(
249
+ cls,
250
+ sock_or_fd: socket.socket | int,
251
+ ) -> SocketListener:
252
+ """
253
+ Wrap an existing socket object or file descriptor as a socket listener.
254
+
255
+ The newly created listener takes ownership of the socket being passed in.
256
+
257
+ :param sock_or_fd: a socket object or file descriptor
258
+ :return: a socket listener
259
+
260
+ """
261
+ sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True)
262
+ return await get_async_backend().wrap_listener_socket(sock)
263
+
264
+ @abstractmethod
265
+ async def accept(self) -> SocketStream:
266
+ """Accept an incoming connection."""
267
+
268
+ async def serve(
269
+ self,
270
+ handler: Callable[[SocketStream], Any],
271
+ task_group: TaskGroup | None = None,
272
+ ) -> None:
273
+ from .. import create_task_group
274
+
275
+ async with AsyncExitStack() as stack:
276
+ if task_group is None:
277
+ task_group = await stack.enter_async_context(create_task_group())
278
+
279
+ while True:
280
+ stream = await self.accept()
281
+ task_group.start_soon(handler, stream)
282
+
283
+
284
+ class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider):
285
+ """
286
+ Represents an unconnected UDP socket.
287
+
288
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
289
+ """
290
+
291
+ @classmethod
292
+ async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket:
293
+ """
294
+ Wrap an existing socket object or file descriptor as a UDP socket.
295
+
296
+ The newly created socket wrapper takes ownership of the socket being passed in.
297
+ The existing socket must be bound to a local address.
298
+
299
+ :param sock_or_fd: a socket object or file descriptor
300
+ :return: a UDP socket
301
+
302
+ """
303
+ sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True)
304
+ return await get_async_backend().wrap_udp_socket(sock)
305
+
306
+ async def sendto(self, data: bytes, host: str, port: int) -> None:
307
+ """
308
+ Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))).
309
+
310
+ """
311
+ return await self.send((data, (host, port)))
312
+
313
+
314
+ class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider):
315
+ """
316
+ Represents an connected UDP socket.
317
+
318
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
319
+ """
320
+
321
+ @classmethod
322
+ async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket:
323
+ """
324
+ Wrap an existing socket object or file descriptor as a connected UDP socket.
325
+
326
+ The newly created socket wrapper takes ownership of the socket being passed in.
327
+ The existing socket must already be connected.
328
+
329
+ :param sock_or_fd: a socket object or file descriptor
330
+ :return: a connected UDP socket
331
+
332
+ """
333
+ sock = _validate_socket(
334
+ sock_or_fd,
335
+ socket.SOCK_DGRAM,
336
+ require_connected=True,
337
+ )
338
+ return await get_async_backend().wrap_connected_udp_socket(sock)
339
+
340
+
341
+ class UNIXDatagramSocket(
342
+ UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider
343
+ ):
344
+ """
345
+ Represents an unconnected Unix datagram socket.
346
+
347
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
348
+ """
349
+
350
+ @classmethod
351
+ async def from_socket(
352
+ cls,
353
+ sock_or_fd: socket.socket | int,
354
+ ) -> UNIXDatagramSocket:
355
+ """
356
+ Wrap an existing socket object or file descriptor as a UNIX datagram
357
+ socket.
358
+
359
+ The newly created socket wrapper takes ownership of the socket being passed in.
360
+
361
+ :param sock_or_fd: a socket object or file descriptor
362
+ :return: a UNIX datagram socket
363
+
364
+ """
365
+ sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX)
366
+ return await get_async_backend().wrap_unix_datagram_socket(sock)
367
+
368
+ async def sendto(self, data: bytes, path: str) -> None:
369
+ """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path))."""
370
+ return await self.send((data, path))
371
+
372
+
373
+ class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider):
374
+ """
375
+ Represents a connected Unix datagram socket.
376
+
377
+ Supports all relevant extra attributes from :class:`~SocketAttribute`.
378
+ """
379
+
380
+ @classmethod
381
+ async def from_socket(
382
+ cls,
383
+ sock_or_fd: socket.socket | int,
384
+ ) -> ConnectedUNIXDatagramSocket:
385
+ """
386
+ Wrap an existing socket object or file descriptor as a connected UNIX datagram
387
+ socket.
388
+
389
+ The newly created socket wrapper takes ownership of the socket being passed in.
390
+ The existing socket must already be connected.
391
+
392
+ :param sock_or_fd: a socket object or file descriptor
393
+ :return: a connected UNIX datagram socket
394
+
395
+ """
396
+ sock = _validate_socket(
397
+ sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True
398
+ )
399
+ return await get_async_backend().wrap_connected_unix_datagram_socket(sock)
.venv/Lib/site-packages/anyio/abc/_streams.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from collections.abc import Callable
5
+ from typing import Any, Generic, TypeAlias, TypeVar
6
+
7
+ from .._core._exceptions import EndOfStream
8
+ from .._core._typedattr import TypedAttributeProvider
9
+ from ._resources import AsyncResource
10
+ from ._tasks import TaskGroup
11
+
12
+ T_Item = TypeVar("T_Item")
13
+ T_co = TypeVar("T_co", covariant=True)
14
+ T_contra = TypeVar("T_contra", contravariant=True)
15
+
16
+
17
+ class UnreliableObjectReceiveStream(
18
+ Generic[T_co], AsyncResource, TypedAttributeProvider
19
+ ):
20
+ """
21
+ An interface for receiving objects.
22
+
23
+ This interface makes no guarantees that the received messages arrive in the order in
24
+ which they were sent, or that no messages are missed.
25
+
26
+ Asynchronously iterating over objects of this type will yield objects matching the
27
+ given type parameter.
28
+ """
29
+
30
+ def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]:
31
+ return self
32
+
33
+ async def __anext__(self) -> T_co:
34
+ try:
35
+ return await self.receive()
36
+ except EndOfStream:
37
+ raise StopAsyncIteration from None
38
+
39
+ @abstractmethod
40
+ async def receive(self) -> T_co:
41
+ """
42
+ Receive the next item.
43
+
44
+ :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly
45
+ closed
46
+ :raises ~anyio.EndOfStream: if this stream has been closed from the other end
47
+ :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable
48
+ due to external causes
49
+ """
50
+
51
+
52
+ class UnreliableObjectSendStream(
53
+ Generic[T_contra], AsyncResource, TypedAttributeProvider
54
+ ):
55
+ """
56
+ An interface for sending objects.
57
+
58
+ This interface makes no guarantees that the messages sent will reach the
59
+ recipient(s) in the same order in which they were sent, or at all.
60
+ """
61
+
62
+ @abstractmethod
63
+ async def send(self, item: T_contra) -> None:
64
+ """
65
+ Send an item to the peer(s).
66
+
67
+ :param item: the item to send
68
+ :raises ~anyio.ClosedResourceError: if the send stream has been explicitly
69
+ closed
70
+ :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable
71
+ due to external causes
72
+ """
73
+
74
+
75
+ class UnreliableObjectStream(
76
+ UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item]
77
+ ):
78
+ """
79
+ A bidirectional message stream which does not guarantee the order or reliability of
80
+ message delivery.
81
+ """
82
+
83
+
84
+ class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]):
85
+ """
86
+ A receive message stream which guarantees that messages are received in the same
87
+ order in which they were sent, and that no messages are missed.
88
+ """
89
+
90
+
91
+ class ObjectSendStream(UnreliableObjectSendStream[T_contra]):
92
+ """
93
+ A send message stream which guarantees that messages are delivered in the same order
94
+ in which they were sent, without missing any messages in the middle.
95
+ """
96
+
97
+
98
+ class ObjectStream(
99
+ ObjectReceiveStream[T_Item],
100
+ ObjectSendStream[T_Item],
101
+ UnreliableObjectStream[T_Item],
102
+ ):
103
+ """
104
+ A bidirectional message stream which guarantees the order and reliability of message
105
+ delivery.
106
+ """
107
+
108
+ @abstractmethod
109
+ async def send_eof(self) -> None:
110
+ """
111
+ Send an end-of-file indication to the peer.
112
+
113
+ You should not try to send any further data to this stream after calling this
114
+ method. This method is idempotent (does nothing on successive calls).
115
+ """
116
+
117
+
118
+ class ByteReceiveStream(AsyncResource, TypedAttributeProvider):
119
+ """
120
+ An interface for receiving bytes from a single peer.
121
+
122
+ Iterating this byte stream will yield a byte string of arbitrary length, but no more
123
+ than 65536 bytes.
124
+ """
125
+
126
+ def __aiter__(self) -> ByteReceiveStream:
127
+ return self
128
+
129
+ async def __anext__(self) -> bytes:
130
+ try:
131
+ return await self.receive()
132
+ except EndOfStream:
133
+ raise StopAsyncIteration from None
134
+
135
+ @abstractmethod
136
+ async def receive(self, max_bytes: int = 65536) -> bytes:
137
+ """
138
+ Receive at most ``max_bytes`` bytes from the peer.
139
+
140
+ .. note:: Implementers of this interface should not return an empty
141
+ :class:`bytes` object, and users should ignore them.
142
+
143
+ :param max_bytes: maximum number of bytes to receive
144
+ :return: the received bytes
145
+ :raises ~anyio.EndOfStream: if this stream has been closed from the other end
146
+ """
147
+
148
+
149
+ class ByteSendStream(AsyncResource, TypedAttributeProvider):
150
+ """An interface for sending bytes to a single peer."""
151
+
152
+ @abstractmethod
153
+ async def send(self, item: bytes) -> None:
154
+ """
155
+ Send the given bytes to the peer.
156
+
157
+ :param item: the bytes to send
158
+ """
159
+
160
+
161
+ class ByteStream(ByteReceiveStream, ByteSendStream):
162
+ """A bidirectional byte stream."""
163
+
164
+ @abstractmethod
165
+ async def send_eof(self) -> None:
166
+ """
167
+ Send an end-of-file indication to the peer.
168
+
169
+ You should not try to send any further data to this stream after calling this
170
+ method. This method is idempotent (does nothing on successive calls).
171
+ """
172
+
173
+
174
+ #: Type alias for all unreliable bytes-oriented receive streams.
175
+ AnyUnreliableByteReceiveStream: TypeAlias = (
176
+ UnreliableObjectReceiveStream[bytes] | ByteReceiveStream
177
+ )
178
+ #: Type alias for all unreliable bytes-oriented send streams.
179
+ AnyUnreliableByteSendStream: TypeAlias = (
180
+ UnreliableObjectSendStream[bytes] | ByteSendStream
181
+ )
182
+ #: Type alias for all unreliable bytes-oriented streams.
183
+ AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream
184
+ #: Type alias for all bytes-oriented receive streams.
185
+ AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream
186
+ #: Type alias for all bytes-oriented send streams.
187
+ AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream
188
+ #: Type alias for all bytes-oriented streams.
189
+ AnyByteStream: TypeAlias = ObjectStream[bytes] | ByteStream
190
+
191
+
192
+ class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider):
193
+ """An interface for objects that let you accept incoming connections."""
194
+
195
+ @abstractmethod
196
+ async def serve(
197
+ self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None
198
+ ) -> None:
199
+ """
200
+ Accept incoming connections as they come in and start tasks to handle them.
201
+
202
+ :param handler: a callable that will be used to handle each accepted connection
203
+ :param task_group: the task group that will be used to start tasks for handling
204
+ each accepted connection (if omitted, an ad-hoc task group will be created)
205
+ """
206
+
207
+
208
+ class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta):
209
+ @abstractmethod
210
+ async def connect(self) -> ObjectStream[T_co]:
211
+ """
212
+ Connect to the remote endpoint.
213
+
214
+ :return: an object stream connected to the remote end
215
+ :raises ConnectionFailed: if the connection fails
216
+ """
217
+
218
+
219
+ class ByteStreamConnectable(metaclass=ABCMeta):
220
+ @abstractmethod
221
+ async def connect(self) -> ByteStream:
222
+ """
223
+ Connect to the remote endpoint.
224
+
225
+ :return: a bytestream connected to the remote end
226
+ :raises ConnectionFailed: if the connection fails
227
+ """
228
+
229
+
230
+ #: Type alias for all connectables returning bytestreams or bytes-oriented object streams
231
+ AnyByteStreamConnectable: TypeAlias = (
232
+ ObjectStreamConnectable[bytes] | ByteStreamConnectable
233
+ )
.venv/Lib/site-packages/anyio/abc/_subprocesses.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from signal import Signals
5
+
6
+ from ._resources import AsyncResource
7
+ from ._streams import ByteReceiveStream, ByteSendStream
8
+
9
+
10
+ class Process(AsyncResource):
11
+ """An asynchronous version of :class:`subprocess.Popen`."""
12
+
13
+ @abstractmethod
14
+ async def wait(self) -> int:
15
+ """
16
+ Wait until the process exits.
17
+
18
+ :return: the exit code of the process
19
+ """
20
+
21
+ @abstractmethod
22
+ def terminate(self) -> None:
23
+ """
24
+ Terminates the process, gracefully if possible.
25
+
26
+ On Windows, this calls ``TerminateProcess()``.
27
+ On POSIX systems, this sends ``SIGTERM`` to the process.
28
+
29
+ .. seealso:: :meth:`subprocess.Popen.terminate`
30
+ """
31
+
32
+ @abstractmethod
33
+ def kill(self) -> None:
34
+ """
35
+ Kills the process.
36
+
37
+ On Windows, this calls ``TerminateProcess()``.
38
+ On POSIX systems, this sends ``SIGKILL`` to the process.
39
+
40
+ .. seealso:: :meth:`subprocess.Popen.kill`
41
+ """
42
+
43
+ @abstractmethod
44
+ def send_signal(self, signal: Signals) -> None:
45
+ """
46
+ Send a signal to the subprocess.
47
+
48
+ .. seealso:: :meth:`subprocess.Popen.send_signal`
49
+
50
+ :param signal: the signal number (e.g. :data:`signal.SIGHUP`)
51
+ """
52
+
53
+ @property
54
+ @abstractmethod
55
+ def pid(self) -> int:
56
+ """The process ID of the process."""
57
+
58
+ @property
59
+ @abstractmethod
60
+ def returncode(self) -> int | None:
61
+ """
62
+ The return code of the process. If the process has not yet terminated, this will
63
+ be ``None``.
64
+ """
65
+
66
+ @property
67
+ @abstractmethod
68
+ def stdin(self) -> ByteSendStream | None:
69
+ """The stream for the standard input of the process."""
70
+
71
+ @property
72
+ @abstractmethod
73
+ def stdout(self) -> ByteReceiveStream | None:
74
+ """The stream for the standard output of the process."""
75
+
76
+ @property
77
+ @abstractmethod
78
+ def stderr(self) -> ByteReceiveStream | None:
79
+ """The stream for the standard error output of the process."""
.venv/Lib/site-packages/anyio/abc/_tasks.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from abc import ABCMeta, abstractmethod
5
+ from collections.abc import Callable, Coroutine
6
+ from contextvars import Context
7
+ from types import TracebackType
8
+ from typing import TYPE_CHECKING, Any, Literal, Protocol, final, overload
9
+
10
+ if sys.version_info >= (3, 13):
11
+ from typing import TypeVar
12
+ else:
13
+ from typing_extensions import TypeVar
14
+
15
+ if sys.version_info >= (3, 11):
16
+ from typing import TypeVarTuple, Unpack
17
+ else:
18
+ from typing_extensions import TypeVarTuple, Unpack
19
+
20
+ if TYPE_CHECKING:
21
+ from .._core._tasks import CancelScope, TaskHandle
22
+
23
+ T_co = TypeVar("T_co", covariant=True)
24
+ T_contra = TypeVar("T_contra", contravariant=True, default=None)
25
+ PosArgsT = TypeVarTuple("PosArgsT")
26
+
27
+
28
+ def get_callable_name(func: Callable, override: object = None) -> str:
29
+ if override is not None:
30
+ return str(override)
31
+
32
+ module = getattr(func, "__module__", None)
33
+ qualname = getattr(func, "__qualname__", None)
34
+ return ".".join([x for x in (module, qualname) if x])
35
+
36
+
37
+ def call_for_coroutine(
38
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
39
+ args: tuple[Unpack[PosArgsT]],
40
+ **kwargs: Any,
41
+ ) -> Coroutine[Any, Any, T_co]:
42
+ """
43
+ Call the given function with the given positional and keyword arguments.
44
+
45
+ :return: the resulting coroutine
46
+ :raises TypeError: if the return value was not a coroutine object
47
+
48
+ """
49
+ coro = func(*args, **kwargs)
50
+ if not isinstance(coro, Coroutine):
51
+ prefix = f"{func.__module__}." if hasattr(func, "__module__") else ""
52
+ raise TypeError(
53
+ f"Expected {prefix}{func.__qualname__}() to return a coroutine, but "
54
+ f"the return value ({coro!r}) is not a coroutine object"
55
+ )
56
+
57
+ return coro
58
+
59
+
60
+ class TaskStatus(Protocol[T_contra]):
61
+ @overload
62
+ def started(self: TaskStatus[None]) -> None: ...
63
+
64
+ @overload
65
+ def started(self, value: T_contra) -> None: ...
66
+
67
+ def started(self, value: T_contra | None = None) -> None:
68
+ """
69
+ Signal that the task has started.
70
+
71
+ :param value: object passed back to the starter of the task
72
+ """
73
+
74
+
75
+ class TaskGroup(metaclass=ABCMeta):
76
+ """
77
+ Groups several asynchronous tasks together.
78
+
79
+ :ivar cancel_scope: the cancel scope inherited by all child tasks
80
+ :vartype cancel_scope: CancelScope
81
+
82
+ .. note:: On asyncio, support for eager task factories is considered to be
83
+ **experimental**. In particular, they don't follow the usual semantics of new
84
+ tasks being scheduled on the next iteration of the event loop, and may thus
85
+ cause unexpected behavior in code that wasn't written with such semantics in
86
+ mind.
87
+ """
88
+
89
+ cancel_scope: CancelScope
90
+
91
+ def cancel(self, reason: str | None = None) -> None:
92
+ """
93
+ Cancel this task group's cancel scope immediately.
94
+
95
+ This is a shortcut for calling ``.cancel_scope.cancel()`` on the task group.
96
+
97
+ :param reason: a message describing the reason for the cancellation
98
+
99
+ .. versionadded:: 4.14.0
100
+
101
+ """
102
+ self.cancel_scope.cancel(reason)
103
+
104
+ @abstractmethod
105
+ def create_task(
106
+ self,
107
+ coro: Coroutine[Any, Any, T_co],
108
+ *,
109
+ name: object = None,
110
+ context: Context | None = None,
111
+ ) -> TaskHandle[T_co]:
112
+ """
113
+ Create a new task from a coroutine object and schedule it to run.
114
+
115
+ :param coro: a coroutine object
116
+ :param name: optional name to give the task
117
+ :param context: optional context to run the task in
118
+ :return: a task handle
119
+
120
+ .. versionadded:: 4.14.0
121
+ """
122
+
123
+ @final
124
+ def start_soon(
125
+ self,
126
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
127
+ *args: Unpack[PosArgsT],
128
+ name: object = None,
129
+ ) -> TaskHandle[T_co]:
130
+ """
131
+ Start a new task in this task group.
132
+
133
+ :param func: a coroutine function
134
+ :param args: positional arguments to call the function with
135
+ :param name: name of the task, for the purposes of introspection and debugging
136
+ :return: a task handle
137
+
138
+ .. versionadded:: 3.0
139
+ .. versionchanged:: 4.14.0
140
+ This method now returns a task handle.
141
+
142
+ """
143
+ final_name = get_callable_name(func, name)
144
+ return self.create_task(call_for_coroutine(func, args), name=final_name)
145
+
146
+ @overload
147
+ async def start(
148
+ self,
149
+ func: Callable[..., Coroutine[Any, Any, T_co]],
150
+ *args: object,
151
+ name: object = None,
152
+ return_handle: Literal[False] = ...,
153
+ ) -> Any: ...
154
+
155
+ @overload
156
+ async def start(
157
+ self,
158
+ func: Callable[..., Coroutine[Any, Any, T_co]],
159
+ *args: object,
160
+ name: object = None,
161
+ return_handle: Literal[True],
162
+ ) -> TaskHandle[T_co, Any]: ...
163
+
164
+ @abstractmethod
165
+ async def start(
166
+ self,
167
+ func: Callable[..., Coroutine[Any, Any, T_co]],
168
+ *args: object,
169
+ name: object = None,
170
+ return_handle: Literal[False] | Literal[True] = False,
171
+ ) -> Any:
172
+ """
173
+ Start a new task and wait until it signals for readiness.
174
+
175
+ The target callable must accept a keyword argument ``task_status`` (of type
176
+ :class:`TaskStatus`). Awaiting on this method will return whatever was passed to
177
+ ``task_status.started()`` (``None`` by default).
178
+
179
+ .. note:: The :class:`TaskStatus` class is generic, and the type argument should
180
+ indicate the type of the value that will be passed to
181
+ ``task_status.started()``.
182
+
183
+ :param func: a coroutine function that accepts the ``task_status`` keyword
184
+ argument
185
+ :param args: positional arguments to call the function with
186
+ :param name: an optional name for the task, for introspection and debugging
187
+ :param return_handle: if ``True``, return a :class:`TaskHandle` which also
188
+ contains the start value in ``start_value``
189
+ :return: the value passed to ``task_status.started()``
190
+ :raises RuntimeError: if the task finishes without calling
191
+ ``task_status.started()``
192
+
193
+ .. seealso:: :ref:`start_initialize`
194
+
195
+ .. versionadded:: 3.0
196
+ """
197
+
198
+ @abstractmethod
199
+ async def __aenter__(self) -> TaskGroup:
200
+ """Enter the task group context and allow starting new tasks."""
201
+
202
+ @abstractmethod
203
+ async def __aexit__(
204
+ self,
205
+ exc_type: type[BaseException] | None,
206
+ exc_val: BaseException | None,
207
+ exc_tb: TracebackType | None,
208
+ ) -> bool:
209
+ """Exit the task group context waiting for all tasks to finish."""
.venv/Lib/site-packages/anyio/abc/_testing.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import types
4
+ from abc import ABCMeta, abstractmethod
5
+ from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable
6
+ from typing import Any, TypeVar
7
+
8
+ _T = TypeVar("_T")
9
+
10
+
11
+ class TestRunner(metaclass=ABCMeta):
12
+ """
13
+ Encapsulates a running event loop. Every call made through this object will use the
14
+ same event loop.
15
+ """
16
+
17
+ def __enter__(self) -> TestRunner:
18
+ return self
19
+
20
+ @abstractmethod
21
+ def __exit__(
22
+ self,
23
+ exc_type: type[BaseException] | None,
24
+ exc_val: BaseException | None,
25
+ exc_tb: types.TracebackType | None,
26
+ ) -> bool | None: ...
27
+
28
+ @abstractmethod
29
+ def run_asyncgen_fixture(
30
+ self,
31
+ fixture_func: Callable[..., AsyncGenerator[_T, Any]],
32
+ kwargs: dict[str, Any],
33
+ ) -> Iterable[_T]:
34
+ """
35
+ Run an async generator fixture.
36
+
37
+ :param fixture_func: the fixture function
38
+ :param kwargs: keyword arguments to call the fixture function with
39
+ :return: an iterator yielding the value yielded from the async generator
40
+ """
41
+
42
+ @abstractmethod
43
+ def run_fixture(
44
+ self,
45
+ fixture_func: Callable[..., Coroutine[Any, Any, _T]],
46
+ kwargs: dict[str, Any],
47
+ ) -> _T:
48
+ """
49
+ Run an async fixture.
50
+
51
+ :param fixture_func: the fixture function
52
+ :param kwargs: keyword arguments to call the fixture function with
53
+ :return: the return value of the fixture function
54
+ """
55
+
56
+ @abstractmethod
57
+ def run_test(
58
+ self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any]
59
+ ) -> None:
60
+ """
61
+ Run an async test function.
62
+
63
+ :param test_func: the test function
64
+ :param kwargs: keyword arguments to call the test function with
65
+ """
66
+
67
+ @abstractmethod
68
+ def is_running(self) -> bool:
69
+ """
70
+ Check if the test runner is running.
71
+
72
+ :return: ``True`` if the coroutine is currently being run, ``False`` otherwise.
73
+ """
.venv/Lib/site-packages/anyio/from_thread.py ADDED
@@ -0,0 +1,582 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "BlockingPortal",
5
+ "BlockingPortalProvider",
6
+ "check_cancelled",
7
+ "run",
8
+ "run_sync",
9
+ "start_blocking_portal",
10
+ )
11
+
12
+ import sys
13
+ from collections.abc import Awaitable, Callable, Coroutine, Generator
14
+ from concurrent.futures import Future
15
+ from contextlib import (
16
+ AbstractAsyncContextManager,
17
+ AbstractContextManager,
18
+ contextmanager,
19
+ )
20
+ from dataclasses import dataclass, field
21
+ from functools import partial
22
+ from inspect import isawaitable
23
+ from threading import Lock, Thread, current_thread, get_ident
24
+ from types import TracebackType
25
+ from typing import (
26
+ Any,
27
+ Generic,
28
+ TypeVar,
29
+ cast,
30
+ overload,
31
+ )
32
+
33
+ from ._core._eventloop import (
34
+ get_cancelled_exc_class,
35
+ threadlocals,
36
+ )
37
+ from ._core._eventloop import run as run_eventloop
38
+ from ._core._exceptions import NoEventLoopError
39
+ from ._core._synchronization import Event
40
+ from ._core._tasks import CancelScope, create_task_group
41
+ from .abc._tasks import TaskStatus
42
+ from .lowlevel import EventLoopToken, current_token
43
+
44
+ if sys.version_info >= (3, 11):
45
+ from typing import TypeVarTuple, Unpack
46
+ else:
47
+ from typing_extensions import TypeVarTuple, Unpack
48
+
49
+ T_Retval = TypeVar("T_Retval")
50
+ T_co = TypeVar("T_co", covariant=True)
51
+ PosArgsT = TypeVarTuple("PosArgsT")
52
+
53
+
54
+ def _token_or_error(token: EventLoopToken | None) -> EventLoopToken:
55
+ if token is not None:
56
+ return token
57
+
58
+ try:
59
+ return threadlocals.current_token
60
+ except AttributeError:
61
+ raise NoEventLoopError(
62
+ "Not running inside an AnyIO worker thread, and no event loop token was "
63
+ "provided"
64
+ ) from None
65
+
66
+
67
+ def run(
68
+ func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
69
+ *args: Unpack[PosArgsT],
70
+ token: EventLoopToken | None = None,
71
+ ) -> T_co:
72
+ """
73
+ Call a coroutine function from a worker thread.
74
+
75
+ :param func: a coroutine function
76
+ :param args: positional arguments for the callable
77
+ :param token: an event loop token to use to get back to the event loop thread
78
+ (required if calling this function from outside an AnyIO worker thread)
79
+ :return: the return value of the coroutine function
80
+ :raises MissingTokenError: if no token was provided and called from outside an
81
+ AnyIO worker thread
82
+ :raises RunFinishedError: if the event loop tied to ``token`` is no longer running
83
+
84
+ .. versionchanged:: 4.11.0
85
+ Added the ``token`` parameter.
86
+
87
+ """
88
+ explicit_token = token is not None
89
+ token = _token_or_error(token)
90
+ return token.backend_class.run_async_from_thread(
91
+ func, args, token=token.native_token if explicit_token else None
92
+ )
93
+
94
+
95
+ def run_sync(
96
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
97
+ *args: Unpack[PosArgsT],
98
+ token: EventLoopToken | None = None,
99
+ ) -> T_Retval:
100
+ """
101
+ Call a function in the event loop thread from a worker thread.
102
+
103
+ :param func: a callable
104
+ :param args: positional arguments for the callable
105
+ :param token: an event loop token to use to get back to the event loop thread
106
+ (required if calling this function from outside an AnyIO worker thread)
107
+ :return: the return value of the callable
108
+ :raises MissingTokenError: if no token was provided and called from outside an
109
+ AnyIO worker thread
110
+ :raises RunFinishedError: if the event loop tied to ``token`` is no longer running
111
+
112
+ .. versionchanged:: 4.11.0
113
+ Added the ``token`` parameter.
114
+
115
+ """
116
+ explicit_token = token is not None
117
+ token = _token_or_error(token)
118
+ return token.backend_class.run_sync_from_thread(
119
+ func, args, token=token.native_token if explicit_token else None
120
+ )
121
+
122
+
123
+ class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager):
124
+ _enter_future: Future[T_co]
125
+ _exit_future: Future[bool | None]
126
+ _exit_event: Event
127
+ _exit_exc_info: tuple[
128
+ type[BaseException] | None, BaseException | None, TracebackType | None
129
+ ] = (None, None, None)
130
+
131
+ def __init__(
132
+ self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal
133
+ ):
134
+ self._async_cm = async_cm
135
+ self._portal = portal
136
+
137
+ async def run_async_cm(self) -> bool | None:
138
+ try:
139
+ self._exit_event = Event()
140
+ value = await self._async_cm.__aenter__()
141
+ except BaseException as exc:
142
+ self._enter_future.set_exception(exc)
143
+ raise
144
+ else:
145
+ self._enter_future.set_result(value)
146
+
147
+ try:
148
+ # Wait for the sync context manager to exit.
149
+ # This next statement can raise `get_cancelled_exc_class()` if
150
+ # something went wrong in a task group in this async context
151
+ # manager.
152
+ await self._exit_event.wait()
153
+ finally:
154
+ # In case of cancellation, it could be that we end up here before
155
+ # `_BlockingAsyncContextManager.__exit__` is called, and an
156
+ # `_exit_exc_info` has been set.
157
+ result = await self._async_cm.__aexit__(*self._exit_exc_info)
158
+
159
+ return result
160
+
161
+ def __enter__(self) -> T_co:
162
+ self._enter_future = Future()
163
+ self._exit_future = self._portal.start_task_soon(self.run_async_cm)
164
+ return self._enter_future.result()
165
+
166
+ def __exit__(
167
+ self,
168
+ __exc_type: type[BaseException] | None,
169
+ __exc_value: BaseException | None,
170
+ __traceback: TracebackType | None,
171
+ ) -> bool | None:
172
+ self._exit_exc_info = __exc_type, __exc_value, __traceback
173
+ self._portal.call(self._exit_event.set)
174
+ return self._exit_future.result()
175
+
176
+
177
+ class _BlockingPortalTaskStatus(TaskStatus):
178
+ def __init__(self, future: Future):
179
+ self._future = future
180
+
181
+ def started(self, value: object = None) -> None:
182
+ self._future.set_result(value)
183
+
184
+
185
+ class BlockingPortal:
186
+ """
187
+ An object that lets external threads run code in an asynchronous event loop.
188
+
189
+ :raises NoEventLoopError: if no supported asynchronous event loop is running in the
190
+ current thread
191
+ """
192
+
193
+ def __init__(self) -> None:
194
+ self._token = current_token()
195
+ self._event_loop_thread_id: int | None = get_ident()
196
+ self._stop_event = Event()
197
+ self._task_group = create_task_group()
198
+
199
+ async def __aenter__(self) -> BlockingPortal:
200
+ await self._task_group.__aenter__()
201
+ return self
202
+
203
+ async def __aexit__(
204
+ self,
205
+ exc_type: type[BaseException] | None,
206
+ exc_val: BaseException | None,
207
+ exc_tb: TracebackType | None,
208
+ ) -> bool:
209
+ await self.stop()
210
+ return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
211
+
212
+ def _check_running(self) -> None:
213
+ if self._event_loop_thread_id is None:
214
+ raise RuntimeError("This portal is not running")
215
+ if self._event_loop_thread_id == get_ident():
216
+ raise RuntimeError(
217
+ "This method cannot be called from the event loop thread"
218
+ )
219
+
220
+ async def sleep_until_stopped(self) -> None:
221
+ """Sleep until :meth:`stop` is called."""
222
+ await self._stop_event.wait()
223
+
224
+ async def stop(self, cancel_remaining: bool = False) -> None:
225
+ """
226
+ Signal the portal to shut down.
227
+
228
+ This marks the portal as no longer accepting new calls and exits from
229
+ :meth:`sleep_until_stopped`.
230
+
231
+ :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False``
232
+ to let them finish before returning
233
+
234
+ """
235
+ self._event_loop_thread_id = None
236
+ self._stop_event.set()
237
+ if cancel_remaining:
238
+ self._task_group.cancel_scope.cancel("the blocking portal is shutting down")
239
+
240
+ async def _call_func(
241
+ self,
242
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
243
+ args: tuple[Unpack[PosArgsT]],
244
+ kwargs: dict[str, Any],
245
+ future: Future[T_Retval],
246
+ ) -> None:
247
+ event_loop_thread_id = self._event_loop_thread_id
248
+
249
+ def callback(f: Future[T_Retval]) -> None:
250
+ if f.cancelled():
251
+ if event_loop_thread_id == get_ident():
252
+ scope.cancel("the future was cancelled")
253
+ elif event_loop_thread_id is not None:
254
+ run_sync(
255
+ scope.cancel, "the future was cancelled", token=self._token
256
+ )
257
+
258
+ try:
259
+ retval_or_awaitable = func(*args, **kwargs)
260
+ if isawaitable(retval_or_awaitable):
261
+ with CancelScope() as scope:
262
+ future.add_done_callback(callback)
263
+ retval = await retval_or_awaitable
264
+ else:
265
+ retval = retval_or_awaitable
266
+ except get_cancelled_exc_class():
267
+ future.cancel()
268
+ future.set_running_or_notify_cancel()
269
+ except BaseException as exc:
270
+ if not future.cancelled():
271
+ future.set_exception(exc)
272
+
273
+ # Let base exceptions fall through
274
+ if not isinstance(exc, Exception):
275
+ raise
276
+ else:
277
+ if not future.cancelled():
278
+ future.set_result(retval)
279
+ finally:
280
+ scope = None # type: ignore[assignment]
281
+
282
+ def _spawn_task_from_thread(
283
+ self,
284
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
285
+ args: tuple[Unpack[PosArgsT]],
286
+ kwargs: dict[str, Any],
287
+ name: object,
288
+ future: Future[T_Retval],
289
+ ) -> None:
290
+ """
291
+ Spawn a new task using the given callable.
292
+
293
+ :param func: a callable
294
+ :param args: positional arguments to be passed to the callable
295
+ :param kwargs: keyword arguments to be passed to the callable
296
+ :param name: name of the task (will be coerced to a string if not ``None``)
297
+ :param future: a future that will resolve to the return value of the callable,
298
+ or the exception raised during its execution
299
+
300
+ """
301
+ run_sync(
302
+ partial(self._task_group.start_soon, name=name),
303
+ self._call_func,
304
+ func,
305
+ args,
306
+ kwargs,
307
+ future,
308
+ token=self._token,
309
+ )
310
+
311
+ @overload
312
+ def call(
313
+ self,
314
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
315
+ *args: Unpack[PosArgsT],
316
+ ) -> T_Retval: ...
317
+
318
+ @overload
319
+ def call(
320
+ self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT]
321
+ ) -> T_Retval: ...
322
+
323
+ def call(
324
+ self,
325
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
326
+ *args: Unpack[PosArgsT],
327
+ ) -> T_Retval:
328
+ """
329
+ Call the given function in the event loop thread.
330
+
331
+ If the callable returns a coroutine object, it is awaited on.
332
+
333
+ :param func: any callable
334
+ :raises RuntimeError: if the portal is not running or if this method is called
335
+ from within the event loop thread
336
+
337
+ """
338
+ return cast(T_Retval, self.start_task_soon(func, *args).result())
339
+
340
+ @overload
341
+ def start_task_soon(
342
+ self,
343
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
344
+ *args: Unpack[PosArgsT],
345
+ name: object = None,
346
+ ) -> Future[T_Retval]: ...
347
+
348
+ @overload
349
+ def start_task_soon(
350
+ self,
351
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
352
+ *args: Unpack[PosArgsT],
353
+ name: object = None,
354
+ ) -> Future[T_Retval]: ...
355
+
356
+ def start_task_soon(
357
+ self,
358
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
359
+ *args: Unpack[PosArgsT],
360
+ name: object = None,
361
+ ) -> Future[T_Retval]:
362
+ """
363
+ Start a task in the portal's task group.
364
+
365
+ The task will be run inside a cancel scope which can be cancelled by cancelling
366
+ the returned future.
367
+
368
+ :param func: the target function
369
+ :param args: positional arguments passed to ``func``
370
+ :param name: name of the task (will be coerced to a string if not ``None``)
371
+ :return: a future that resolves with the return value of the callable if the
372
+ task completes successfully, or with the exception raised in the task
373
+ :raises RuntimeError: if the portal is not running or if this method is called
374
+ from within the event loop thread
375
+ :rtype: concurrent.futures.Future[T_Retval]
376
+
377
+ .. versionadded:: 3.0
378
+
379
+ """
380
+ self._check_running()
381
+ f: Future[T_Retval] = Future()
382
+ self._spawn_task_from_thread(func, args, {}, name, f)
383
+ return f
384
+
385
+ def start_task(
386
+ self,
387
+ func: Callable[..., Awaitable[T_Retval]],
388
+ *args: object,
389
+ name: object = None,
390
+ ) -> tuple[Future[T_Retval], Any]:
391
+ """
392
+ Start a task in the portal's task group and wait until it signals for readiness.
393
+
394
+ This method works the same way as :meth:`.abc.TaskGroup.start`.
395
+
396
+ :param func: the target function
397
+ :param args: positional arguments passed to ``func``
398
+ :param name: name of the task (will be coerced to a string if not ``None``)
399
+ :return: a tuple of (future, task_status_value) where the ``task_status_value``
400
+ is the value passed to ``task_status.started()`` from within the target
401
+ function
402
+ :rtype: tuple[concurrent.futures.Future[T_Retval], Any]
403
+
404
+ .. versionadded:: 3.0
405
+
406
+ """
407
+
408
+ def task_done(future: Future[T_Retval]) -> None:
409
+ if not task_status_future.done():
410
+ if future.cancelled():
411
+ task_status_future.cancel()
412
+ elif future.exception():
413
+ task_status_future.set_exception(future.exception())
414
+ else:
415
+ exc = RuntimeError(
416
+ "Task exited without calling task_status.started()"
417
+ )
418
+ task_status_future.set_exception(exc)
419
+
420
+ self._check_running()
421
+ task_status_future: Future = Future()
422
+ task_status = _BlockingPortalTaskStatus(task_status_future)
423
+ f: Future = Future()
424
+ f.add_done_callback(task_done)
425
+ self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f)
426
+ return f, task_status_future.result()
427
+
428
+ def wrap_async_context_manager(
429
+ self, cm: AbstractAsyncContextManager[T_co]
430
+ ) -> AbstractContextManager[T_co]:
431
+ """
432
+ Wrap an async context manager as a synchronous context manager via this portal.
433
+
434
+ Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping
435
+ in the middle until the synchronous context manager exits.
436
+
437
+ :param cm: an asynchronous context manager
438
+ :return: a synchronous context manager
439
+
440
+ .. versionadded:: 2.1
441
+
442
+ """
443
+ return _BlockingAsyncContextManager(cm, self)
444
+
445
+
446
+ @dataclass
447
+ class BlockingPortalProvider:
448
+ """
449
+ A manager for a blocking portal. Used as a context manager. The first thread to
450
+ enter this context manager causes a blocking portal to be started with the specific
451
+ parameters, and the last thread to exit causes the portal to be shut down. Thus,
452
+ there will be exactly one blocking portal running in this context as long as at
453
+ least one thread has entered this context manager.
454
+
455
+ The parameters are the same as for :func:`~anyio.run`.
456
+
457
+ :param backend: name of the backend
458
+ :param backend_options: backend options
459
+
460
+ .. versionadded:: 4.4
461
+ """
462
+
463
+ backend: str = "asyncio"
464
+ backend_options: dict[str, Any] | None = None
465
+ _lock: Lock = field(init=False, default_factory=Lock)
466
+ _leases: int = field(init=False, default=0)
467
+ _portal: BlockingPortal = field(init=False)
468
+ _portal_cm: AbstractContextManager[BlockingPortal] | None = field(
469
+ init=False, default=None
470
+ )
471
+
472
+ def __enter__(self) -> BlockingPortal:
473
+ with self._lock:
474
+ if self._portal_cm is None:
475
+ self._portal_cm = start_blocking_portal(
476
+ self.backend, self.backend_options
477
+ )
478
+ self._portal = self._portal_cm.__enter__()
479
+
480
+ self._leases += 1
481
+ return self._portal
482
+
483
+ def __exit__(
484
+ self,
485
+ exc_type: type[BaseException] | None,
486
+ exc_val: BaseException | None,
487
+ exc_tb: TracebackType | None,
488
+ ) -> None:
489
+ portal_cm: AbstractContextManager[BlockingPortal] | None = None
490
+ with self._lock:
491
+ assert self._portal_cm
492
+ assert self._leases > 0
493
+ self._leases -= 1
494
+ if not self._leases:
495
+ portal_cm = self._portal_cm
496
+ self._portal_cm = None
497
+ del self._portal
498
+
499
+ if portal_cm:
500
+ portal_cm.__exit__(None, None, None)
501
+
502
+
503
+ @contextmanager
504
+ def start_blocking_portal(
505
+ backend: str = "asyncio",
506
+ backend_options: dict[str, Any] | None = None,
507
+ *,
508
+ name: str | None = None,
509
+ ) -> Generator[BlockingPortal, Any, None]:
510
+ """
511
+ Start a new event loop in a new thread and run a blocking portal in its main task.
512
+
513
+ The parameters are the same as for :func:`~anyio.run`.
514
+
515
+ :param backend: name of the backend
516
+ :param backend_options: backend options
517
+ :param name: name of the thread
518
+ :return: a context manager that yields a blocking portal
519
+
520
+ .. versionchanged:: 3.0
521
+ Usage as a context manager is now required.
522
+
523
+ """
524
+
525
+ async def run_portal() -> None:
526
+ async with BlockingPortal() as portal_:
527
+ if name is None:
528
+ current_thread().name = f"{backend}-portal-{id(portal_):x}"
529
+
530
+ future.set_result(portal_)
531
+ await portal_.sleep_until_stopped()
532
+
533
+ def run_blocking_portal() -> None:
534
+ if future.set_running_or_notify_cancel():
535
+ try:
536
+ run_eventloop(
537
+ run_portal, backend=backend, backend_options=backend_options
538
+ )
539
+ except BaseException as exc:
540
+ if not future.done():
541
+ future.set_exception(exc)
542
+
543
+ future: Future[BlockingPortal] = Future()
544
+ thread = Thread(target=run_blocking_portal, daemon=True, name=name)
545
+ thread.start()
546
+ try:
547
+ cancel_remaining_tasks = False
548
+ portal = future.result()
549
+ try:
550
+ yield portal
551
+ except BaseException:
552
+ cancel_remaining_tasks = True
553
+ raise
554
+ finally:
555
+ try:
556
+ portal.call(portal.stop, cancel_remaining_tasks)
557
+ except RuntimeError:
558
+ pass
559
+ finally:
560
+ thread.join()
561
+
562
+
563
+ def check_cancelled() -> None:
564
+ """
565
+ Check if the cancel scope of the host task's running the current worker thread has
566
+ been cancelled.
567
+
568
+ If the host task's current cancel scope has indeed been cancelled, the
569
+ backend-specific cancellation exception will be raised.
570
+
571
+ :raises RuntimeError: if the current thread was not spawned by
572
+ :func:`.to_thread.run_sync`
573
+
574
+ """
575
+ try:
576
+ token: EventLoopToken = threadlocals.current_token
577
+ except AttributeError:
578
+ raise NoEventLoopError(
579
+ "This function can only be called inside an AnyIO worker thread"
580
+ ) from None
581
+
582
+ token.backend_class.check_cancelled()