pytrove 1.0.0__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.
pytrove/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ from .__meta__ import __version__
2
+
3
+ from .validate_tools import *
4
+ from .date_tools import *
5
+ from .num_tools import *
6
+ from .files_tools import *
7
+ from .iter_tools import *
8
+ from .text_tools import *
9
+ from .async_tools import *
10
+ from .misc_tools import *
11
+ from .tg_tools import *
12
+ from .phone_tools import *
13
+ from .country_tools import *
14
+ from .crypto_tools import *
15
+ from .data_tools import *
16
+ from .callable_tools import *
17
+ from .logging_tools import *
18
+ from .email_tools import *
19
+ from .classes import *
20
+
21
+ __all__ = (
22
+ *validate_tools.__all__,
23
+ *date_tools.__all__,
24
+ *num_tools.__all__,
25
+ *files_tools.__all__,
26
+ *iter_tools.__all__,
27
+ *text_tools.__all__,
28
+ *async_tools.__all__,
29
+ *misc_tools.__all__,
30
+ *tg_tools.__all__,
31
+ *phone_tools.__all__,
32
+ *country_tools.__all__,
33
+ *crypto_tools.__all__,
34
+ *data_tools.__all__,
35
+ *callable_tools.__all__,
36
+ *classes.__all__,
37
+ *logging_tools.__all__,
38
+ *email_tools.__all__,
39
+ )
pytrove/__meta__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,202 @@
1
+ import asyncio
2
+ import sys
3
+
4
+ from typing import Any, Awaitable, List, Optional, Union, TYPE_CHECKING, overload
5
+ from .typings import _T, _True, _False
6
+
7
+
8
+ _PY314 = sys.version_info >= (3, 14)
9
+
10
+ def _get_fut_loop(fut):
11
+ try:
12
+ get_loop = fut.get_loop
13
+ except AttributeError:
14
+ pass
15
+ else:
16
+ return get_loop()
17
+ return fut._loop
18
+
19
+ def _get_running_loop() -> Optional[asyncio.AbstractEventLoop]:
20
+ try:
21
+ return asyncio.get_running_loop()
22
+ except RuntimeError:
23
+ return
24
+
25
+
26
+ class _GatheringFuture(asyncio.Future):
27
+ def __init__(self, children, *, loop):
28
+ super().__init__(loop=loop)
29
+
30
+ self._children = children
31
+
32
+ if not TYPE_CHECKING:
33
+ @property
34
+ def cancel_message(self) -> Optional[Any]:
35
+ return getattr(self, "_cancel_message", None)
36
+
37
+ @cancel_message.setter
38
+ def cancel_message(self, value: Optional[Any]):
39
+ setattr(self, "_cancel_message", value)
40
+
41
+
42
+ def cancel(self, msg: Optional[Any] = None) -> bool:
43
+ # Cancelling the outer future does not cancel *this* future
44
+ # immediately -- it forwards the request to every child and lets
45
+ # them unwind first, exactly like asyncio.gather(). _done_callback
46
+ # only turns `outer` itself into a CancelledError once every child
47
+ # has actually finished (npending == 0); until then `outer` stays
48
+ # pending even though a cancellation is already in flight.
49
+ if self.done():
50
+ return False
51
+
52
+ ret = False
53
+
54
+ for child in self._children:
55
+ if child.cancel(msg=msg):
56
+ ret = True
57
+
58
+ if ret:
59
+ self.cancel_message = "" if msg is None else msg
60
+
61
+ return ret
62
+
63
+
64
+ @overload
65
+ def _gather_cancel_on_error(*awaitables: Awaitable[_T], return_exceptions: _False = False) -> asyncio.Future[List[_T]]:...
66
+ @overload
67
+ def _gather_cancel_on_error(*awaitables: Awaitable[_T], return_exceptions: _True) -> asyncio.Future[List[Union[_T, Exception]]]:...
68
+ def _gather_cancel_on_error(*awaitables, return_exceptions = False):
69
+ """Reimplementation of asyncio.gather() that cancels every sibling as soon
70
+ as one fails (asyncio.gather() itself leaves the rest running).
71
+
72
+ `_done_callback` fires once per child future and does one of three things:
73
+ - if `outer` is already resolved (a prior sibling failed first, or the
74
+ caller cancelled `outer` itself -- see _GatheringFuture.cancel above),
75
+ the result/exception is only drained via fut.exception() so asyncio's
76
+ "exception was never retrieved" warning doesn't fire on a future
77
+ nobody will ever await again;
78
+ - if this child failed and `return_exceptions` is False, `outer` is
79
+ failed with that exception and every other still-running child is
80
+ cancelled -- this is the actual "cancel on error" behavior;
81
+ - once every child has reported in (`npending == 0`), results are
82
+ collected in the original awaitables order and `outer` is resolved --
83
+ as a success, or as CancelledError if `outer` was cancelled while
84
+ children were still finishing (see _GatheringFuture.cancel, which
85
+ lets already-running children finish instead of force-stopping them).
86
+
87
+ `awaitable_2_fut` deduplicates: passing the same awaitable twice must not
88
+ schedule it twice or count it twice toward `npending`, since asyncio.gather
89
+ has the same behavior for repeated arguments.
90
+
91
+ On 3.14+, `future_add_to_awaited_by`/`future_discard_from_awaited_by` keep
92
+ each child's "awaited by" introspection link pointed at the *caller's*
93
+ task instead of this function's internal bookkeeping -- otherwise tools
94
+ like asyncio's task graph (or Task.print_stack()) would show the children
95
+ as awaited by a frame that already returned.
96
+ """
97
+
98
+ if not awaitables:
99
+ outer = asyncio.get_event_loop().create_future()
100
+ outer.set_result([])
101
+ return outer
102
+
103
+ if _PY314:
104
+ loop = _get_running_loop()
105
+ current_task = None if loop is None else asyncio.current_task(loop)
106
+ else:
107
+ loop = None
108
+ current_task = None
109
+
110
+
111
+ def _done_callback(fut, current_task = current_task):
112
+ nonlocal npending
113
+ npending -= 1
114
+
115
+ if _PY314 and current_task is not None:
116
+ asyncio.future_discard_from_awaited_by(fut, current_task)
117
+
118
+ if outer is None or outer.done():
119
+ if not fut.cancelled():
120
+ fut.exception() # استهلاك الاستثناء لمنع تحذير asyncio
121
+ return
122
+
123
+ if not return_exceptions:
124
+ try:
125
+ exc = fut.exception()
126
+ except asyncio.CancelledError as e:
127
+ exc = e
128
+
129
+ if exc is not None:
130
+ outer.set_exception(exc)
131
+
132
+ for child in children:
133
+ if child is not fut and not child.done():
134
+ child.cancel()
135
+
136
+ return
137
+
138
+ if npending == 0:
139
+ results = []
140
+
141
+ for child in children:
142
+ try:
143
+ result = child.exception()
144
+ except asyncio.CancelledError as e:
145
+ result = e
146
+
147
+ if result is None:
148
+ result = child.result()
149
+
150
+ results.append(result)
151
+
152
+ if (cancel_message := outer.cancel_message) is not None:
153
+ outer.set_exception(asyncio.CancelledError(cancel_message))
154
+ else:
155
+ outer.set_result(results)
156
+
157
+
158
+
159
+ npending = 0
160
+ outer = None
161
+
162
+ done_futs = []
163
+ children = []
164
+
165
+ awaitable_2_fut = {}
166
+
167
+
168
+ for awaitable in awaitables:
169
+ fut = awaitable_2_fut.get(awaitable)
170
+
171
+ if fut is None:
172
+ fut = asyncio.ensure_future(awaitable, loop=loop)
173
+
174
+ if loop is None:
175
+ loop = _get_fut_loop(fut)
176
+
177
+ if fut is not awaitable:
178
+ fut._log_destroy_pending = False
179
+
180
+ npending += 1
181
+ awaitable_2_fut[awaitable] = fut
182
+
183
+ if fut.done():
184
+ done_futs.append(fut)
185
+ else:
186
+ if _PY314 and current_task is not None:
187
+ asyncio.future_add_to_awaited_by(fut, current_task)
188
+
189
+ fut.add_done_callback(_done_callback)
190
+
191
+ children.append(fut)
192
+
193
+ outer = _GatheringFuture(children, loop=loop)
194
+
195
+ for fut in done_futs:
196
+ _done_callback(fut)
197
+
198
+ return outer
199
+
200
+
201
+
202
+