ez-a-sync 0.22.5__tar.gz → 0.22.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ez-a-sync might be problematic. Click here for more details.
- ez_a_sync-0.22.7/.sourcery.yaml +85 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/PKG-INFO +1 -1
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/_typing.py +35 -5
- ez_a_sync-0.22.7/a_sync/a_sync/_descriptor.py +219 -0
- ez_a_sync-0.22.7/a_sync/a_sync/_flags.py +59 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/_helpers.py +29 -0
- ez_a_sync-0.22.7/a_sync/a_sync/_kwargs.py +44 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/abstract.py +2 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/base.py +48 -8
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/config.py +16 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/decorator.py +98 -85
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/function.py +392 -33
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/method.py +336 -1
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/property.py +69 -4
- ez_a_sync-0.22.7/a_sync/a_sync/singleton.py +36 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/asyncio/as_completed.py +3 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/asyncio/gather.py +29 -17
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/exceptions.py +20 -10
- ez_a_sync-0.22.7/a_sync/iter.py +477 -0
- ez_a_sync-0.22.7/a_sync/primitives/_debug.py +82 -0
- ez_a_sync-0.22.7/a_sync/primitives/locks/counter.py +144 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/locks/semaphore.py +101 -5
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/task.py +21 -2
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/utils/__init__.py +35 -5
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/utils/iterators.py +9 -12
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/conf.py +2 -1
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/ez_a_sync.egg-info/PKG-INFO +1 -1
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/ez_a_sync.egg-info/SOURCES.txt +2 -0
- ez_a_sync-0.22.7/tests/test_abstract.py +17 -0
- ez_a_sync-0.22.7/tests/test_base.py +235 -0
- ez_a_sync-0.22.7/tests/test_iter.py +294 -0
- ez_a_sync-0.22.5/a_sync/a_sync/_descriptor.py +0 -62
- ez_a_sync-0.22.5/a_sync/a_sync/_flags.py +0 -26
- ez_a_sync-0.22.5/a_sync/a_sync/_kwargs.py +0 -18
- ez_a_sync-0.22.5/a_sync/a_sync/singleton.py +0 -12
- ez_a_sync-0.22.5/a_sync/iter.py +0 -276
- ez_a_sync-0.22.5/a_sync/primitives/_debug.py +0 -30
- ez_a_sync-0.22.5/a_sync/primitives/locks/counter.py +0 -73
- ez_a_sync-0.22.5/tests/test_base.py +0 -99
- ez_a_sync-0.22.5/tests/test_iter.py +0 -106
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.coverage +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.github/workflows/codeql.yaml +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.github/workflows/docs.yaml +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.github/workflows/mypy.yaml +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.github/workflows/pytest.yaml +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.github/workflows/release.yaml +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/.gitignore +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/Makefile +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/README.md +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/TODO +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/ENVIRONMENT_VARIABLES.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/_smart.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/_meta.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/cache/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/cache/memory.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/limiter.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/manager.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/a_sync/modifiers/semaphores.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/aliases.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/asyncio/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/asyncio/create_task.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/asyncio/utils.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/executor.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/future.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/_loggable.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/locks/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/locks/event.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/locks/prio_semaphore.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/primitives/queue.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/py.typed +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/sphinx/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/a_sync/sphinx/ext.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/Makefile +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/alabaster.css +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/basic.css +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/custom.css +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/doctools.js +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/documentation_options.js +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/file.png +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/language_data.js +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/minus.png +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/plus.png +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/pygments.css +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/searchtools.js +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/_build/html/_static/sphinx_highlight.js +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/index.rst +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/docs/make.bat +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/ez_a_sync.egg-info/dependency_links.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/ez_a_sync.egg-info/requires.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/ez_a_sync.egg-info/top_level.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/requirements-dev.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/requirements.txt +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/setup.cfg +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/setup.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/__init__.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/conftest.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/executor.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/fixtures.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_as_completed.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_cache.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_decorator.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_executor.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_future.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_gather.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_helpers.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_limiter.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_meta.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_modified.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_semaphore.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_singleton.py +0 -0
- {ez_a_sync-0.22.5 → ez_a_sync-0.22.7}/tests/test_task.py +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# 🪄 This is your project's Sourcery configuration file.
|
|
2
|
+
|
|
3
|
+
# You can use it to get Sourcery working in the way you want, such as
|
|
4
|
+
# ignoring specific refactorings, skipping directories in your project,
|
|
5
|
+
# or writing custom rules.
|
|
6
|
+
|
|
7
|
+
# 📚 For a complete reference to this file, see the documentation at
|
|
8
|
+
# https://docs.sourcery.ai/Configuration/Project-Settings/
|
|
9
|
+
|
|
10
|
+
# This file was auto-generated by Sourcery on 2024-09-06 at 01:04.
|
|
11
|
+
|
|
12
|
+
version: '1' # The schema version of this config file
|
|
13
|
+
|
|
14
|
+
ignore: # A list of paths or files which Sourcery will ignore.
|
|
15
|
+
- .git
|
|
16
|
+
- env
|
|
17
|
+
- .env
|
|
18
|
+
- .tox
|
|
19
|
+
- node_modules
|
|
20
|
+
- vendor
|
|
21
|
+
- venv
|
|
22
|
+
- .venv
|
|
23
|
+
- ~/.pyenv
|
|
24
|
+
- ~/.rye
|
|
25
|
+
- ~/.vscode
|
|
26
|
+
- .vscode
|
|
27
|
+
- ~/.cache
|
|
28
|
+
- ~/.config
|
|
29
|
+
- ~/.local
|
|
30
|
+
|
|
31
|
+
rule_settings:
|
|
32
|
+
enable:
|
|
33
|
+
- default
|
|
34
|
+
disable: [
|
|
35
|
+
no-conditionals-in-tests,
|
|
36
|
+
no-loop-in-tests,
|
|
37
|
+
dict-assign-update-to-union, # we want to be backward compatible with many pythons
|
|
38
|
+
] # A list of rule IDs Sourcery will never suggest.
|
|
39
|
+
rule_types:
|
|
40
|
+
- refactoring
|
|
41
|
+
- suggestion
|
|
42
|
+
- comment
|
|
43
|
+
python_version: '3.9' # A string specifying the lowest Python version your project supports. Sourcery will not suggest refactorings requiring a higher Python version.
|
|
44
|
+
|
|
45
|
+
# rules: # A list of custom rules Sourcery will include in its analysis.
|
|
46
|
+
# - id: no-print-statements
|
|
47
|
+
# description: Do not use print statements in the test directory.
|
|
48
|
+
# pattern: print(...)
|
|
49
|
+
# language: python
|
|
50
|
+
# replacement:
|
|
51
|
+
# condition:
|
|
52
|
+
# explanation:
|
|
53
|
+
# paths:
|
|
54
|
+
# include:
|
|
55
|
+
# - test
|
|
56
|
+
# exclude:
|
|
57
|
+
# - conftest.py
|
|
58
|
+
# tests: []
|
|
59
|
+
# tags: []
|
|
60
|
+
|
|
61
|
+
# rule_tags: {} # Additional rule tags.
|
|
62
|
+
|
|
63
|
+
# metrics:
|
|
64
|
+
# quality_threshold: 25.0
|
|
65
|
+
|
|
66
|
+
# github:
|
|
67
|
+
# labels: []
|
|
68
|
+
# ignore_labels:
|
|
69
|
+
# - sourcery-ignore
|
|
70
|
+
# request_review: author
|
|
71
|
+
# sourcery_branch: sourcery/{base_branch}
|
|
72
|
+
|
|
73
|
+
# clone_detection:
|
|
74
|
+
# min_lines: 3
|
|
75
|
+
# min_duplicates: 2
|
|
76
|
+
# identical_clones_only: false
|
|
77
|
+
|
|
78
|
+
# proxy:
|
|
79
|
+
# url:
|
|
80
|
+
# ssl_certs_file:
|
|
81
|
+
# no_ssl_verify: false
|
|
82
|
+
|
|
83
|
+
# coding_assistant:
|
|
84
|
+
# project_description: ''
|
|
85
|
+
# enabled: true
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides type definitions and type-related utilities for the a_sync library.
|
|
3
|
-
It includes various type aliases, protocols, and TypedDicts used throughout the library
|
|
3
|
+
It includes various type aliases, protocols, and TypedDicts used throughout the library
|
|
4
|
+
to enhance type checking and provide better IDE support.
|
|
4
5
|
"""
|
|
5
6
|
|
|
6
7
|
import asyncio
|
|
@@ -24,15 +25,19 @@ T = TypeVar("T")
|
|
|
24
25
|
K = TypeVar("K")
|
|
25
26
|
V = TypeVar("V")
|
|
26
27
|
I = TypeVar("I")
|
|
28
|
+
"""A :class:`TypeVar` that is used to represent instances of a common class."""
|
|
29
|
+
|
|
27
30
|
E = TypeVar('E', bound=Exception)
|
|
28
31
|
TYPE = TypeVar("TYPE", bound=Type)
|
|
32
|
+
|
|
29
33
|
P = ParamSpec("P")
|
|
34
|
+
"""A :class:`ParamSpec` used everywhere in the lib."""
|
|
30
35
|
|
|
31
36
|
Numeric = Union[int, float, Decimal]
|
|
32
|
-
"Type alias for numeric values int, float,
|
|
37
|
+
"""Type alias for numeric values of types int, float, or Decimal."""
|
|
33
38
|
|
|
34
39
|
MaybeAwaitable = Union[Awaitable[T], T]
|
|
35
|
-
"Type alias for values that may or may not be awaitable."
|
|
40
|
+
"""Type alias for values that may or may not be awaitable. Useful for functions that can return either an awaitable or a direct value."""
|
|
36
41
|
|
|
37
42
|
MaybeCoro = Union[Coroutine[Any, Any, T], T]
|
|
38
43
|
"Type alias for values that may or may not be coroutine."
|
|
@@ -41,7 +46,7 @@ CoroFn = Callable[P, Awaitable[T]]
|
|
|
41
46
|
"Type alias for any function that returns an awaitable."
|
|
42
47
|
|
|
43
48
|
SyncFn = Callable[P, T]
|
|
44
|
-
"Type alias for synchronous functions."
|
|
49
|
+
"""Type alias for synchronous functions."""
|
|
45
50
|
|
|
46
51
|
AnyFn = Union[CoroFn[P, T], SyncFn[P, T]]
|
|
47
52
|
"Type alias for any function, whether synchronous or asynchronous."
|
|
@@ -49,6 +54,14 @@ AnyFn = Union[CoroFn[P, T], SyncFn[P, T]]
|
|
|
49
54
|
class CoroBoundMethod(Protocol[I, P, T]):
|
|
50
55
|
"""
|
|
51
56
|
Protocol for coroutine bound methods.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
class MyClass:
|
|
60
|
+
async def my_method(self, x: int) -> str:
|
|
61
|
+
return str(x)
|
|
62
|
+
|
|
63
|
+
instance = MyClass()
|
|
64
|
+
bound_method: CoroBoundMethod[MyClass, [int], str] = instance.my_method
|
|
52
65
|
"""
|
|
53
66
|
__self__: I
|
|
54
67
|
__call__: Callable[P, Awaitable[T]]
|
|
@@ -56,6 +69,14 @@ class CoroBoundMethod(Protocol[I, P, T]):
|
|
|
56
69
|
class SyncBoundMethod(Protocol[I, P, T]):
|
|
57
70
|
"""
|
|
58
71
|
Protocol for synchronous bound methods.
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
class MyClass:
|
|
75
|
+
def my_method(self, x: int) -> str:
|
|
76
|
+
return str(x)
|
|
77
|
+
|
|
78
|
+
instance = MyClass()
|
|
79
|
+
bound_method: SyncBoundMethod[MyClass, [int], str] = instance.my_method
|
|
59
80
|
"""
|
|
60
81
|
__self__: I
|
|
61
82
|
__call__: Callable[P, T]
|
|
@@ -67,6 +88,9 @@ AnyBoundMethod = Union[CoroBoundMethod[Any, P, T], SyncBoundMethod[Any, P, T]]
|
|
|
67
88
|
class AsyncUnboundMethod(Protocol[I, P, T]):
|
|
68
89
|
"""
|
|
69
90
|
Protocol for unbound asynchronous methods.
|
|
91
|
+
|
|
92
|
+
An unbound method is a method that hasn't been bound to an instance of a class yet.
|
|
93
|
+
It's essentially the function object itself, before it's accessed through an instance.
|
|
70
94
|
"""
|
|
71
95
|
__get__: Callable[[I, Type], CoroBoundMethod[I, P, T]]
|
|
72
96
|
|
|
@@ -74,6 +98,9 @@ class AsyncUnboundMethod(Protocol[I, P, T]):
|
|
|
74
98
|
class SyncUnboundMethod(Protocol[I, P, T]):
|
|
75
99
|
"""
|
|
76
100
|
Protocol for unbound synchronous methods.
|
|
101
|
+
|
|
102
|
+
An unbound method is a method that hasn't been bound to an instance of a class yet.
|
|
103
|
+
It's essentially the function object itself, before it's accessed through an instance.
|
|
77
104
|
"""
|
|
78
105
|
__get__: Callable[[I, Type], SyncBoundMethod[I, P, T]]
|
|
79
106
|
|
|
@@ -122,4 +149,7 @@ AnyIterable = Union[AsyncIterable[K], Iterable[K]]
|
|
|
122
149
|
"Type alias for any iterable, whether synchronous or asynchronous."
|
|
123
150
|
|
|
124
151
|
AnyIterableOrAwaitableIterable = Union[AnyIterable[K], Awaitable[AnyIterable[K]]]
|
|
125
|
-
"
|
|
152
|
+
"""
|
|
153
|
+
Type alias for any iterable, whether synchronous or asynchronous,
|
|
154
|
+
or an awaitable that resolves to any iterable, whether synchronous or asynchronous.
|
|
155
|
+
"""
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the ASyncDescriptor class, which is used to create sync/async methods
|
|
3
|
+
and properties.
|
|
4
|
+
|
|
5
|
+
It also includes utility methods for mapping operations across multiple instances.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import functools
|
|
9
|
+
|
|
10
|
+
from a_sync._typing import *
|
|
11
|
+
from a_sync.a_sync import decorator
|
|
12
|
+
from a_sync.a_sync.function import ASyncFunction, ModifiedMixin, ModifierManager
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from a_sync import TaskMapping
|
|
16
|
+
|
|
17
|
+
class ASyncDescriptor(ModifiedMixin, Generic[I, P, T]):
|
|
18
|
+
"""
|
|
19
|
+
A descriptor base class for asynchronous methods and properties.
|
|
20
|
+
|
|
21
|
+
This class provides functionality for mapping operations across multiple instances
|
|
22
|
+
and includes utility methods for common operations.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__wrapped__: AnyFn[Concatenate[I, P], T]
|
|
26
|
+
"""The wrapped function or method."""
|
|
27
|
+
|
|
28
|
+
__slots__ = "field_name", "_fget"
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
_fget: AnyFn[Concatenate[I, P], T],
|
|
33
|
+
field_name: Optional[str] = None,
|
|
34
|
+
**modifiers: ModifierKwargs,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Initialize the {cls}.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
_fget: The function to be wrapped.
|
|
41
|
+
field_name: Optional name for the field. If not provided, the function's name will be used.
|
|
42
|
+
**modifiers: Additional modifier arguments.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ValueError: If _fget is not callable.
|
|
46
|
+
"""
|
|
47
|
+
if not callable(_fget):
|
|
48
|
+
raise ValueError(f'Unable to decorate {_fget}')
|
|
49
|
+
self.modifiers = ModifierManager(modifiers)
|
|
50
|
+
if isinstance(_fget, ASyncFunction):
|
|
51
|
+
self.modifiers.update(_fget.modifiers)
|
|
52
|
+
self.__wrapped__ = _fget
|
|
53
|
+
elif asyncio.iscoroutinefunction(_fget):
|
|
54
|
+
self.__wrapped__: AsyncUnboundMethod[I, P, T] = self.modifiers.apply_async_modifiers(_fget)
|
|
55
|
+
else:
|
|
56
|
+
self.__wrapped__ = _fget
|
|
57
|
+
|
|
58
|
+
self.field_name = field_name or _fget.__name__
|
|
59
|
+
"""The name of the field the {cls} is bound to."""
|
|
60
|
+
|
|
61
|
+
functools.update_wrapper(self, self.__wrapped__)
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
return f"<{self.__class__.__name__} for {self.__wrapped__}>"
|
|
65
|
+
|
|
66
|
+
def __set_name__(self, owner, name):
|
|
67
|
+
"""
|
|
68
|
+
Set the field name when the {cls} is assigned to a class.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
owner: The class owning this descriptor.
|
|
72
|
+
name: The name assigned to this descriptor in the class.
|
|
73
|
+
"""
|
|
74
|
+
self.field_name = name
|
|
75
|
+
|
|
76
|
+
def map(self, *instances: AnyIterable[I], **bound_method_kwargs: P.kwargs) -> "TaskMapping[I, T]":
|
|
77
|
+
"""
|
|
78
|
+
Create a TaskMapping for the given instances.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
*instances: Iterable of instances to map over.
|
|
82
|
+
**bound_method_kwargs: Additional keyword arguments for the bound method.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
A TaskMapping object.
|
|
86
|
+
"""
|
|
87
|
+
from a_sync.task import TaskMapping
|
|
88
|
+
return TaskMapping(self, *instances, **bound_method_kwargs)
|
|
89
|
+
|
|
90
|
+
@functools.cached_property
|
|
91
|
+
def all(self) -> ASyncFunction[Concatenate[AnyIterable[I], P], bool]:
|
|
92
|
+
"""
|
|
93
|
+
Create an :class:`~ASyncFunction` that checks if all results are truthy.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
An ASyncFunction object.
|
|
97
|
+
"""
|
|
98
|
+
return decorator.a_sync(default=self.default)(self._all)
|
|
99
|
+
|
|
100
|
+
@functools.cached_property
|
|
101
|
+
def any(self) -> ASyncFunction[Concatenate[AnyIterable[I], P], bool]:
|
|
102
|
+
"""
|
|
103
|
+
Create an :class:`~ASyncFunction` that checks if any result is truthy.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
An ASyncFunction object.
|
|
107
|
+
"""
|
|
108
|
+
return decorator.a_sync(default=self.default)(self._any)
|
|
109
|
+
|
|
110
|
+
@functools.cached_property
|
|
111
|
+
def min(self) -> ASyncFunction[Concatenate[AnyIterable[I], P], T]:
|
|
112
|
+
"""
|
|
113
|
+
Create an :class:`~ASyncFunction` that returns the minimum result.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
An ASyncFunction object.
|
|
117
|
+
"""
|
|
118
|
+
return decorator.a_sync(default=self.default)(self._min)
|
|
119
|
+
|
|
120
|
+
@functools.cached_property
|
|
121
|
+
def max(self) -> ASyncFunction[Concatenate[AnyIterable[I], P], T]:
|
|
122
|
+
"""
|
|
123
|
+
Create an :class:`~ASyncFunction` that returns the maximum result.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
An ASyncFunction object.
|
|
127
|
+
"""
|
|
128
|
+
return decorator.a_sync(default=self.default)(self._max)
|
|
129
|
+
|
|
130
|
+
@functools.cached_property
|
|
131
|
+
def sum(self) -> ASyncFunction[Concatenate[AnyIterable[I], P], T]:
|
|
132
|
+
"""
|
|
133
|
+
Create an :class:`~ASyncFunction` that returns the sum of results.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
An ASyncFunction object.
|
|
137
|
+
"""
|
|
138
|
+
return decorator.a_sync(default=self.default)(self._sum)
|
|
139
|
+
|
|
140
|
+
async def _all(self, *instances: AnyIterable[I], concurrency: Optional[int] = None, name: str = "", **kwargs: P.kwargs) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
Check if all results are truthy.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
*instances: Iterable of instances to check.
|
|
146
|
+
concurrency: Optional maximum number of concurrent tasks.
|
|
147
|
+
name: Optional name for the task.
|
|
148
|
+
**kwargs: Additional keyword arguments.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
A boolean indicating if all results are truthy.
|
|
152
|
+
"""
|
|
153
|
+
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).all(pop=True, sync=False)
|
|
154
|
+
|
|
155
|
+
async def _any(self, *instances: AnyIterable[I], concurrency: Optional[int] = None, name: str = "", **kwargs: P.kwargs) -> bool:
|
|
156
|
+
"""
|
|
157
|
+
Check if any result is truthy.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
*instances: Iterable of instances to check.
|
|
161
|
+
concurrency: Optional maximum number of concurrent tasks.
|
|
162
|
+
name: Optional name for the task.
|
|
163
|
+
**kwargs: Additional keyword arguments.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
A boolean indicating if any result is truthy.
|
|
167
|
+
"""
|
|
168
|
+
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).any(pop=True, sync=False)
|
|
169
|
+
|
|
170
|
+
async def _min(self, *instances: AnyIterable[I], concurrency: Optional[int] = None, name: str = "", **kwargs: P.kwargs) -> T:
|
|
171
|
+
"""
|
|
172
|
+
Find the minimum result.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
*instances: Iterable of instances to check.
|
|
176
|
+
concurrency: Optional maximum number of concurrent tasks.
|
|
177
|
+
name: Optional name for the task.
|
|
178
|
+
**kwargs: Additional keyword arguments.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
The minimum result.
|
|
182
|
+
"""
|
|
183
|
+
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).min(pop=True, sync=False)
|
|
184
|
+
|
|
185
|
+
async def _max(self, *instances: AnyIterable[I], concurrency: Optional[int] = None, name: str = "", **kwargs: P.kwargs) -> T:
|
|
186
|
+
"""
|
|
187
|
+
Find the maximum result.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
*instances: Iterable of instances to check.
|
|
191
|
+
concurrency: Optional maximum number of concurrent tasks.
|
|
192
|
+
name: Optional name for the task.
|
|
193
|
+
**kwargs: Additional keyword arguments.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
The maximum result.
|
|
197
|
+
"""
|
|
198
|
+
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).max(pop=True, sync=False)
|
|
199
|
+
|
|
200
|
+
async def _sum(self, *instances: AnyIterable[I], concurrency: Optional[int] = None, name: str = "", **kwargs: P.kwargs) -> T:
|
|
201
|
+
"""
|
|
202
|
+
Calculate the sum of results.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
*instances: Iterable of instances to sum.
|
|
206
|
+
concurrency: Optional maximum number of concurrent tasks.
|
|
207
|
+
name: Optional name for the task.
|
|
208
|
+
**kwargs: Additional keyword arguments.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
The sum of the results.
|
|
212
|
+
"""
|
|
213
|
+
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).sum(pop=True, sync=False)
|
|
214
|
+
|
|
215
|
+
def __init_subclass__(cls) -> None:
|
|
216
|
+
for attr in cls.__dict__.values():
|
|
217
|
+
if attr.__doc__ and "{cls}" in attr.__doc__:
|
|
218
|
+
attr.__doc__ = attr.__doc__.replace("{cls}", f":class:`{cls.__name__}`")
|
|
219
|
+
return super().__init_subclass__()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides functionality for handling synchronous and asynchronous flags
|
|
3
|
+
in the ez-a-sync library.
|
|
4
|
+
|
|
5
|
+
ez-a-sync uses 'flags' to indicate whether objects / function calls will be sync or async.
|
|
6
|
+
|
|
7
|
+
You can use any of the provided flags, whichever makes most sense for your use case.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from a_sync import exceptions
|
|
13
|
+
|
|
14
|
+
AFFIRMATIVE_FLAGS = {'sync'}
|
|
15
|
+
"""Set of flags indicating synchronous behavior."""
|
|
16
|
+
|
|
17
|
+
NEGATIVE_FLAGS = {'asynchronous'}
|
|
18
|
+
"""Set of flags indicating asynchronous behavior."""
|
|
19
|
+
|
|
20
|
+
VIABLE_FLAGS = AFFIRMATIVE_FLAGS | NEGATIVE_FLAGS
|
|
21
|
+
"""Set of all valid flags."""
|
|
22
|
+
|
|
23
|
+
def negate_if_necessary(flag: str, flag_value: bool) -> bool:
|
|
24
|
+
"""Negate the flag value if necessary based on the flag type.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
flag: The flag to check.
|
|
28
|
+
flag_value: The value of the flag.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The potentially negated flag value.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
:class:`exceptions.InvalidFlag`: If the flag is not recognized.
|
|
35
|
+
"""
|
|
36
|
+
validate_flag_value(flag, flag_value)
|
|
37
|
+
if flag in AFFIRMATIVE_FLAGS:
|
|
38
|
+
return bool(flag_value)
|
|
39
|
+
elif flag in NEGATIVE_FLAGS:
|
|
40
|
+
return bool(not flag_value)
|
|
41
|
+
raise exceptions.InvalidFlag(flag)
|
|
42
|
+
|
|
43
|
+
def validate_flag_value(flag: str, flag_value: Any) -> bool:
|
|
44
|
+
"""
|
|
45
|
+
Validate that the flag value is a boolean.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
flag: The flag being validated.
|
|
49
|
+
flag_value: The value to validate.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The validated flag value.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
:class:`exceptions.InvalidFlagValue`: If the flag value is not a boolean.
|
|
56
|
+
"""
|
|
57
|
+
if not isinstance(flag_value, bool):
|
|
58
|
+
raise exceptions.InvalidFlagValue(flag, flag_value)
|
|
59
|
+
return flag_value
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utility functions for handling asynchronous operations
|
|
3
|
+
and converting synchronous functions to asynchronous ones.
|
|
4
|
+
"""
|
|
1
5
|
|
|
2
6
|
import asyncio
|
|
3
7
|
import functools
|
|
@@ -9,6 +13,18 @@ from a_sync._typing import *
|
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
def _await(awaitable: Awaitable[T]) -> T:
|
|
16
|
+
"""
|
|
17
|
+
Await an awaitable object in a synchronous context.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
awaitable: The awaitable object to be awaited.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
The result of the awaitable.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
:class:`exceptions.SyncModeInAsyncContextError`: If the event loop is already running.
|
|
27
|
+
"""
|
|
12
28
|
try:
|
|
13
29
|
return a_sync.asyncio.get_event_loop().run_until_complete(awaitable)
|
|
14
30
|
except RuntimeError as e:
|
|
@@ -17,6 +33,19 @@ def _await(awaitable: Awaitable[T]) -> T:
|
|
|
17
33
|
raise
|
|
18
34
|
|
|
19
35
|
def _asyncify(func: SyncFn[P, T], executor: Executor) -> CoroFn[P, T]: # type: ignore [misc]
|
|
36
|
+
"""
|
|
37
|
+
Convert a synchronous function to a coroutine function.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
func: The synchronous function to be converted.
|
|
41
|
+
executor: The executor to run the synchronous function.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A coroutine function wrapping the input function.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
:class:`exceptions.FunctionNotSync`: If the input function is already asynchronous.
|
|
48
|
+
"""
|
|
20
49
|
from a_sync.a_sync.function import ASyncFunction
|
|
21
50
|
if asyncio.iscoroutinefunction(func) or isinstance(func, ASyncFunction):
|
|
22
51
|
raise exceptions.FunctionNotSync(func)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utility functions for handling keyword arguments related to synchronous and asynchronous flags.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from a_sync import exceptions
|
|
8
|
+
from a_sync.a_sync import _flags
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_flag_name(kwargs: dict) -> Optional[str]:
|
|
12
|
+
"""
|
|
13
|
+
Get the name of the flag present in the kwargs.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
kwargs: A dictionary of keyword arguments.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The name of the flag if present, None otherwise.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
:class:`exceptions.TooManyFlags`: If more than one flag is present in the kwargs.
|
|
23
|
+
"""
|
|
24
|
+
present_flags = [flag for flag in _flags.VIABLE_FLAGS if flag in kwargs]
|
|
25
|
+
if len(present_flags) == 0:
|
|
26
|
+
return None
|
|
27
|
+
if len(present_flags) != 1:
|
|
28
|
+
raise exceptions.TooManyFlags('kwargs', present_flags)
|
|
29
|
+
return present_flags[0]
|
|
30
|
+
|
|
31
|
+
def is_sync(flag: str, kwargs: dict, pop_flag: bool = False) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
Determine if the operation should be synchronous based on the flag value.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
flag: The name of the flag to check.
|
|
37
|
+
kwargs: A dictionary of keyword arguments.
|
|
38
|
+
pop_flag: Whether to remove the flag from kwargs. Defaults to False.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if the operation should be synchronous, False otherwise.
|
|
42
|
+
"""
|
|
43
|
+
flag_value = kwargs.pop(flag) if pop_flag else kwargs[flag]
|
|
44
|
+
return _flags.negate_if_necessary(flag, flag_value)
|
|
@@ -29,6 +29,8 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
29
29
|
@functools.cached_property
|
|
30
30
|
def __a_sync_instance_should_await__(self) -> bool:
|
|
31
31
|
"""
|
|
32
|
+
A flag indicating whether the instance should default to asynchronous execution.
|
|
33
|
+
|
|
32
34
|
You can override this if you want.
|
|
33
35
|
If you want to be able to hotswap instance modes, you can redefine this as a non-cached property.
|
|
34
36
|
"""
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import functools
|
|
3
2
|
import inspect
|
|
4
3
|
import logging
|
|
@@ -14,10 +13,51 @@ logger = logging.getLogger(__name__)
|
|
|
14
13
|
|
|
15
14
|
class ASyncGenericBase(ASyncABC):
|
|
16
15
|
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
Base class for creating dual-function sync/async-capable classes without writing all your code twice.
|
|
17
|
+
|
|
18
|
+
This class provides the foundation for creating hybrid sync/async classes. It allows methods
|
|
19
|
+
and properties to be defined once and used in both synchronous and asynchronous contexts.
|
|
20
|
+
|
|
21
|
+
The class uses the :func:`a_sync` decorator internally to create dual-mode methods and properties.
|
|
22
|
+
Subclasses should define their methods as coroutines (using `async def`) where possible, and
|
|
23
|
+
use the `@a_sync.property` or `@a_sync.cached_property` decorators for properties that need to support both modes.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
class MyClass(ASyncGenericBase):
|
|
28
|
+
def __init__(self, sync: bool):
|
|
29
|
+
self.sync = sync
|
|
30
|
+
|
|
31
|
+
@a_sync.property
|
|
32
|
+
async def my_property(self):
|
|
33
|
+
return await some_async_operation()
|
|
34
|
+
|
|
35
|
+
@a_sync
|
|
36
|
+
async def my_method(self):
|
|
37
|
+
return await another_async_operation()
|
|
38
|
+
|
|
39
|
+
# Synchronous usage
|
|
40
|
+
obj = MyClass(sync=True)
|
|
41
|
+
sync_result = obj.my_property
|
|
42
|
+
sync_method_result = obj.my_method()
|
|
43
|
+
|
|
44
|
+
# Asynchronous usage
|
|
45
|
+
obj = MyClass(sync=False)
|
|
46
|
+
async_result = await obj.my_property
|
|
47
|
+
async_method_result = await obj.my_method()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Note:
|
|
51
|
+
When subclassing, be aware that all async methods and properties will be
|
|
52
|
+
automatically wrapped to support both sync and async calls. This allows for
|
|
53
|
+
seamless usage in different contexts without changing the underlying implementation.
|
|
20
54
|
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
if type(self) is ASyncGenericBase:
|
|
58
|
+
cls_name = type(self).__name__
|
|
59
|
+
raise NotImplementedError(f"You should not create instances of `{cls_name}` directly, you should subclass `ASyncGenericBase` instead.")
|
|
60
|
+
|
|
21
61
|
@functools.cached_property
|
|
22
62
|
def __a_sync_flag_name__(self) -> str:
|
|
23
63
|
logger.debug("checking a_sync flag for %s", self)
|
|
@@ -29,10 +69,10 @@ class ASyncGenericBase(ASyncABC):
|
|
|
29
69
|
# Let's check the instance's atributes
|
|
30
70
|
logger.debug("unable to find flag name using `%s.__init__` signature, checking for flag attributes defined on %s", self.__class__.__name__, self)
|
|
31
71
|
present_flags = [flag for flag in _flags.VIABLE_FLAGS if hasattr(self, flag)]
|
|
32
|
-
if
|
|
33
|
-
raise exceptions.NoFlagsFound(self)
|
|
72
|
+
if not present_flags:
|
|
73
|
+
raise exceptions.NoFlagsFound(self) from None
|
|
34
74
|
if len(present_flags) > 1:
|
|
35
|
-
raise exceptions.TooManyFlags(self, present_flags)
|
|
75
|
+
raise exceptions.TooManyFlags(self, present_flags) from None
|
|
36
76
|
flag = present_flags[0]
|
|
37
77
|
if not isinstance(flag, str):
|
|
38
78
|
raise exceptions.InvalidFlag(flag)
|
|
@@ -106,7 +146,7 @@ class ASyncGenericBase(ASyncABC):
|
|
|
106
146
|
@classmethod
|
|
107
147
|
def __parse_flag_name_from_list(cls, items: Dict[str, Any]) -> str:
|
|
108
148
|
present_flags = [flag for flag in _flags.VIABLE_FLAGS if flag in items]
|
|
109
|
-
if
|
|
149
|
+
if not present_flags:
|
|
110
150
|
logger.debug("There are too many flags defined on %s", cls)
|
|
111
151
|
raise exceptions.NoFlagsFound(cls, items.keys())
|
|
112
152
|
if len(present_flags) > 1:
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration module for a_sync library.
|
|
3
|
+
|
|
4
|
+
This module provides configuration options and default settings for the a_sync library.
|
|
5
|
+
It includes functionality for setting up executors, defining default modifiers,
|
|
6
|
+
and handling environment variable configurations.
|
|
7
|
+
"""
|
|
1
8
|
|
|
2
9
|
import functools
|
|
3
10
|
import os
|
|
@@ -11,6 +18,15 @@ EXECUTOR_VALUE = int(os.environ.get("A_SYNC_EXECUTOR_VALUE", 8))
|
|
|
11
18
|
|
|
12
19
|
@functools.lru_cache(maxsize=1)
|
|
13
20
|
def get_default_executor() -> Executor:
|
|
21
|
+
"""
|
|
22
|
+
Get the default executor based on the :obj:`EXECUTOR_TYPE` environment variable.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Executor: An instance of either ProcessPoolExecutor or ThreadPoolExecutor.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
:class:`ValueError`: If an invalid EXECUTOR_TYPE is specified.
|
|
29
|
+
"""
|
|
14
30
|
if EXECUTOR_TYPE.lower().startswith('p'): # p, P, proc, Processes, etc
|
|
15
31
|
return ProcessPoolExecutor(EXECUTOR_VALUE)
|
|
16
32
|
elif EXECUTOR_TYPE.lower().startswith('t'): # t, T, thread, THREADS, etc
|