ez-a-sync 0.22.15__py3-none-any.whl → 0.22.16__py3-none-any.whl
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.
- a_sync/ENVIRONMENT_VARIABLES.py +34 -3
- a_sync/__init__.py +32 -9
- a_sync/_smart.py +105 -6
- a_sync/_typing.py +56 -3
- a_sync/a_sync/_descriptor.py +174 -12
- a_sync/a_sync/_flags.py +64 -3
- a_sync/a_sync/_helpers.py +40 -8
- a_sync/a_sync/_kwargs.py +30 -6
- a_sync/a_sync/_meta.py +35 -6
- a_sync/a_sync/abstract.py +57 -9
- a_sync/a_sync/config.py +44 -7
- a_sync/a_sync/decorator.py +217 -37
- a_sync/a_sync/function.py +339 -47
- a_sync/a_sync/method.py +241 -52
- a_sync/a_sync/modifiers/__init__.py +39 -1
- a_sync/a_sync/modifiers/cache/__init__.py +75 -5
- a_sync/a_sync/modifiers/cache/memory.py +50 -6
- a_sync/a_sync/modifiers/limiter.py +55 -6
- a_sync/a_sync/modifiers/manager.py +46 -2
- a_sync/a_sync/modifiers/semaphores.py +84 -11
- a_sync/a_sync/singleton.py +43 -19
- a_sync/asyncio/__init__.py +137 -1
- a_sync/asyncio/as_completed.py +44 -38
- a_sync/asyncio/create_task.py +46 -10
- a_sync/asyncio/gather.py +72 -25
- a_sync/exceptions.py +178 -11
- a_sync/executor.py +51 -3
- a_sync/future.py +671 -29
- a_sync/iter.py +64 -7
- a_sync/primitives/_debug.py +59 -5
- a_sync/primitives/_loggable.py +36 -6
- a_sync/primitives/locks/counter.py +74 -7
- a_sync/primitives/locks/prio_semaphore.py +87 -8
- a_sync/primitives/locks/semaphore.py +68 -20
- a_sync/primitives/queue.py +65 -26
- a_sync/task.py +51 -15
- a_sync/utils/iterators.py +52 -16
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/executor.py +150 -12
- tests/test_abstract.py +15 -0
- tests/test_base.py +198 -2
- tests/test_executor.py +23 -0
- tests/test_singleton.py +13 -1
- tests/test_task.py +45 -17
- ez_a_sync-0.22.15.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
a_sync/a_sync/_flags.py
CHANGED
|
@@ -11,6 +11,10 @@ AFFIRMATIVE_FLAGS: Set of flags indicating synchronous behavior. Currently inclu
|
|
|
11
11
|
NEGATIVE_FLAGS: Set of flags indicating asynchronous behavior. Currently includes "asynchronous".
|
|
12
12
|
|
|
13
13
|
VIABLE_FLAGS: Set of all valid flags, combining both synchronous and asynchronous indicators.
|
|
14
|
+
|
|
15
|
+
Functions:
|
|
16
|
+
- negate_if_necessary: Negates the flag value if necessary based on the flag type.
|
|
17
|
+
- validate_flag_value: Validates that the flag value is a boolean.
|
|
14
18
|
"""
|
|
15
19
|
|
|
16
20
|
from typing import Any
|
|
@@ -18,18 +22,51 @@ from typing import Any
|
|
|
18
22
|
from a_sync import exceptions
|
|
19
23
|
|
|
20
24
|
AFFIRMATIVE_FLAGS = {"sync"}
|
|
21
|
-
"""Set of flags indicating synchronous behavior.
|
|
25
|
+
"""Set of flags indicating synchronous behavior.
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
>>> 'sync' in AFFIRMATIVE_FLAGS
|
|
29
|
+
True
|
|
30
|
+
|
|
31
|
+
>>> 'async' in AFFIRMATIVE_FLAGS
|
|
32
|
+
False
|
|
33
|
+
"""
|
|
22
34
|
|
|
23
35
|
NEGATIVE_FLAGS = {"asynchronous"}
|
|
24
|
-
"""Set of flags indicating asynchronous behavior.
|
|
36
|
+
"""Set of flags indicating asynchronous behavior.
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
>>> 'asynchronous' in NEGATIVE_FLAGS
|
|
40
|
+
True
|
|
41
|
+
|
|
42
|
+
>>> 'sync' in NEGATIVE_FLAGS
|
|
43
|
+
False
|
|
44
|
+
"""
|
|
25
45
|
|
|
26
46
|
VIABLE_FLAGS = AFFIRMATIVE_FLAGS | NEGATIVE_FLAGS
|
|
27
|
-
"""Set of all valid flags.
|
|
47
|
+
"""Set of all valid flags, combining both synchronous and asynchronous indicators.
|
|
48
|
+
|
|
49
|
+
A-Sync uses 'flags' to indicate whether objects or function calls will be sync or async.
|
|
50
|
+
You can use any of the provided flags, whichever makes most sense for your use case.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
>>> 'sync' in VIABLE_FLAGS
|
|
54
|
+
True
|
|
55
|
+
|
|
56
|
+
>>> 'asynchronous' in VIABLE_FLAGS
|
|
57
|
+
True
|
|
58
|
+
|
|
59
|
+
>>> 'invalid' in VIABLE_FLAGS
|
|
60
|
+
False
|
|
61
|
+
"""
|
|
28
62
|
|
|
29
63
|
|
|
30
64
|
def negate_if_necessary(flag: str, flag_value: bool) -> bool:
|
|
31
65
|
"""Negate the flag value if necessary based on the flag type.
|
|
32
66
|
|
|
67
|
+
This function checks if the provided flag is in the set of affirmative or negative flags
|
|
68
|
+
and negates the flag value accordingly. If the flag is not recognized, it raises an exception.
|
|
69
|
+
|
|
33
70
|
Args:
|
|
34
71
|
flag: The flag to check.
|
|
35
72
|
flag_value: The value of the flag.
|
|
@@ -39,6 +76,16 @@ def negate_if_necessary(flag: str, flag_value: bool) -> bool:
|
|
|
39
76
|
|
|
40
77
|
Raises:
|
|
41
78
|
exceptions.InvalidFlag: If the flag is not recognized.
|
|
79
|
+
|
|
80
|
+
Examples:
|
|
81
|
+
>>> negate_if_necessary('sync', True)
|
|
82
|
+
True
|
|
83
|
+
|
|
84
|
+
>>> negate_if_necessary('asynchronous', True)
|
|
85
|
+
False
|
|
86
|
+
|
|
87
|
+
See Also:
|
|
88
|
+
- :func:`validate_flag_value`: Validates that the flag value is a boolean.
|
|
42
89
|
"""
|
|
43
90
|
validate_flag_value(flag, flag_value)
|
|
44
91
|
if flag in AFFIRMATIVE_FLAGS:
|
|
@@ -52,6 +99,8 @@ def validate_flag_value(flag: str, flag_value: Any) -> bool:
|
|
|
52
99
|
"""
|
|
53
100
|
Validate that the flag value is a boolean.
|
|
54
101
|
|
|
102
|
+
This function ensures that the provided flag value is of type boolean. If not, it raises an exception.
|
|
103
|
+
|
|
55
104
|
Args:
|
|
56
105
|
flag: The flag being validated.
|
|
57
106
|
flag_value: The value to validate.
|
|
@@ -61,6 +110,18 @@ def validate_flag_value(flag: str, flag_value: Any) -> bool:
|
|
|
61
110
|
|
|
62
111
|
Raises:
|
|
63
112
|
exceptions.InvalidFlagValue: If the flag value is not a boolean.
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
>>> validate_flag_value('sync', True)
|
|
116
|
+
True
|
|
117
|
+
|
|
118
|
+
>>> validate_flag_value('asynchronous', 'yes')
|
|
119
|
+
Traceback (most recent call last):
|
|
120
|
+
...
|
|
121
|
+
exceptions.InvalidFlagValue: Invalid flag value for 'asynchronous': 'yes'
|
|
122
|
+
|
|
123
|
+
See Also:
|
|
124
|
+
- :func:`negate_if_necessary`: Negates the flag value if necessary based on the flag type.
|
|
64
125
|
"""
|
|
65
126
|
if not isinstance(flag_value, bool):
|
|
66
127
|
raise exceptions.InvalidFlagValue(flag, flag_value)
|
a_sync/a_sync/_helpers.py
CHANGED
|
@@ -17,10 +17,21 @@ def _await(awaitable: Awaitable[T]) -> T:
|
|
|
17
17
|
Await an awaitable object in a synchronous context.
|
|
18
18
|
|
|
19
19
|
Args:
|
|
20
|
-
awaitable: The awaitable object to be awaited.
|
|
20
|
+
awaitable (Awaitable[T]): The awaitable object to be awaited.
|
|
21
21
|
|
|
22
22
|
Raises:
|
|
23
23
|
exceptions.SyncModeInAsyncContextError: If the event loop is already running.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
>>> async def example_coroutine():
|
|
27
|
+
... return 42
|
|
28
|
+
...
|
|
29
|
+
>>> result = _await(example_coroutine())
|
|
30
|
+
>>> print(result)
|
|
31
|
+
42
|
|
32
|
+
|
|
33
|
+
See Also:
|
|
34
|
+
- :func:`asyncio.run`: For running the main entry point of an asyncio program.
|
|
24
35
|
"""
|
|
25
36
|
try:
|
|
26
37
|
return a_sync.asyncio.get_event_loop().run_until_complete(awaitable)
|
|
@@ -32,17 +43,38 @@ def _await(awaitable: Awaitable[T]) -> T:
|
|
|
32
43
|
|
|
33
44
|
def _asyncify(func: SyncFn[P, T], executor: Executor) -> CoroFn[P, T]: # type: ignore [misc]
|
|
34
45
|
"""
|
|
35
|
-
Convert a synchronous function to a coroutine function.
|
|
46
|
+
Convert a synchronous function to a coroutine function using an executor.
|
|
36
47
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
This function submits the synchronous function to the provided executor and wraps
|
|
49
|
+
the resulting future in a coroutine function. This allows the synchronous function
|
|
50
|
+
to be executed asynchronously.
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
Note:
|
|
53
|
+
The function `_asyncify` uses `asyncio.futures.wrap_future` to wrap the future
|
|
54
|
+
returned by the executor, specifying the event loop with `a_sync.asyncio.get_event_loop()`.
|
|
55
|
+
Ensure that your environment supports this usage or adjust the import if necessary.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
func (SyncFn[P, T]): The synchronous function to be converted.
|
|
59
|
+
executor (Executor): The executor used to run the synchronous function.
|
|
43
60
|
|
|
44
61
|
Raises:
|
|
45
|
-
exceptions.FunctionNotSync: If the input function is a coroutine function or an instance of ASyncFunction
|
|
62
|
+
exceptions.FunctionNotSync: If the input function is a coroutine function or an instance of :class:`~a_sync.a_sync.function.ASyncFunction`.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
>>> from concurrent.futures import ThreadPoolExecutor
|
|
66
|
+
>>> def sync_function(x):
|
|
67
|
+
... return x * 2
|
|
68
|
+
...
|
|
69
|
+
>>> executor = ThreadPoolExecutor()
|
|
70
|
+
>>> async_function = _asyncify(sync_function, executor)
|
|
71
|
+
>>> result = await async_function(3)
|
|
72
|
+
>>> print(result)
|
|
73
|
+
6
|
|
74
|
+
|
|
75
|
+
See Also:
|
|
76
|
+
- :class:`concurrent.futures.Executor`: For managing pools of threads or processes.
|
|
77
|
+
- :func:`asyncio.to_thread`: For running blocking code in a separate thread.
|
|
46
78
|
"""
|
|
47
79
|
from a_sync.a_sync.function import ASyncFunction
|
|
48
80
|
|
a_sync/a_sync/_kwargs.py
CHANGED
|
@@ -19,7 +19,21 @@ def get_flag_name(kwargs: dict) -> Optional[str]:
|
|
|
19
19
|
The name of the flag if present, None otherwise.
|
|
20
20
|
|
|
21
21
|
Raises:
|
|
22
|
-
:class:`exceptions.TooManyFlags`: If more than one flag is present in the kwargs
|
|
22
|
+
:class:`exceptions.TooManyFlags`: If more than one flag is present in the kwargs,
|
|
23
|
+
the exception includes the message "kwargs" and the list of present flags.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
>>> get_flag_name({'sync': True})
|
|
27
|
+
'sync'
|
|
28
|
+
|
|
29
|
+
>>> get_flag_name({'async': False})
|
|
30
|
+
'async'
|
|
31
|
+
|
|
32
|
+
>>> get_flag_name({})
|
|
33
|
+
None
|
|
34
|
+
|
|
35
|
+
See Also:
|
|
36
|
+
:func:`is_sync`: Determines if the operation should be synchronous based on the flag value.
|
|
23
37
|
"""
|
|
24
38
|
present_flags = [flag for flag in _flags.VIABLE_FLAGS if flag in kwargs]
|
|
25
39
|
if len(present_flags) == 0:
|
|
@@ -34,12 +48,22 @@ def is_sync(flag: str, kwargs: dict, pop_flag: bool = False) -> bool:
|
|
|
34
48
|
Determine if the operation should be synchronous based on the flag value.
|
|
35
49
|
|
|
36
50
|
Args:
|
|
37
|
-
flag: The name of the flag to check.
|
|
38
|
-
kwargs: A dictionary of keyword arguments.
|
|
39
|
-
pop_flag: Whether to remove the flag from kwargs. Defaults to False.
|
|
51
|
+
flag (str): The name of the flag to check.
|
|
52
|
+
kwargs (dict): A dictionary of keyword arguments.
|
|
53
|
+
pop_flag (bool, optional): Whether to remove the flag from kwargs. Defaults to False.
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
Examples:
|
|
56
|
+
>>> is_sync('sync', {'sync': True})
|
|
57
|
+
True
|
|
58
|
+
|
|
59
|
+
>>> is_sync('async', {'async': False})
|
|
60
|
+
False
|
|
61
|
+
|
|
62
|
+
>>> is_sync('sync', {'sync': True}, pop_flag=True)
|
|
63
|
+
True
|
|
64
|
+
|
|
65
|
+
See Also:
|
|
66
|
+
:func:`get_flag_name`: Retrieves the name of the flag present in the kwargs.
|
|
43
67
|
"""
|
|
44
68
|
flag_value = kwargs.pop(flag) if pop_flag else kwargs[flag]
|
|
45
69
|
return _flags.negate_if_necessary(flag, flag_value)
|
a_sync/a_sync/_meta.py
CHANGED
|
@@ -24,11 +24,28 @@ class ASyncMeta(ABCMeta):
|
|
|
24
24
|
|
|
25
25
|
Any class with `ASyncMeta` as its metaclass will have its functions and properties
|
|
26
26
|
wrapped with asynchronous capabilities upon class instantiation. This includes
|
|
27
|
-
wrapping functions with
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
which are used when functions are decorated with a_sync decorators
|
|
31
|
-
|
|
27
|
+
wrapping functions with :class:`~a_sync.a_sync.method.ASyncMethodDescriptor` and properties with
|
|
28
|
+
:class:`~a_sync.a_sync.property.ASyncPropertyDescriptor` or :class:`~a_sync.a_sync.property.ASyncCachedPropertyDescriptor`.
|
|
29
|
+
It also handles attributes that are instances of :class:`~a_sync.a_sync.function.ASyncFunction`,
|
|
30
|
+
which are used when functions are decorated with a_sync decorators to apply specific modifiers to those functions.
|
|
31
|
+
|
|
32
|
+
Attributes that are instances of :class:`~a_sync.future._ASyncFutureWrappedFn` and :class:`~a_sync.primitives.locks.semaphore.Semaphore`
|
|
33
|
+
are explicitly skipped and not wrapped.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
To create a class with asynchronous capabilities, define your class with `ASyncMeta` as its metaclass:
|
|
37
|
+
|
|
38
|
+
>>> class MyClass(metaclass=ASyncMeta):
|
|
39
|
+
... def my_method(self):
|
|
40
|
+
... return "Hello, World!"
|
|
41
|
+
|
|
42
|
+
The `my_method` function will be wrapped with :class:`~a_sync.a_sync.method.ASyncMethodDescriptor`, allowing it to be used asynchronously.
|
|
43
|
+
|
|
44
|
+
See Also:
|
|
45
|
+
- :class:`~a_sync.a_sync.function.ASyncFunction`
|
|
46
|
+
- :class:`~a_sync.a_sync.method.ASyncMethodDescriptor`
|
|
47
|
+
- :class:`~a_sync.a_sync.property.ASyncPropertyDescriptor`
|
|
48
|
+
- :class:`~a_sync.a_sync.property.ASyncCachedPropertyDescriptor`
|
|
32
49
|
"""
|
|
33
50
|
|
|
34
51
|
def __new__(cls, new_class_name, bases, attrs):
|
|
@@ -145,8 +162,20 @@ class ASyncMeta(ABCMeta):
|
|
|
145
162
|
class ASyncSingletonMeta(ASyncMeta):
|
|
146
163
|
"""Metaclass for creating singleton instances with asynchronous capabilities.
|
|
147
164
|
|
|
148
|
-
This metaclass extends
|
|
165
|
+
This metaclass extends :class:`~a_sync.a_sync._meta.ASyncMeta` to ensure that only one instance of a class
|
|
149
166
|
is created for each synchronous or asynchronous context.
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
To create a singleton class with asynchronous capabilities, define your class with `ASyncSingletonMeta` as its metaclass:
|
|
170
|
+
|
|
171
|
+
>>> class MySingleton(metaclass=ASyncSingletonMeta):
|
|
172
|
+
... def __init__(self):
|
|
173
|
+
... print("Instance created")
|
|
174
|
+
|
|
175
|
+
The `MySingleton` class will ensure that only one instance is created for each context.
|
|
176
|
+
|
|
177
|
+
See Also:
|
|
178
|
+
- :class:`~a_sync.a_sync._meta.ASyncMeta`
|
|
150
179
|
"""
|
|
151
180
|
|
|
152
181
|
def __init__(
|
a_sync/a_sync/abstract.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides an abstract base class for defining asynchronous and synchronous behavior.
|
|
3
3
|
|
|
4
|
-
The ASyncABC class uses the ASyncMeta metaclass to
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
default mode for asynchronous or synchronous execution.
|
|
4
|
+
The :class:`ASyncABC` class uses the :class:`ASyncMeta` metaclass to facilitate the creation of classes
|
|
5
|
+
that can operate in both asynchronous and synchronous contexts. It provides concrete methods to determine
|
|
6
|
+
the execution mode based on flags and keyword arguments.
|
|
8
7
|
|
|
9
|
-
Note: It is recommended to use ASyncGenericBase for most use cases. This class
|
|
8
|
+
Note: It is recommended to use :class:`ASyncGenericBase` for most use cases. This class
|
|
10
9
|
is intended for more custom implementations if necessary.
|
|
11
10
|
"""
|
|
12
11
|
|
|
@@ -26,10 +25,35 @@ logger = logging.getLogger(__name__)
|
|
|
26
25
|
class ASyncABC(metaclass=ASyncMeta):
|
|
27
26
|
"""Abstract Base Class for defining asynchronous and synchronous behavior.
|
|
28
27
|
|
|
29
|
-
This class
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
This class provides methods to determine the execution mode based on flags and keyword arguments.
|
|
29
|
+
It is designed to be subclassed, allowing developers to create classes that can be used in both
|
|
30
|
+
synchronous and asynchronous contexts.
|
|
31
|
+
|
|
32
|
+
See Also:
|
|
33
|
+
- :class:`ASyncGenericBase`: A more user-friendly base class for creating dual-mode classes.
|
|
34
|
+
- :class:`ASyncMeta`: Metaclass that facilitates asynchronous capabilities in class attributes.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
To create a class that inherits from `ASyncABC`, you need to implement the abstract methods
|
|
38
|
+
and can override the concrete methods if needed.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
class MyASyncClass(ASyncABC):
|
|
42
|
+
@property
|
|
43
|
+
def __a_sync_flag_name__(self) -> str:
|
|
44
|
+
return "sync"
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def __a_sync_flag_value__(self) -> bool:
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def __a_sync_default_mode__(cls) -> bool:
|
|
52
|
+
return False
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
In this example, `MyASyncClass` is a subclass of `ASyncABC` with custom implementations
|
|
56
|
+
for the required abstract methods.
|
|
33
57
|
"""
|
|
34
58
|
|
|
35
59
|
##################################
|
|
@@ -45,6 +69,11 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
45
69
|
|
|
46
70
|
Args:
|
|
47
71
|
kwargs: A dictionary of keyword arguments to check for flags.
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
>>> instance = MyASyncClass()
|
|
75
|
+
>>> instance.__a_sync_should_await__({'sync': True})
|
|
76
|
+
False
|
|
48
77
|
"""
|
|
49
78
|
try:
|
|
50
79
|
return self.__a_sync_should_await_from_kwargs__(kwargs)
|
|
@@ -58,6 +87,11 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
58
87
|
This property can be overridden if dynamic behavior is needed. For
|
|
59
88
|
instance, to allow hot-swapping of instance modes, redefine this as a
|
|
60
89
|
non-cached property.
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
>>> instance = MyASyncClass()
|
|
93
|
+
>>> instance.__a_sync_instance_should_await__
|
|
94
|
+
True
|
|
61
95
|
"""
|
|
62
96
|
return _flags.negate_if_necessary(
|
|
63
97
|
self.__a_sync_flag_name__, self.__a_sync_flag_value__
|
|
@@ -74,6 +108,11 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
74
108
|
|
|
75
109
|
Raises:
|
|
76
110
|
NoFlagsFound: If no valid flags are found in the keyword arguments.
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
>>> instance = MyASyncClass()
|
|
114
|
+
>>> instance.__a_sync_should_await_from_kwargs__({'sync': False})
|
|
115
|
+
True
|
|
77
116
|
"""
|
|
78
117
|
if flag := _kwargs.get_flag_name(kwargs):
|
|
79
118
|
return _kwargs.is_sync(flag, kwargs, pop_flag=True) # type: ignore [arg-type]
|
|
@@ -89,6 +128,10 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
89
128
|
Args:
|
|
90
129
|
args: A tuple of positional arguments for the instance.
|
|
91
130
|
kwargs: A dictionary of keyword arguments for the instance.
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
>>> MyASyncClass.__a_sync_instance_will_be_sync__((), {'sync': True})
|
|
134
|
+
True
|
|
92
135
|
"""
|
|
93
136
|
logger.debug(
|
|
94
137
|
"checking `%s.%s.__init__` signature against provided kwargs to determine a_sync mode for the new instance",
|
|
@@ -119,6 +162,11 @@ class ASyncABC(metaclass=ASyncMeta):
|
|
|
119
162
|
|
|
120
163
|
This method should not be overridden. It returns the modifiers
|
|
121
164
|
associated with the instance, which are used to customize behavior.
|
|
165
|
+
|
|
166
|
+
Examples:
|
|
167
|
+
>>> instance = MyASyncClass()
|
|
168
|
+
>>> instance.__a_sync_modifiers__
|
|
169
|
+
{'cache_type': 'memory'}
|
|
122
170
|
"""
|
|
123
171
|
return modifiers.get_modifiers_from(self)
|
|
124
172
|
|
a_sync/a_sync/config.py
CHANGED
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Configuration module for the a_sync library.
|
|
3
|
-
|
|
4
2
|
This module provides configuration options and default settings for the a_sync library.
|
|
5
3
|
It includes functionality for setting up executors, defining default modifiers,
|
|
6
4
|
and handling environment variable configurations.
|
|
7
5
|
|
|
8
6
|
Environment Variables:
|
|
9
7
|
A_SYNC_EXECUTOR_TYPE: Specifies the type of executor to use. Valid values are
|
|
10
|
-
strings that start with 'p' for ProcessPoolExecutor
|
|
11
|
-
|
|
8
|
+
strings that start with 'p' for :class:`~concurrent.futures.ProcessPoolExecutor`
|
|
9
|
+
(e.g., 'processes') or 't' for :class:`~concurrent.futures.ThreadPoolExecutor`
|
|
10
|
+
(e.g., 'threads'). Defaults to 'threads'.
|
|
12
11
|
A_SYNC_EXECUTOR_VALUE: Specifies the number of workers for the executor.
|
|
13
12
|
Defaults to 8.
|
|
14
13
|
A_SYNC_DEFAULT_MODE: Sets the default mode for a_sync functions if not specified.
|
|
15
14
|
A_SYNC_CACHE_TYPE: Sets the default cache type. If not specified, defaults to None.
|
|
16
15
|
A_SYNC_CACHE_TYPED: Boolean flag to determine if cache keys should consider types.
|
|
17
16
|
A_SYNC_RAM_CACHE_MAXSIZE: Sets the maximum size for the RAM cache. Defaults to -1.
|
|
18
|
-
A_SYNC_RAM_CACHE_TTL: Sets the time-to-live for cache entries.
|
|
17
|
+
A_SYNC_RAM_CACHE_TTL: Sets the time-to-live for cache entries. If not specified,
|
|
18
|
+
defaults to None, meaning cache entries do not expire by default.
|
|
19
|
+
Note: Although the environment variable retrieval process uses 0 as a placeholder,
|
|
20
|
+
the actual default behavior is determined by `null_modifiers["ram_cache_ttl"]`,
|
|
21
|
+
which is `None`.
|
|
19
22
|
A_SYNC_RUNS_PER_MINUTE: Sets the rate limit for function execution.
|
|
20
23
|
A_SYNC_SEMAPHORE: Sets the semaphore limit for function execution.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
To set the executor type to use threads with 4 workers, set the environment variables:
|
|
27
|
+
|
|
28
|
+
.. code-block:: bash
|
|
29
|
+
|
|
30
|
+
export A_SYNC_EXECUTOR_TYPE=threads
|
|
31
|
+
export A_SYNC_EXECUTOR_VALUE=4
|
|
32
|
+
|
|
33
|
+
To configure caching with a maximum size of 100 and a TTL of 60 seconds:
|
|
34
|
+
|
|
35
|
+
.. code-block:: bash
|
|
36
|
+
|
|
37
|
+
export A_SYNC_CACHE_TYPE=memory
|
|
38
|
+
export A_SYNC_RAM_CACHE_MAXSIZE=100
|
|
39
|
+
export A_SYNC_RAM_CACHE_TTL=60
|
|
40
|
+
|
|
41
|
+
See Also:
|
|
42
|
+
- :mod:`concurrent.futures`: For more details on executors.
|
|
43
|
+
- :mod:`functools`: For caching mechanisms.
|
|
21
44
|
"""
|
|
22
45
|
|
|
23
46
|
import functools
|
|
@@ -36,11 +59,25 @@ def get_default_executor() -> Executor:
|
|
|
36
59
|
"""Get the default executor based on the EXECUTOR_TYPE environment variable.
|
|
37
60
|
|
|
38
61
|
Returns:
|
|
39
|
-
An instance of either ProcessPoolExecutor
|
|
62
|
+
An instance of either :class:`~concurrent.futures.ProcessPoolExecutor`
|
|
63
|
+
or :class:`~concurrent.futures.ThreadPoolExecutor`.
|
|
40
64
|
|
|
41
65
|
Raises:
|
|
42
66
|
ValueError: If an invalid EXECUTOR_TYPE is specified. Valid values are
|
|
43
|
-
strings that start with 'p' for ProcessPoolExecutor
|
|
67
|
+
strings that start with 'p' for :class:`~concurrent.futures.ProcessPoolExecutor`
|
|
68
|
+
or 't' for :class:`~concurrent.futures.ThreadPoolExecutor`.
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
>>> import os
|
|
72
|
+
>>> os.environ["A_SYNC_EXECUTOR_TYPE"] = "threads"
|
|
73
|
+
>>> executor = get_default_executor()
|
|
74
|
+
>>> isinstance(executor, ThreadPoolExecutor)
|
|
75
|
+
True
|
|
76
|
+
|
|
77
|
+
>>> os.environ["A_SYNC_EXECUTOR_TYPE"] = "processes"
|
|
78
|
+
>>> executor = get_default_executor()
|
|
79
|
+
>>> isinstance(executor, ProcessPoolExecutor)
|
|
80
|
+
True
|
|
44
81
|
"""
|
|
45
82
|
if EXECUTOR_TYPE.lower().startswith("p"): # p, P, proc, Processes, etc
|
|
46
83
|
return ProcessPoolExecutor(EXECUTOR_VALUE)
|