sputchedtools 0.10.0__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.
- sputchedtools-0.10.0/PKG-INFO +9 -0
- sputchedtools-0.10.0/setup.cfg +4 -0
- sputchedtools-0.10.0/setup.py +17 -0
- sputchedtools-0.10.0/sputchedtools.egg-info/PKG-INFO +9 -0
- sputchedtools-0.10.0/sputchedtools.egg-info/SOURCES.txt +7 -0
- sputchedtools-0.10.0/sputchedtools.egg-info/dependency_links.txt +1 -0
- sputchedtools-0.10.0/sputchedtools.egg-info/requires.txt +2 -0
- sputchedtools-0.10.0/sputchedtools.egg-info/top_level.txt +1 -0
- sputchedtools-0.10.0/sputchedtools.py +276 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name = 'sputchedtools',
|
|
5
|
+
version = '0.10.0',
|
|
6
|
+
packages = find_packages(),
|
|
7
|
+
py_modules = ['sputchedtools'],
|
|
8
|
+
install_requires = [
|
|
9
|
+
'aiohttp>=3.10.6',
|
|
10
|
+
'aiofiles>=24.1.0',
|
|
11
|
+
# uvloop / winloop
|
|
12
|
+
],
|
|
13
|
+
author = 'Sputchik',
|
|
14
|
+
author_email = 'sputchik@gmail.com',
|
|
15
|
+
url = 'https://github.com/Sputchik/sputchedtools',
|
|
16
|
+
python_requires = '>=3.8'
|
|
17
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sputchedtools
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
class Timer:
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
Code execution Timer, use 'with' keyword
|
|
5
|
+
|
|
6
|
+
Accepts:
|
|
7
|
+
txt = '': text after main print message
|
|
8
|
+
decimals = 2: time difference precission
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, txt = '', decimals = 2):
|
|
13
|
+
import time
|
|
14
|
+
self.time = time
|
|
15
|
+
self.txt = txt
|
|
16
|
+
self.decimals = decimals
|
|
17
|
+
|
|
18
|
+
def __enter__(self):
|
|
19
|
+
self.was = self.time.time()
|
|
20
|
+
|
|
21
|
+
def __exit__(self, f, u, c):
|
|
22
|
+
self.diff = format((self.time.time() - self.was), f'.{self.decimals}f')
|
|
23
|
+
print(f'\nTaken time: {self.diff}s {self.txt}')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class aio:
|
|
27
|
+
import asyncio, aiohttp, aiofiles
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
async def request(
|
|
31
|
+
url: str,
|
|
32
|
+
toreturn: str = 'text',
|
|
33
|
+
session = None,
|
|
34
|
+
**kwargs,
|
|
35
|
+
|
|
36
|
+
) -> tuple:
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
Accepts:
|
|
40
|
+
Args:
|
|
41
|
+
url
|
|
42
|
+
Kwargs:
|
|
43
|
+
toreturn: read, text, json
|
|
44
|
+
session: aiohttp.ClientSession
|
|
45
|
+
any other session.get() argument
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Valid response: (data, response.status)
|
|
49
|
+
status == 403: (-2, status)
|
|
50
|
+
status == 521: (-1, status)
|
|
51
|
+
status not in range(200, 400): (None, status)
|
|
52
|
+
|
|
53
|
+
Request Timeout: (0, None)
|
|
54
|
+
Cancelled Error: (None, None)
|
|
55
|
+
Exception: (-3, Exception as e)
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
created_session = False
|
|
61
|
+
if session is None:
|
|
62
|
+
session = aio.aiohttp.ClientSession()
|
|
63
|
+
created_session = True
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
async with session.get(url, **kwargs) as response:
|
|
67
|
+
|
|
68
|
+
if 200 <= response.status < 300 and str(response.url)[-5:] != '/404/':
|
|
69
|
+
status = response.status
|
|
70
|
+
|
|
71
|
+
if toreturn == 'text':
|
|
72
|
+
data = await response.text()
|
|
73
|
+
elif toreturn == 'read':
|
|
74
|
+
data = await response.read()
|
|
75
|
+
elif toreturn == 'json':
|
|
76
|
+
data = await response.json()
|
|
77
|
+
else:
|
|
78
|
+
data = None
|
|
79
|
+
|
|
80
|
+
return data, status
|
|
81
|
+
|
|
82
|
+
elif status == 403:
|
|
83
|
+
# print('\nToo many requests...')
|
|
84
|
+
return -2, status
|
|
85
|
+
|
|
86
|
+
elif status == 521:
|
|
87
|
+
return -1, status
|
|
88
|
+
|
|
89
|
+
else: return None, status
|
|
90
|
+
|
|
91
|
+
except aio.asyncio.TimeoutError:
|
|
92
|
+
return 0, None
|
|
93
|
+
|
|
94
|
+
except aio.asyncio.CancelledError:
|
|
95
|
+
return None, None
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
# print(f'\nFailed to get response from {url}')
|
|
99
|
+
return -3, e
|
|
100
|
+
|
|
101
|
+
finally:
|
|
102
|
+
if created_session is True:
|
|
103
|
+
await session.close()
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
async def post(url, session = None, toreturn = 'json', **kwargs):
|
|
107
|
+
|
|
108
|
+
created_session = False
|
|
109
|
+
if session is None:
|
|
110
|
+
session = aio.aiohttp.ClientSession()
|
|
111
|
+
created_session = True
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
|
|
115
|
+
async with session.post(url, **kwargs) as response:
|
|
116
|
+
status = response.status
|
|
117
|
+
|
|
118
|
+
if 200 <= status < 300 and str(response.url)[-5:] != '/404/':
|
|
119
|
+
|
|
120
|
+
if toreturn == 'text':
|
|
121
|
+
data = await response.text()
|
|
122
|
+
elif toreturn == 'read':
|
|
123
|
+
data = await response.read()
|
|
124
|
+
elif toreturn == 'json':
|
|
125
|
+
data = await response.json()
|
|
126
|
+
else:
|
|
127
|
+
data = None
|
|
128
|
+
|
|
129
|
+
return data, status
|
|
130
|
+
|
|
131
|
+
else:
|
|
132
|
+
return None, status
|
|
133
|
+
|
|
134
|
+
except aio.asyncio.TimeoutError:
|
|
135
|
+
return 0, None
|
|
136
|
+
|
|
137
|
+
except aio.asyncio.CancelledError:
|
|
138
|
+
return None, None
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
# print(f'\nFailed to get response from {url}')
|
|
142
|
+
return -3, e
|
|
143
|
+
|
|
144
|
+
finally:
|
|
145
|
+
if created_session is True:
|
|
146
|
+
await session.close()
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
async def open(file: str, action: str = 'read', mode: str = 'r', content = None, **kwargs):
|
|
150
|
+
async with aio.aiofiles.open(file, mode, **kwargs) as f:
|
|
151
|
+
|
|
152
|
+
if action == 'read': return await f.read()
|
|
153
|
+
|
|
154
|
+
elif action == 'write': return await f.write(content)
|
|
155
|
+
|
|
156
|
+
else: return None
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
async def sem_task(
|
|
160
|
+
semaphore,
|
|
161
|
+
func: callable,
|
|
162
|
+
*args, **kwargs
|
|
163
|
+
):
|
|
164
|
+
|
|
165
|
+
async with semaphore:
|
|
166
|
+
return await func(*args, **kwargs)
|
|
167
|
+
|
|
168
|
+
def enhance_loop():
|
|
169
|
+
import asyncio
|
|
170
|
+
from sys import platform
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
|
|
174
|
+
if 'win' in platform:
|
|
175
|
+
import winloop # type: ignore
|
|
176
|
+
winloop.install()
|
|
177
|
+
|
|
178
|
+
else:
|
|
179
|
+
import uvloop # type: ignore
|
|
180
|
+
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
181
|
+
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
except ImportError:
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
class num:
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def shorten(num: int | float, decimals = 2):
|
|
191
|
+
|
|
192
|
+
suffixes = ['k', 'm', 'b', 't']
|
|
193
|
+
|
|
194
|
+
if not isinstance(num, (int, float)):
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
magnitude = 1000.0
|
|
198
|
+
|
|
199
|
+
sign = '-' if num < 0 else ''
|
|
200
|
+
num = abs(num)
|
|
201
|
+
|
|
202
|
+
if num < magnitude:
|
|
203
|
+
return f"{sign}{num}"
|
|
204
|
+
|
|
205
|
+
for i, suffix in enumerate(suffixes, start=1):
|
|
206
|
+
unit = magnitude ** i
|
|
207
|
+
if num < unit * magnitude:
|
|
208
|
+
num = format(num / unit, f'.{decimals}f')
|
|
209
|
+
return f"{sign}{num}{suffix}"
|
|
210
|
+
|
|
211
|
+
num = format(num / (magnitude ** len(suffixes)), f'.{decimals}f')
|
|
212
|
+
return f"{sign}{num}t"
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def unshorten(num: str) -> float | str:
|
|
216
|
+
|
|
217
|
+
multipliers = {'k': 10**3, 'm': 10**6, 'b': 10**9, 't': 10**12}
|
|
218
|
+
|
|
219
|
+
mp = num[-1].lower()
|
|
220
|
+
digit = num[:-1]
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
digit = float(digit)
|
|
224
|
+
mp = multipliers[mp]
|
|
225
|
+
return digit * mp
|
|
226
|
+
|
|
227
|
+
except (ValueError, IndexError):
|
|
228
|
+
return num
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def decim_round(value: float, decimals: int = 2, precission: int = 20) -> str:
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
Accepts:
|
|
235
|
+
value: float - usually with mid-big decimals length
|
|
236
|
+
decimals: int - determines amount of digits (+2 for rounding, after decimal point) that will be used in 'calculations'
|
|
237
|
+
precission: int - determines precission level (format(value, f'.->{precission}<-f'
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
accepted value:
|
|
241
|
+
if value == 0,
|
|
242
|
+
not isinstance(value & (decimals, precission), float & int)
|
|
243
|
+
decimals & value < 1
|
|
244
|
+
|
|
245
|
+
str-like float
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
if value == 0: return value
|
|
250
|
+
elif not isinstance(value, float): return value
|
|
251
|
+
elif not (decimals > 0 and isinstance(decimals, int)) or not (precission > 0 and isinstance(precission, int)): return value
|
|
252
|
+
|
|
253
|
+
str_val = format(value, f'.{precission}f')
|
|
254
|
+
|
|
255
|
+
integer = str_val.split('.')[0]
|
|
256
|
+
decim = str_val.split('.')[1]
|
|
257
|
+
|
|
258
|
+
if integer != '0':
|
|
259
|
+
i = 0
|
|
260
|
+
|
|
261
|
+
else:
|
|
262
|
+
for i in range(len(decim)):
|
|
263
|
+
if decim[i] != '0': break
|
|
264
|
+
|
|
265
|
+
decim = decim[i:i + decimals + 2].rstrip('0')
|
|
266
|
+
|
|
267
|
+
if decim == '':
|
|
268
|
+
return integer
|
|
269
|
+
|
|
270
|
+
if len(decim) > decimals:
|
|
271
|
+
rounded = str(round(float(decim[:-2] + '.' + decim[-2:]))).rstrip('0')
|
|
272
|
+
decim = '0' * i + rounded
|
|
273
|
+
|
|
274
|
+
else: decim = '0' * i + str(decim)
|
|
275
|
+
|
|
276
|
+
return integer + '.' + decim
|