ez-a-sync 0.32.29__cp310-cp310-win32.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.

Files changed (177) hide show
  1. a_sync/ENVIRONMENT_VARIABLES.py +42 -0
  2. a_sync/__init__.pxd +2 -0
  3. a_sync/__init__.py +145 -0
  4. a_sync/_smart.c +22803 -0
  5. a_sync/_smart.cp310-win32.pyd +0 -0
  6. a_sync/_smart.pxd +2 -0
  7. a_sync/_smart.pyi +202 -0
  8. a_sync/_smart.pyx +674 -0
  9. a_sync/_typing.py +258 -0
  10. a_sync/a_sync/__init__.py +60 -0
  11. a_sync/a_sync/_descriptor.c +20528 -0
  12. a_sync/a_sync/_descriptor.cp310-win32.pyd +0 -0
  13. a_sync/a_sync/_descriptor.pyi +33 -0
  14. a_sync/a_sync/_descriptor.pyx +422 -0
  15. a_sync/a_sync/_flags.c +6074 -0
  16. a_sync/a_sync/_flags.cp310-win32.pyd +0 -0
  17. a_sync/a_sync/_flags.pxd +3 -0
  18. a_sync/a_sync/_flags.pyx +92 -0
  19. a_sync/a_sync/_helpers.c +14521 -0
  20. a_sync/a_sync/_helpers.cp310-win32.pyd +0 -0
  21. a_sync/a_sync/_helpers.pxd +3 -0
  22. a_sync/a_sync/_helpers.pyi +10 -0
  23. a_sync/a_sync/_helpers.pyx +167 -0
  24. a_sync/a_sync/_kwargs.c +12194 -0
  25. a_sync/a_sync/_kwargs.cp310-win32.pyd +0 -0
  26. a_sync/a_sync/_kwargs.pxd +2 -0
  27. a_sync/a_sync/_kwargs.pyx +64 -0
  28. a_sync/a_sync/_meta.py +210 -0
  29. a_sync/a_sync/abstract.c +12411 -0
  30. a_sync/a_sync/abstract.cp310-win32.pyd +0 -0
  31. a_sync/a_sync/abstract.pyi +141 -0
  32. a_sync/a_sync/abstract.pyx +221 -0
  33. a_sync/a_sync/base.c +14932 -0
  34. a_sync/a_sync/base.cp310-win32.pyd +0 -0
  35. a_sync/a_sync/base.pyi +60 -0
  36. a_sync/a_sync/base.pyx +271 -0
  37. a_sync/a_sync/config.py +168 -0
  38. a_sync/a_sync/decorator.py +651 -0
  39. a_sync/a_sync/flags.c +5272 -0
  40. a_sync/a_sync/flags.cp310-win32.pyd +0 -0
  41. a_sync/a_sync/flags.pxd +72 -0
  42. a_sync/a_sync/flags.pyi +74 -0
  43. a_sync/a_sync/flags.pyx +72 -0
  44. a_sync/a_sync/function.c +37846 -0
  45. a_sync/a_sync/function.cp310-win32.pyd +0 -0
  46. a_sync/a_sync/function.pxd +28 -0
  47. a_sync/a_sync/function.pyi +571 -0
  48. a_sync/a_sync/function.pyx +1381 -0
  49. a_sync/a_sync/method.c +29774 -0
  50. a_sync/a_sync/method.cp310-win32.pyd +0 -0
  51. a_sync/a_sync/method.pxd +9 -0
  52. a_sync/a_sync/method.pyi +525 -0
  53. a_sync/a_sync/method.pyx +1023 -0
  54. a_sync/a_sync/modifiers/__init__.pxd +1 -0
  55. a_sync/a_sync/modifiers/__init__.py +101 -0
  56. a_sync/a_sync/modifiers/cache/__init__.py +160 -0
  57. a_sync/a_sync/modifiers/cache/memory.py +165 -0
  58. a_sync/a_sync/modifiers/limiter.py +132 -0
  59. a_sync/a_sync/modifiers/manager.c +16149 -0
  60. a_sync/a_sync/modifiers/manager.cp310-win32.pyd +0 -0
  61. a_sync/a_sync/modifiers/manager.pxd +5 -0
  62. a_sync/a_sync/modifiers/manager.pyi +219 -0
  63. a_sync/a_sync/modifiers/manager.pyx +299 -0
  64. a_sync/a_sync/modifiers/semaphores.py +173 -0
  65. a_sync/a_sync/property.c +27260 -0
  66. a_sync/a_sync/property.cp310-win32.pyd +0 -0
  67. a_sync/a_sync/property.pyi +376 -0
  68. a_sync/a_sync/property.pyx +819 -0
  69. a_sync/a_sync/singleton.py +63 -0
  70. a_sync/aliases.py +3 -0
  71. a_sync/async_property/__init__.pxd +1 -0
  72. a_sync/async_property/__init__.py +1 -0
  73. a_sync/async_property/cached.c +20386 -0
  74. a_sync/async_property/cached.cp310-win32.pyd +0 -0
  75. a_sync/async_property/cached.pxd +10 -0
  76. a_sync/async_property/cached.pyi +45 -0
  77. a_sync/async_property/cached.pyx +178 -0
  78. a_sync/async_property/proxy.c +34654 -0
  79. a_sync/async_property/proxy.cp310-win32.pyd +0 -0
  80. a_sync/async_property/proxy.pxd +2 -0
  81. a_sync/async_property/proxy.pyi +124 -0
  82. a_sync/async_property/proxy.pyx +474 -0
  83. a_sync/asyncio/__init__.pxd +6 -0
  84. a_sync/asyncio/__init__.py +164 -0
  85. a_sync/asyncio/as_completed.c +18841 -0
  86. a_sync/asyncio/as_completed.cp310-win32.pyd +0 -0
  87. a_sync/asyncio/as_completed.pxd +8 -0
  88. a_sync/asyncio/as_completed.pyi +109 -0
  89. a_sync/asyncio/as_completed.pyx +269 -0
  90. a_sync/asyncio/create_task.c +15902 -0
  91. a_sync/asyncio/create_task.cp310-win32.pyd +0 -0
  92. a_sync/asyncio/create_task.pxd +2 -0
  93. a_sync/asyncio/create_task.pyi +51 -0
  94. a_sync/asyncio/create_task.pyx +271 -0
  95. a_sync/asyncio/gather.c +16679 -0
  96. a_sync/asyncio/gather.cp310-win32.pyd +0 -0
  97. a_sync/asyncio/gather.pyi +107 -0
  98. a_sync/asyncio/gather.pyx +218 -0
  99. a_sync/asyncio/igather.c +12676 -0
  100. a_sync/asyncio/igather.cp310-win32.pyd +0 -0
  101. a_sync/asyncio/igather.pxd +1 -0
  102. a_sync/asyncio/igather.pyi +7 -0
  103. a_sync/asyncio/igather.pyx +182 -0
  104. a_sync/asyncio/sleep.c +9593 -0
  105. a_sync/asyncio/sleep.cp310-win32.pyd +0 -0
  106. a_sync/asyncio/sleep.pyi +14 -0
  107. a_sync/asyncio/sleep.pyx +49 -0
  108. a_sync/debugging.c +15362 -0
  109. a_sync/debugging.cp310-win32.pyd +0 -0
  110. a_sync/debugging.pyi +76 -0
  111. a_sync/debugging.pyx +107 -0
  112. a_sync/exceptions.c +13312 -0
  113. a_sync/exceptions.cp310-win32.pyd +0 -0
  114. a_sync/exceptions.pyi +376 -0
  115. a_sync/exceptions.pyx +446 -0
  116. a_sync/executor.py +619 -0
  117. a_sync/functools.c +12738 -0
  118. a_sync/functools.cp310-win32.pyd +0 -0
  119. a_sync/functools.pxd +7 -0
  120. a_sync/functools.pyi +33 -0
  121. a_sync/functools.pyx +139 -0
  122. a_sync/future.py +1497 -0
  123. a_sync/iter.c +37271 -0
  124. a_sync/iter.cp310-win32.pyd +0 -0
  125. a_sync/iter.pxd +11 -0
  126. a_sync/iter.pyi +370 -0
  127. a_sync/iter.pyx +981 -0
  128. a_sync/primitives/__init__.pxd +1 -0
  129. a_sync/primitives/__init__.py +53 -0
  130. a_sync/primitives/_debug.c +15757 -0
  131. a_sync/primitives/_debug.cp310-win32.pyd +0 -0
  132. a_sync/primitives/_debug.pxd +12 -0
  133. a_sync/primitives/_debug.pyi +52 -0
  134. a_sync/primitives/_debug.pyx +223 -0
  135. a_sync/primitives/_loggable.c +11529 -0
  136. a_sync/primitives/_loggable.cp310-win32.pyd +0 -0
  137. a_sync/primitives/_loggable.pxd +4 -0
  138. a_sync/primitives/_loggable.pyi +66 -0
  139. a_sync/primitives/_loggable.pyx +102 -0
  140. a_sync/primitives/locks/__init__.pxd +8 -0
  141. a_sync/primitives/locks/__init__.py +17 -0
  142. a_sync/primitives/locks/counter.c +17679 -0
  143. a_sync/primitives/locks/counter.cp310-win32.pyd +0 -0
  144. a_sync/primitives/locks/counter.pxd +12 -0
  145. a_sync/primitives/locks/counter.pyi +151 -0
  146. a_sync/primitives/locks/counter.pyx +260 -0
  147. a_sync/primitives/locks/event.c +17063 -0
  148. a_sync/primitives/locks/event.cp310-win32.pyd +0 -0
  149. a_sync/primitives/locks/event.pxd +22 -0
  150. a_sync/primitives/locks/event.pyi +43 -0
  151. a_sync/primitives/locks/event.pyx +185 -0
  152. a_sync/primitives/locks/prio_semaphore.c +25590 -0
  153. a_sync/primitives/locks/prio_semaphore.cp310-win32.pyd +0 -0
  154. a_sync/primitives/locks/prio_semaphore.pxd +25 -0
  155. a_sync/primitives/locks/prio_semaphore.pyi +217 -0
  156. a_sync/primitives/locks/prio_semaphore.pyx +597 -0
  157. a_sync/primitives/locks/semaphore.c +26509 -0
  158. a_sync/primitives/locks/semaphore.cp310-win32.pyd +0 -0
  159. a_sync/primitives/locks/semaphore.pxd +21 -0
  160. a_sync/primitives/locks/semaphore.pyi +197 -0
  161. a_sync/primitives/locks/semaphore.pyx +454 -0
  162. a_sync/primitives/queue.py +1022 -0
  163. a_sync/py.typed +0 -0
  164. a_sync/sphinx/__init__.py +3 -0
  165. a_sync/sphinx/ext.py +289 -0
  166. a_sync/task.py +932 -0
  167. a_sync/utils/__init__.py +105 -0
  168. a_sync/utils/iterators.py +297 -0
  169. a_sync/utils/repr.c +15799 -0
  170. a_sync/utils/repr.cp310-win32.pyd +0 -0
  171. a_sync/utils/repr.pyi +2 -0
  172. a_sync/utils/repr.pyx +73 -0
  173. ez_a_sync-0.32.29.dist-info/METADATA +367 -0
  174. ez_a_sync-0.32.29.dist-info/RECORD +177 -0
  175. ez_a_sync-0.32.29.dist-info/WHEEL +5 -0
  176. ez_a_sync-0.32.29.dist-info/licenses/LICENSE.txt +17 -0
  177. ez_a_sync-0.32.29.dist-info/top_level.txt +1 -0
Binary file
a_sync/a_sync/base.pyi ADDED
@@ -0,0 +1,60 @@
1
+ from a_sync._typing import *
2
+ import functools
3
+ import logging
4
+ from a_sync import exceptions as exceptions
5
+ from a_sync.a_sync.abstract import ASyncABC as ASyncABC
6
+ from a_sync.a_sync.flags import VIABLE_FLAGS as VIABLE_FLAGS
7
+
8
+ logger: logging.Logger
9
+
10
+ class ASyncGenericBase(ASyncABC):
11
+ """
12
+ Base class for creating dual-function sync/async-capable classes without writing all your code twice.
13
+
14
+ This class, via its inherited metaclass :class:`~ASyncMeta', provides the foundation for creating hybrid sync/async classes. It allows methods
15
+ and properties to be defined once and used in both synchronous and asynchronous contexts.
16
+
17
+ The class uses the :func:`a_sync` decorator internally to create dual-mode methods and properties.
18
+ Subclasses should define their methods as coroutines (using `async def`) where possible, and
19
+ use the `@a_sync.property` or `@a_sync.cached_property` decorators for properties that need to support both modes.
20
+
21
+ Example:
22
+ ```python
23
+ class MyClass(ASyncGenericBase):
24
+ def __init__(self, sync: bool):
25
+ self.sync = sync
26
+
27
+ @a_sync.property
28
+ async def my_property(self):
29
+ return await some_async_operation()
30
+
31
+ @a_sync
32
+ async def my_method(self):
33
+ return await another_async_operation()
34
+
35
+ # Synchronous usage
36
+ obj = MyClass(sync=True)
37
+ sync_result = obj.my_property
38
+ sync_method_result = obj.my_method()
39
+
40
+ # Asynchronous usage
41
+ obj = MyClass(sync=False)
42
+ async_result = await obj.my_property
43
+ async_method_result = await obj.my_method()
44
+ ```
45
+
46
+ Note:
47
+ When subclassing, be aware that all async methods and properties will be
48
+ automatically wrapped to support both sync and async calls. This allows for
49
+ seamless usage in different contexts without changing the underlying implementation.
50
+ """
51
+
52
+ def __init__(self) -> None: ...
53
+ @functools.cached_property
54
+ def __a_sync_flag_name__(self) -> str: ...
55
+ @functools.cached_property
56
+ def __a_sync_flag_value__(self) -> bool:
57
+ """If you wish to be able to hotswap default modes, just duplicate this def as a non-cached property."""
58
+
59
+ @classmethod
60
+ def __a_sync_default_mode__(cls) -> bool: ...
a_sync/a_sync/base.pyx ADDED
@@ -0,0 +1,271 @@
1
+ # cython: boundscheck=False
2
+ import inspect
3
+ from logging import getLogger
4
+
5
+ from cpython.object cimport Py_TYPE, PyObject
6
+ from cpython.tuple cimport PyTuple_GET_SIZE, PyTuple_GetItem
7
+
8
+ from a_sync._typing import *
9
+ from a_sync.a_sync._flags cimport validate_and_negate_if_necessary, validate_flag_value
10
+ from a_sync.a_sync.abstract import ASyncABC
11
+ from a_sync.a_sync.flags cimport VIABLE_FLAGS
12
+ from a_sync.exceptions import ASyncFlagException, FlagNotDefined, InvalidFlag, NoFlagsFound, TooManyFlags
13
+ from a_sync.functools cimport cached_property_unsafe
14
+
15
+
16
+ cdef extern from "Python.h":
17
+ ctypedef struct PyTypeObject:
18
+ PyObject *tp_bases
19
+ PyObject *tp_dict
20
+
21
+ ctypedef object object_id
22
+ ctypedef dict[str, object] cls_init_flags
23
+
24
+
25
+ # cdef inspect
26
+ cdef object signature = inspect.signature
27
+ cdef object _empty = inspect._empty
28
+ del inspect
29
+
30
+ # cdef logging
31
+ cdef public object logger = getLogger(__name__)
32
+ cdef object _logger_is_enabled = logger.isEnabledFor
33
+ cdef object _logger_debug = logger.debug
34
+ cdef object _logger_log = logger._log
35
+ cdef object DEBUG = 10
36
+ del getLogger
37
+
38
+
39
+ cdef object _init_ASyncABC = ASyncABC.__init__
40
+
41
+
42
+ class ASyncGenericBase(ASyncABC):
43
+ """
44
+ Base class for creating dual-function sync/async-capable classes without writing all your code twice.
45
+
46
+ This class, via its inherited metaclass :class:`~ASyncMeta', provides the foundation for creating hybrid sync/async classes. It allows methods
47
+ and properties to be defined once and used in both synchronous and asynchronous contexts.
48
+
49
+ The class uses the :func:`a_sync` decorator internally to create dual-mode methods and properties.
50
+ Subclasses should define their methods as coroutines (using `async def`) where possible, and
51
+ use the `@a_sync.property` or `@a_sync.cached_property` decorators for properties that need to support both modes.
52
+
53
+ Example:
54
+ ```python
55
+ class MyClass(ASyncGenericBase):
56
+ def __init__(self, sync: bool):
57
+ self.sync = sync
58
+
59
+ @a_sync.property
60
+ async def my_property(self):
61
+ return await some_async_operation()
62
+
63
+ @a_sync
64
+ async def my_method(self):
65
+ return await another_async_operation()
66
+
67
+ # Synchronous usage
68
+ obj = MyClass(sync=True)
69
+ sync_result = obj.my_property
70
+ sync_method_result = obj.my_method()
71
+
72
+ # Asynchronous usage
73
+ obj = MyClass(sync=False)
74
+ async_result = await obj.my_property
75
+ async_method_result = await obj.my_method()
76
+ ```
77
+
78
+ Note:
79
+ When subclassing, be aware that all async methods and properties will be
80
+ automatically wrapped to support both sync and async calls. This allows for
81
+ seamless usage in different contexts without changing the underlying implementation.
82
+ """
83
+
84
+ @classmethod # type: ignore [misc]
85
+ def __a_sync_default_mode__(cls) -> bint: # type: ignore [override]
86
+ cdef str flag
87
+ cdef bint flag_value
88
+ cdef PyTypeObject *cls_ptr
89
+
90
+ cls_ptr = <PyTypeObject*>cls
91
+ if not _logger_is_enabled(DEBUG):
92
+ # we can optimize this if we dont need to log `flag` and the return value
93
+ try:
94
+ flag = _get_a_sync_flag_name_from_signature(cls_ptr, False)
95
+ flag_value = _a_sync_flag_default_value_from_signature(cls_ptr)
96
+ except NoFlagsFound:
97
+ flag = _get_a_sync_flag_name_from_class_def(cls_ptr)
98
+ flag_value = _get_a_sync_flag_value_from_class_def(cls, flag)
99
+ return validate_and_negate_if_necessary(flag, flag_value)
100
+
101
+ try:
102
+ flag = _get_a_sync_flag_name_from_signature(cls_ptr, True)
103
+ flag_value = _a_sync_flag_default_value_from_signature(cls_ptr)
104
+ except NoFlagsFound:
105
+ flag = _get_a_sync_flag_name_from_class_def(cls_ptr)
106
+ flag_value = _get_a_sync_flag_value_from_class_def(cls, flag)
107
+
108
+ cdef bint sync = validate_and_negate_if_necessary(flag, flag_value)
109
+ _logger_log(
110
+ DEBUG,
111
+ "`%s.%s` indicates default mode is %ssynchronous",
112
+ (cls, flag, "a" if sync is False else ""),
113
+ )
114
+ return sync
115
+
116
+ def __init__(self):
117
+ if Py_TYPE(self) == ASyncGenericBase_ptr:
118
+ raise NotImplementedError(
119
+ "You should not create instances of `ASyncGenericBase` directly, "
120
+ "you should subclass `ASyncGenericBase` instead."
121
+ )
122
+ _init_ASyncABC(self)
123
+
124
+ @cached_property_unsafe
125
+ def __a_sync_flag_name__(self) -> str:
126
+ # TODO: cythonize this cache
127
+ cdef bint debug_logs
128
+ if debug_logs := _logger_is_enabled(DEBUG):
129
+ _logger_log(DEBUG, "checking a_sync flag for %s", (self, ))
130
+ try:
131
+ flag = _get_a_sync_flag_name_from_signature(Py_TYPE(self), debug_logs)
132
+ except ASyncFlagException:
133
+ # We can't get the flag name from the __init__ signature,
134
+ # but maybe the implementation sets the flag somewhere else.
135
+ # Let's check the instance's atributes
136
+ if debug_logs:
137
+ _logger_log(
138
+ DEBUG,
139
+ "unable to find flag name using `%s.__init__` signature, checking for flag attributes defined on %s",
140
+ (self.__class__.__name__, self),
141
+ )
142
+ present_flags = [flag for flag in VIABLE_FLAGS if hasattr(self, flag)]
143
+ if not present_flags:
144
+ raise NoFlagsFound(self) from None
145
+ if len(present_flags) > 1:
146
+ raise TooManyFlags(self, present_flags) from None
147
+ flag = present_flags[0]
148
+ if not isinstance(flag, str):
149
+ raise InvalidFlag(flag)
150
+ return flag
151
+
152
+ @cached_property_unsafe
153
+ def __a_sync_flag_value__(self) -> bint:
154
+ # TODO: cythonize this cache
155
+ """If you wish to be able to hotswap default modes, just duplicate this def as a non-cached property."""
156
+ cdef str flag = self.__a_sync_flag_name__
157
+ flag_value = getattr(self, flag)
158
+ _logger_debug("`%s.%s` is currently %s", self, flag, flag_value)
159
+ return validate_flag_value(flag, flag_value)
160
+
161
+
162
+ cdef PyTypeObject *ASyncGenericBase_ptr = <PyTypeObject*>ASyncGenericBase
163
+
164
+
165
+ cdef inline str _get_a_sync_flag_name_from_class_def(PyTypeObject *cls_ptr):
166
+ cdef object bases
167
+ cdef PyObject *bases_ptr
168
+ cdef PyTypeObject *base_ptr
169
+ cdef Py_ssize_t len_bases
170
+
171
+ if _logger_is_enabled(DEBUG):
172
+ _logger_log(DEBUG, "Searching for flags defined on %s", (<object>cls_ptr,))
173
+
174
+ try:
175
+ return _parse_flag_name_from_dict_keys(cls_ptr, <object>cls_ptr.tp_dict)
176
+ except NoFlagsFound:
177
+ bases_ptr = cls_ptr.tp_bases # This is a tuple or NULL
178
+ if bases_ptr != NULL:
179
+ bases = <object>bases_ptr
180
+ len_bases = PyTuple_GET_SIZE(bases)
181
+ for i in range(len_bases):
182
+ base_ptr = <PyTypeObject*>PyTuple_GetItem(bases, i)
183
+ try:
184
+ return _get_a_sync_flag_name_from_class_def(base_ptr)
185
+ except NoFlagsFound:
186
+ pass
187
+ raise NoFlagsFound(<object>cls_ptr, list(<object>cls_ptr.tp_dict))
188
+
189
+
190
+ cdef bint _a_sync_flag_default_value_from_signature(PyTypeObject *cls_ptr):
191
+ cdef cls_init_flags flags = _get_init_flags(cls_ptr)
192
+
193
+ if not _logger_is_enabled(DEBUG):
194
+ # we can optimize this much better
195
+ return flags[_get_a_sync_flag_name_from_signature(cls_ptr, False)].default
196
+
197
+ cdef object cls = <object>cls_ptr
198
+ _logger_log(
199
+ DEBUG, "checking `__init__` signature for default %s a_sync flag value", (cls, )
200
+ )
201
+
202
+ cdef str flag = _get_a_sync_flag_name_from_signature(cls_ptr, True)
203
+ cdef object flag_value = flags[flag].default
204
+ if flag_value is _empty: # type: ignore [attr-defined]
205
+ raise NotImplementedError(
206
+ "The implementation for 'cls' uses an arg to specify sync mode, instead of a kwarg. We are unable to proceed. I suppose we can extend the code to accept positional arg flags if necessary"
207
+ )
208
+ _logger_log(DEBUG, "%s defines %s, default value %s", (cls, flag, flag_value))
209
+ return flag_value
210
+
211
+
212
+ cdef str _get_a_sync_flag_name_from_signature(PyTypeObject *cls_ptr, bint debug_logs):
213
+ cdef cls_init_flags flags
214
+
215
+ if cls_ptr == ASyncGenericBase_ptr:
216
+ # There are no flags defined on the base class
217
+ return ""
218
+
219
+ # if we fail this one there's no need to check again
220
+ if not debug_logs:
221
+ # we can also skip assigning params to a var
222
+ return _parse_flag_name_from_dict_keys(cls_ptr, _get_init_flags(cls_ptr))
223
+
224
+ _logger_log(DEBUG, "Searching for flags defined on %s.__init__", (<object>cls_ptr, ))
225
+ flags = _get_init_flags(cls_ptr)
226
+ _logger_log(DEBUG, "flags: %s", (flags, ))
227
+ return _parse_flag_name_from_dict_keys(cls_ptr, flags)
228
+
229
+
230
+ cdef inline str _parse_flag_name_from_dict_keys(PyTypeObject *cls_ptr, dict[str, object] d):
231
+ cdef str flag
232
+ cdef list[str] present_flags = [flag for flag in VIABLE_FLAGS if flag in d]
233
+ cdef int flags_len = len(present_flags)
234
+ if not flags_len:
235
+ cls = <object>cls_ptr
236
+ _logger_debug("There are no flags defined on %s", cls)
237
+ raise NoFlagsFound(cls, d.keys())
238
+ if flags_len > 1:
239
+ cls = <object>cls_ptr
240
+ _logger_debug("There are too many flags defined on %s", cls)
241
+ raise TooManyFlags(cls, present_flags)
242
+ if _logger_is_enabled(DEBUG):
243
+ flag = present_flags[0]
244
+ _logger_log(DEBUG, "found flag %s", (flag, ))
245
+ return flag
246
+ return present_flags[0]
247
+
248
+
249
+ cdef inline bint _get_a_sync_flag_value_from_class_def(object cls, str flag):
250
+ cdef object spec
251
+ cdef bint flag_value
252
+ for spec in [cls, *cls.__bases__]:
253
+ flag_value = spec.__dict__.get(flag)
254
+ if flag_value is not None:
255
+ return flag_value
256
+ raise FlagNotDefined(cls, flag)
257
+
258
+
259
+ cdef dict[object_id, cls_init_flags] _init_flags_cache = {}
260
+
261
+
262
+ cdef inline cls_init_flags _get_init_flags(PyTypeObject *cls_ptr):
263
+ cdef cls_init_flags init_flags
264
+ cdef object init_method = (<object>cls_ptr).__init__
265
+ # We keep a smaller dict if we use the id of the method instead of the class
266
+ cdef object_id init_method_id = id(init_method)
267
+ init_flags = _init_flags_cache.get(init_method_id)
268
+ if init_flags is None:
269
+ init_flags = {k: v for k, v in signature(init_method).parameters.items() if k in VIABLE_FLAGS}
270
+ _init_flags_cache[init_method_id] = init_flags
271
+ return init_flags
@@ -0,0 +1,168 @@
1
+ """
2
+ This module provides configuration options and default settings for the a_sync library.
3
+ It includes functionality for setting up executors, defining default modifiers,
4
+ and handling environment variable configurations.
5
+
6
+ Environment Variables:
7
+ :obj:`~A_SYNC_EXECUTOR_TYPE`: Specifies the type of executor to use. Valid values are
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'.
11
+ :obj:`~A_SYNC_EXECUTOR_VALUE`: Specifies the number of workers for the executor.
12
+ Defaults to 8.
13
+ :obj:`~A_SYNC_DEFAULT_MODE`: Sets the default mode for a_sync functions if not specified.
14
+ :obj:`~A_SYNC_CACHE_TYPE`: Sets the default cache type. If not specified, defaults to None.
15
+ :obj:`~A_SYNC_CACHE_TYPED`: Boolean flag to determine if cache keys should consider types.
16
+ :obj:`~A_SYNC_RAM_CACHE_MAXSIZE`: Sets the maximum size for the RAM cache. Defaults to -1.
17
+ :obj:`~A_SYNC_RAM_CACHE_TTL`: Sets the time-to-live for cache entries. If not specified,
18
+ defaults to 0, which is then checked against `null_modifiers["ram_cache_ttl"]`
19
+ to potentially set it to `None`, meaning cache entries do not expire by default.
20
+ :obj:`~A_SYNC_RUNS_PER_MINUTE`: Sets the rate limit for function execution.
21
+ :obj:`~A_SYNC_SEMAPHORE`: Sets the semaphore limit for function execution.
22
+
23
+ Examples:
24
+ To set the executor type to use threads with 4 workers, set the environment variables:
25
+
26
+ .. code-block:: bash
27
+
28
+ export A_SYNC_EXECUTOR_TYPE=threads
29
+ export A_SYNC_EXECUTOR_VALUE=4
30
+
31
+ To configure caching with a maximum size of 100 and a TTL of 60 seconds:
32
+
33
+ .. code-block:: bash
34
+
35
+ export A_SYNC_CACHE_TYPE=memory
36
+ export A_SYNC_RAM_CACHE_MAXSIZE=100
37
+ export A_SYNC_RAM_CACHE_TTL=60
38
+
39
+ TODO: explain how and where these values are used
40
+
41
+ See Also:
42
+ - :mod:`concurrent.futures`: For more details on executors.
43
+ - :mod:`functools`: For caching mechanisms.
44
+ """
45
+
46
+ import functools
47
+ import os
48
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
49
+ from concurrent.futures._base import Executor
50
+
51
+ from a_sync._typing import *
52
+
53
+ EXECUTOR_TYPE = os.environ.get("A_SYNC_EXECUTOR_TYPE", "threads")
54
+ """Specifies the type of executor to use.
55
+
56
+ Valid values are strings that start with 'p' for :class:`~concurrent.futures.ProcessPoolExecutor`
57
+ (e.g., 'processes') or 't' for :class:`~concurrent.futures.ThreadPoolExecutor` (e.g., 'threads').
58
+ Defaults to 'threads'.
59
+ """
60
+
61
+ EXECUTOR_VALUE = int(os.environ.get("A_SYNC_EXECUTOR_VALUE", 8))
62
+ """Specifies the number of workers for the executor. Defaults to 8."""
63
+
64
+
65
+ @functools.lru_cache(maxsize=1)
66
+ def get_default_executor() -> Executor:
67
+ """Get the default executor based on the EXECUTOR_TYPE environment variable.
68
+
69
+ Returns:
70
+ An instance of either :class:`~concurrent.futures.ProcessPoolExecutor`
71
+ or :class:`~concurrent.futures.ThreadPoolExecutor`.
72
+
73
+ Raises:
74
+ ValueError: If an invalid EXECUTOR_TYPE is specified. Valid values are
75
+ strings that start with 'p' for :class:`~concurrent.futures.ProcessPoolExecutor`
76
+ or 't' for :class:`~concurrent.futures.ThreadPoolExecutor`.
77
+
78
+ Examples:
79
+ >>> import os
80
+ >>> os.environ["A_SYNC_EXECUTOR_TYPE"] = "threads"
81
+ >>> executor = get_default_executor()
82
+ >>> isinstance(executor, ThreadPoolExecutor)
83
+ True
84
+
85
+ >>> os.environ["A_SYNC_EXECUTOR_TYPE"] = "processes"
86
+ >>> executor = get_default_executor()
87
+ >>> isinstance(executor, ProcessPoolExecutor)
88
+ True
89
+ """
90
+ if EXECUTOR_TYPE.lower().startswith("p"): # p, P, proc, Processes, etc
91
+ return ProcessPoolExecutor(EXECUTOR_VALUE)
92
+ elif EXECUTOR_TYPE.lower().startswith("t"): # t, T, thread, THREADS, etc
93
+ return ThreadPoolExecutor(EXECUTOR_VALUE)
94
+ raise ValueError("Invalid value for A_SYNC_EXECUTOR_TYPE. Please use 'threads' or 'processes'.")
95
+
96
+
97
+ default_sync_executor = get_default_executor()
98
+
99
+ null_modifiers = ModifierKwargs(
100
+ default=None,
101
+ cache_type=None,
102
+ cache_typed=False,
103
+ ram_cache_maxsize=-1,
104
+ ram_cache_ttl=None,
105
+ runs_per_minute=None,
106
+ semaphore=None,
107
+ executor=default_sync_executor,
108
+ )
109
+
110
+ #####################
111
+ # Default Modifiers #
112
+ #####################
113
+
114
+ # User configurable default modifiers to be applied to any a_sync decorated function if you do not specify kwarg values for each modifier.
115
+
116
+ DEFAULT_MODE: DefaultMode = os.environ.get("A_SYNC_DEFAULT_MODE") # type: ignore [assignment]
117
+ """Sets the default mode for a_sync functions if not specified."""
118
+
119
+ CACHE_TYPE: CacheType = (
120
+ typ
121
+ if (typ := os.environ.get("A_SYNC_CACHE_TYPE", "").lower())
122
+ else null_modifiers["cache_type"]
123
+ )
124
+ """Sets the default cache type. If not specified, defaults to None."""
125
+
126
+ CACHE_TYPED = bool(os.environ.get("A_SYNC_CACHE_TYPED"))
127
+ """Boolean flag to determine if cache keys should consider types."""
128
+
129
+ RAM_CACHE_MAXSIZE = int(os.environ.get("A_SYNC_RAM_CACHE_MAXSIZE", -1))
130
+ """
131
+ Sets the maximum size for the RAM cache. Defaults to -1.
132
+ # TODO: explain what -1 does
133
+ """
134
+
135
+ RAM_CACHE_TTL = (
136
+ ttl
137
+ if (ttl := float(os.environ.get("A_SYNC_RAM_CACHE_TTL", 0)))
138
+ else null_modifiers["ram_cache_ttl"]
139
+ )
140
+ """
141
+ Sets the time-to-live for cache entries. If not specified, defaults to 0, which is then checked against
142
+ `null_modifiers["ram_cache_ttl"]` to potentially set it to `None`, meaning cache entries do not expire
143
+ by default.
144
+ """
145
+
146
+
147
+ RUNS_PER_MINUTE = (
148
+ rpm
149
+ if (rpm := int(os.environ.get("A_SYNC_RUNS_PER_MINUTE", 0)))
150
+ else null_modifiers["runs_per_minute"]
151
+ )
152
+ """Sets the rate limit for function execution."""
153
+
154
+ SEMAPHORE = (
155
+ rpm if (rpm := int(os.environ.get("A_SYNC_SEMAPHORE", 0))) else null_modifiers["semaphore"]
156
+ )
157
+ """Sets the semaphore limit for function execution."""
158
+
159
+ user_set_default_modifiers = ModifierKwargs(
160
+ default=DEFAULT_MODE,
161
+ cache_type=CACHE_TYPE,
162
+ cache_typed=CACHE_TYPED,
163
+ ram_cache_maxsize=RAM_CACHE_MAXSIZE,
164
+ ram_cache_ttl=RAM_CACHE_TTL,
165
+ runs_per_minute=RUNS_PER_MINUTE,
166
+ semaphore=SEMAPHORE,
167
+ executor=default_sync_executor,
168
+ )