reykit 1.1.26__py3-none-any.whl → 1.1.28__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.
- reykit/__init__.py +2 -3
- reykit/rall.py +1 -2
- reykit/rbase.py +973 -0
- reykit/rdata.py +3 -5
- reykit/remail.py +1 -2
- reykit/rimage.py +2 -2
- reykit/rlog.py +5 -7
- reykit/rmonkey.py +2 -2
- reykit/rnet.py +1 -2
- reykit/rnum.py +2 -1
- reykit/ros.py +36 -26
- reykit/rrand.py +10 -35
- reykit/rre.py +1 -1
- reykit/rschedule.py +1 -1
- reykit/rstdout.py +2 -3
- reykit/rsys.py +31 -498
- reykit/rtable.py +3 -3
- reykit/rtask.py +9 -34
- reykit/rtext.py +1 -2
- reykit/rtime.py +28 -49
- reykit/rwrap.py +5 -6
- {reykit-1.1.26.dist-info → reykit-1.1.28.dist-info}/METADATA +2 -2
- reykit-1.1.28.dist-info/RECORD +28 -0
- reykit/rexc.py +0 -335
- reykit/rtype.py +0 -130
- reykit-1.1.26.dist-info/RECORD +0 -29
- {reykit-1.1.26.dist-info → reykit-1.1.28.dist-info}/WHEEL +0 -0
- {reykit-1.1.26.dist-info → reykit-1.1.28.dist-info}/licenses/LICENSE +0 -0
reykit/rbase.py
ADDED
@@ -0,0 +1,973 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2024-07-17 09:46:40
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Base methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from typing import Any, Literal, Self, TypeVar, NoReturn, overload
|
13
|
+
from types import TracebackType
|
14
|
+
from collections.abc import Callable, Iterable, Mapping
|
15
|
+
from sys import exc_info as sys_exc_info
|
16
|
+
from os.path import exists as os_exists
|
17
|
+
from traceback import format_exc
|
18
|
+
from warnings import warn as warnings_warn
|
19
|
+
from traceback import format_stack, extract_stack
|
20
|
+
from atexit import register as atexit_register
|
21
|
+
from time import sleep as time_sleep
|
22
|
+
from inspect import signature as inspect_signature, _ParameterKind, _empty
|
23
|
+
from varname import VarnameRetrievingError, argname
|
24
|
+
|
25
|
+
|
26
|
+
__all__ = (
|
27
|
+
'T',
|
28
|
+
'U',
|
29
|
+
'V',
|
30
|
+
'KT',
|
31
|
+
'VT',
|
32
|
+
'Base',
|
33
|
+
'StaticMeta',
|
34
|
+
'ConfigMeta',
|
35
|
+
'Singleton',
|
36
|
+
'Null',
|
37
|
+
'null',
|
38
|
+
'BaseError',
|
39
|
+
'Exit',
|
40
|
+
'Error',
|
41
|
+
'throw',
|
42
|
+
'warn',
|
43
|
+
'catch_exc',
|
44
|
+
'check_least_one',
|
45
|
+
'check_most_one',
|
46
|
+
'check_file_found',
|
47
|
+
'check_file_exist',
|
48
|
+
'check_response_code',
|
49
|
+
'is_class',
|
50
|
+
'is_instance',
|
51
|
+
'is_iterable',
|
52
|
+
'is_table',
|
53
|
+
'is_num_str',
|
54
|
+
'get_first_notnone',
|
55
|
+
'get_stack_text',
|
56
|
+
'get_stack_param',
|
57
|
+
'get_arg_info',
|
58
|
+
'get_name',
|
59
|
+
'block',
|
60
|
+
'at_exit'
|
61
|
+
)
|
62
|
+
|
63
|
+
|
64
|
+
# Generic.
|
65
|
+
T = TypeVar('T') # Any.
|
66
|
+
U = TypeVar('U') # Any.
|
67
|
+
V = TypeVar('V') # Any.
|
68
|
+
KT = TypeVar('KT') # Any dictionary key.
|
69
|
+
VT = TypeVar('VT') # Any dictionary value.
|
70
|
+
|
71
|
+
|
72
|
+
class Base(object):
|
73
|
+
"""
|
74
|
+
Base type.
|
75
|
+
"""
|
76
|
+
|
77
|
+
def __getitem__(self, name: str) -> Any:
|
78
|
+
"""
|
79
|
+
Get Attribute.
|
80
|
+
|
81
|
+
Parameters
|
82
|
+
----------
|
83
|
+
name : Attribute name.
|
84
|
+
|
85
|
+
Returns
|
86
|
+
-------
|
87
|
+
Attribute value.
|
88
|
+
"""
|
89
|
+
|
90
|
+
# Get.
|
91
|
+
value = getattr(self, name)
|
92
|
+
|
93
|
+
return value
|
94
|
+
|
95
|
+
|
96
|
+
def __setitem__(self, name: str, value: Any) -> None:
|
97
|
+
"""
|
98
|
+
Set Attribute.
|
99
|
+
|
100
|
+
Parameters
|
101
|
+
----------
|
102
|
+
name : Attribute name.
|
103
|
+
value : Attribute value.
|
104
|
+
"""
|
105
|
+
|
106
|
+
# Set.
|
107
|
+
setattr(self, name, value)
|
108
|
+
|
109
|
+
|
110
|
+
def __delitem__(self, name: str) -> None:
|
111
|
+
"""
|
112
|
+
Delete Attribute.
|
113
|
+
|
114
|
+
Parameters
|
115
|
+
----------
|
116
|
+
name : Attribute name.
|
117
|
+
"""
|
118
|
+
|
119
|
+
# Delete.
|
120
|
+
delattr(self, name)
|
121
|
+
|
122
|
+
|
123
|
+
class StaticMeta(Base, type):
|
124
|
+
"""
|
125
|
+
Static meta type.
|
126
|
+
"""
|
127
|
+
|
128
|
+
|
129
|
+
def __call__(cls):
|
130
|
+
"""
|
131
|
+
Call method.
|
132
|
+
"""
|
133
|
+
|
134
|
+
# Throw exception.
|
135
|
+
raise TypeError('static class, no instances allowed.')
|
136
|
+
|
137
|
+
|
138
|
+
class ConfigMeta(StaticMeta):
|
139
|
+
"""
|
140
|
+
Config meta type.
|
141
|
+
"""
|
142
|
+
|
143
|
+
|
144
|
+
def __getitem__(cls, name: str) -> Any:
|
145
|
+
"""
|
146
|
+
Get item.
|
147
|
+
|
148
|
+
Parameters
|
149
|
+
----------
|
150
|
+
name : Item name.
|
151
|
+
|
152
|
+
Returns
|
153
|
+
-------
|
154
|
+
Item value.
|
155
|
+
"""
|
156
|
+
|
157
|
+
# Get.
|
158
|
+
item = getattr(cls, name)
|
159
|
+
|
160
|
+
return item
|
161
|
+
|
162
|
+
|
163
|
+
def __setitem__(cls, name: str, value: Any) -> None:
|
164
|
+
"""
|
165
|
+
Set item.
|
166
|
+
|
167
|
+
Parameters
|
168
|
+
----------
|
169
|
+
name : Item name.
|
170
|
+
"""
|
171
|
+
|
172
|
+
# Set.
|
173
|
+
setattr(cls, name, value)
|
174
|
+
|
175
|
+
|
176
|
+
class Singleton(Base):
|
177
|
+
"""
|
178
|
+
Singleton type.
|
179
|
+
When instantiated, method `__singleton__` will be called only once, and will accept arguments.
|
180
|
+
|
181
|
+
Attributes
|
182
|
+
----------
|
183
|
+
_instance : Global singleton instance.
|
184
|
+
"""
|
185
|
+
|
186
|
+
_instance: Self | None = None
|
187
|
+
|
188
|
+
|
189
|
+
def __new__(self, *arg: Any, **kwargs: Any) -> Self:
|
190
|
+
"""
|
191
|
+
Build `singleton` instance.
|
192
|
+
"""
|
193
|
+
|
194
|
+
# Build.
|
195
|
+
if self._instance is None:
|
196
|
+
self._instance = super().__new__(self)
|
197
|
+
|
198
|
+
## Singleton method.
|
199
|
+
if hasattr(self, "__singleton__"):
|
200
|
+
__singleton__: Callable = getattr(self, "__singleton__")
|
201
|
+
__singleton__(self, *arg, **kwargs)
|
202
|
+
|
203
|
+
return self._instance
|
204
|
+
|
205
|
+
|
206
|
+
class Null(Singleton):
|
207
|
+
"""
|
208
|
+
Null type.
|
209
|
+
"""
|
210
|
+
|
211
|
+
|
212
|
+
null = Null()
|
213
|
+
|
214
|
+
|
215
|
+
class BaseError(Base, BaseException):
|
216
|
+
"""
|
217
|
+
Base error type.
|
218
|
+
"""
|
219
|
+
|
220
|
+
|
221
|
+
class Exit(BaseError):
|
222
|
+
"""
|
223
|
+
Exit type.
|
224
|
+
"""
|
225
|
+
|
226
|
+
|
227
|
+
class Error(BaseError):
|
228
|
+
"""
|
229
|
+
Error type.
|
230
|
+
"""
|
231
|
+
|
232
|
+
|
233
|
+
def throw(
|
234
|
+
exception: type[BaseException] = AssertionError,
|
235
|
+
value: Any = null,
|
236
|
+
*values: Any,
|
237
|
+
text: str | None = None,
|
238
|
+
frame: int = 2
|
239
|
+
) -> NoReturn:
|
240
|
+
"""
|
241
|
+
Throw exception.
|
242
|
+
|
243
|
+
Parameters
|
244
|
+
----------
|
245
|
+
exception : Exception Type.
|
246
|
+
value : Exception value.
|
247
|
+
values : Exception values.
|
248
|
+
text : Exception text.
|
249
|
+
frame : Number of code to upper level.
|
250
|
+
"""
|
251
|
+
|
252
|
+
# Text.
|
253
|
+
if text is None:
|
254
|
+
if exception.__doc__ is not None:
|
255
|
+
text = exception.__doc__.strip()
|
256
|
+
if (
|
257
|
+
text is None
|
258
|
+
or text == ''
|
259
|
+
):
|
260
|
+
text = 'use error'
|
261
|
+
else:
|
262
|
+
text = text[0].lower() + text[1:]
|
263
|
+
|
264
|
+
## Value.
|
265
|
+
if value != null:
|
266
|
+
values = (value,) + values
|
267
|
+
|
268
|
+
### Name.
|
269
|
+
name = get_name(value, frame)
|
270
|
+
names = (name,)
|
271
|
+
if values != ():
|
272
|
+
names_values = get_name(values)
|
273
|
+
if names_values is not None:
|
274
|
+
names += names_values
|
275
|
+
|
276
|
+
### Convert.
|
277
|
+
match exception:
|
278
|
+
case TypeError():
|
279
|
+
values = [
|
280
|
+
type(value)
|
281
|
+
for value in values
|
282
|
+
if value is not None
|
283
|
+
]
|
284
|
+
case TimeoutError():
|
285
|
+
values = [
|
286
|
+
int(value)
|
287
|
+
if value % 1 == 0
|
288
|
+
else round(value, 3)
|
289
|
+
for value in values
|
290
|
+
if type(value) == float
|
291
|
+
]
|
292
|
+
values = [
|
293
|
+
repr(value)
|
294
|
+
for value in values
|
295
|
+
]
|
296
|
+
|
297
|
+
### Join.
|
298
|
+
if names == ():
|
299
|
+
values_len = len(values)
|
300
|
+
text_value = ', '.join(values)
|
301
|
+
if values_len == 1:
|
302
|
+
text_value = 'value is ' + text_value
|
303
|
+
else:
|
304
|
+
text_value = 'values is (%s)' % text_value
|
305
|
+
else:
|
306
|
+
names_values = zip(names, values)
|
307
|
+
text_value = ', '.join(
|
308
|
+
[
|
309
|
+
'parameter "%s" is %s' % (name, value)
|
310
|
+
for name, value in names_values
|
311
|
+
]
|
312
|
+
)
|
313
|
+
text += ' %s.' % text_value
|
314
|
+
|
315
|
+
# Throw exception.
|
316
|
+
exception = exception(text)
|
317
|
+
raise exception
|
318
|
+
|
319
|
+
|
320
|
+
def warn(
|
321
|
+
*infos: Any,
|
322
|
+
exception: type[BaseException] = UserWarning,
|
323
|
+
stacklevel: int = 3
|
324
|
+
) -> None:
|
325
|
+
"""
|
326
|
+
Throw warning.
|
327
|
+
|
328
|
+
Parameters
|
329
|
+
----------
|
330
|
+
infos : Warn informations.
|
331
|
+
exception : Exception type.
|
332
|
+
stacklevel : Warning code location, number of recursions up the code level.
|
333
|
+
"""
|
334
|
+
|
335
|
+
# Handle parameter.
|
336
|
+
if infos == ():
|
337
|
+
infos = 'Warning!'
|
338
|
+
elif len(infos) == 1:
|
339
|
+
if type(infos[0]) == str:
|
340
|
+
infos = infos[0]
|
341
|
+
else:
|
342
|
+
infos = str(infos[0])
|
343
|
+
else:
|
344
|
+
infos = str(infos)
|
345
|
+
|
346
|
+
# Throw warning.
|
347
|
+
warnings_warn(infos, exception, stacklevel)
|
348
|
+
|
349
|
+
|
350
|
+
def catch_exc(
|
351
|
+
title: str | None = None
|
352
|
+
) -> tuple[str, type[BaseException], BaseException, TracebackType]:
|
353
|
+
"""
|
354
|
+
Catch exception information and print, must used in `except` syntax.
|
355
|
+
|
356
|
+
Parameters
|
357
|
+
----------
|
358
|
+
title : Print title.
|
359
|
+
- `None`: Not print.
|
360
|
+
- `str`: Print and use this title.
|
361
|
+
|
362
|
+
Returns
|
363
|
+
-------
|
364
|
+
Exception data.
|
365
|
+
- `str`: Exception report text.
|
366
|
+
- `type[BaseException]`: Exception type.
|
367
|
+
- `BaseException`: Exception instance.
|
368
|
+
- `TracebackType`: Exception traceback instance.
|
369
|
+
"""
|
370
|
+
|
371
|
+
# Get parameter.
|
372
|
+
exc_report = format_exc()
|
373
|
+
exc_report = exc_report.strip()
|
374
|
+
exc_type, exc_instance, exc_traceback = sys_exc_info()
|
375
|
+
|
376
|
+
# Print.
|
377
|
+
if title is not None:
|
378
|
+
|
379
|
+
## Import.
|
380
|
+
from .rstdout import echo
|
381
|
+
|
382
|
+
## Execute.
|
383
|
+
echo(exc_report, title=title, frame='half')
|
384
|
+
|
385
|
+
return exc_report, exc_type, exc_instance, exc_traceback
|
386
|
+
|
387
|
+
|
388
|
+
@overload
|
389
|
+
def check_least_one(*values: None) -> NoReturn: ...
|
390
|
+
|
391
|
+
@overload
|
392
|
+
def check_least_one(*values: Any) -> None: ...
|
393
|
+
|
394
|
+
def check_least_one(*values: Any) -> None:
|
395
|
+
"""
|
396
|
+
Check that at least one of multiple values is not null, when check fail, then throw exception.
|
397
|
+
|
398
|
+
Parameters
|
399
|
+
----------
|
400
|
+
values : Check values.
|
401
|
+
"""
|
402
|
+
|
403
|
+
# Check.
|
404
|
+
for value in values:
|
405
|
+
if value is not None:
|
406
|
+
return
|
407
|
+
|
408
|
+
# Throw exception.
|
409
|
+
vars_name = get_name(values)
|
410
|
+
if vars_name is not None:
|
411
|
+
vars_name_de_dup = list(set(vars_name))
|
412
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
413
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
414
|
+
else:
|
415
|
+
vars_name_str = ''
|
416
|
+
raise TypeError(f'at least one of parameters{vars_name_str} is not None')
|
417
|
+
|
418
|
+
|
419
|
+
def check_most_one(*values: Any) -> None:
|
420
|
+
"""
|
421
|
+
Check that at most one of multiple values is not null, when check fail, then throw exception.
|
422
|
+
|
423
|
+
Parameters
|
424
|
+
----------
|
425
|
+
values : Check values.
|
426
|
+
"""
|
427
|
+
|
428
|
+
# Check.
|
429
|
+
exist = False
|
430
|
+
for value in values:
|
431
|
+
if value is not None:
|
432
|
+
if exist is True:
|
433
|
+
|
434
|
+
# Throw exception.
|
435
|
+
vars_name = get_name(values)
|
436
|
+
if vars_name is not None:
|
437
|
+
vars_name_de_dup = list(set(vars_name))
|
438
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
439
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
440
|
+
else:
|
441
|
+
vars_name_str = ''
|
442
|
+
raise TypeError(f'at most one of parameters{vars_name_str} is not None')
|
443
|
+
|
444
|
+
exist = True
|
445
|
+
|
446
|
+
|
447
|
+
def check_file_found(path: str) -> None:
|
448
|
+
"""
|
449
|
+
Check if file path found, if not, throw exception.
|
450
|
+
|
451
|
+
Parameters
|
452
|
+
----------
|
453
|
+
path : File path.
|
454
|
+
"""
|
455
|
+
|
456
|
+
# Check.
|
457
|
+
exist = os_exists(path)
|
458
|
+
|
459
|
+
# Throw exception.
|
460
|
+
if not exist:
|
461
|
+
throw(FileNotFoundError, path)
|
462
|
+
|
463
|
+
|
464
|
+
def check_file_exist(path: str) -> None:
|
465
|
+
"""
|
466
|
+
Check if file path exist, if exist, throw exception.
|
467
|
+
|
468
|
+
Parameters
|
469
|
+
----------
|
470
|
+
path : File path.
|
471
|
+
"""
|
472
|
+
|
473
|
+
# Check.
|
474
|
+
exist = os_exists(path)
|
475
|
+
|
476
|
+
# Throw exception.
|
477
|
+
if exist:
|
478
|
+
throw(FileExistsError, path)
|
479
|
+
|
480
|
+
|
481
|
+
def check_response_code(
|
482
|
+
code: int,
|
483
|
+
range_: int | Iterable[int] | None = None
|
484
|
+
) -> bool:
|
485
|
+
"""
|
486
|
+
Check if the response code is in range.
|
487
|
+
|
488
|
+
Parameters
|
489
|
+
----------
|
490
|
+
code : Response code.
|
491
|
+
range_ : Pass the code range.
|
492
|
+
- `None`: Check if is between 200 and 299.
|
493
|
+
- `int`: Check if is this value.
|
494
|
+
- `Iterable`: Check if is in sequence.
|
495
|
+
|
496
|
+
Returns
|
497
|
+
-------
|
498
|
+
Check result.
|
499
|
+
"""
|
500
|
+
|
501
|
+
# Check.
|
502
|
+
match range_:
|
503
|
+
case None:
|
504
|
+
result = code // 100 == 2
|
505
|
+
case int():
|
506
|
+
result = code == range_
|
507
|
+
case _ if hasattr(range_, '__contains__'):
|
508
|
+
result = code in range_
|
509
|
+
case _:
|
510
|
+
throw(TypeError, range_)
|
511
|
+
|
512
|
+
# Throw exception.
|
513
|
+
if not result:
|
514
|
+
throw(value=code)
|
515
|
+
|
516
|
+
return result
|
517
|
+
|
518
|
+
|
519
|
+
def is_class(obj: Any) -> bool:
|
520
|
+
"""
|
521
|
+
Judge whether it is class.
|
522
|
+
|
523
|
+
Parameters
|
524
|
+
----------
|
525
|
+
obj : Judge object.
|
526
|
+
|
527
|
+
Returns
|
528
|
+
-------
|
529
|
+
Judgment result.
|
530
|
+
"""
|
531
|
+
|
532
|
+
# Judge.
|
533
|
+
judge = isinstance(obj, type)
|
534
|
+
|
535
|
+
return judge
|
536
|
+
|
537
|
+
|
538
|
+
def is_instance(obj: Any) -> bool:
|
539
|
+
"""
|
540
|
+
Judge whether it is instance.
|
541
|
+
|
542
|
+
Parameters
|
543
|
+
----------
|
544
|
+
obj : Judge object.
|
545
|
+
|
546
|
+
Returns
|
547
|
+
-------
|
548
|
+
Judgment result.
|
549
|
+
"""
|
550
|
+
|
551
|
+
# judge.
|
552
|
+
judge = not is_class(obj)
|
553
|
+
|
554
|
+
return judge
|
555
|
+
|
556
|
+
|
557
|
+
def is_iterable(
|
558
|
+
obj: Any,
|
559
|
+
exclude_types: Iterable[type] | None = None
|
560
|
+
) -> bool:
|
561
|
+
"""
|
562
|
+
Judge whether it is iterable.
|
563
|
+
|
564
|
+
Parameters
|
565
|
+
----------
|
566
|
+
obj : Judge object.
|
567
|
+
exclude_types : Non iterative types.
|
568
|
+
|
569
|
+
Returns
|
570
|
+
-------
|
571
|
+
Judgment result.
|
572
|
+
"""
|
573
|
+
|
574
|
+
# Judge.
|
575
|
+
if (
|
576
|
+
hasattr(obj, '__iter__')
|
577
|
+
and not (
|
578
|
+
exclude_types is not None
|
579
|
+
and type(obj) in exclude_types
|
580
|
+
)
|
581
|
+
):
|
582
|
+
return True
|
583
|
+
|
584
|
+
return False
|
585
|
+
|
586
|
+
|
587
|
+
def is_table(
|
588
|
+
obj: Any,
|
589
|
+
check_fields: bool = True
|
590
|
+
) -> bool:
|
591
|
+
"""
|
592
|
+
Judge whether it is `list[dict]` table format and keys and keys sort of the dict are the same.
|
593
|
+
|
594
|
+
Parameters
|
595
|
+
----------
|
596
|
+
obj : Judge object.
|
597
|
+
check_fields : Do you want to check the keys and keys sort of the dict are the same.
|
598
|
+
|
599
|
+
Returns
|
600
|
+
-------
|
601
|
+
Judgment result.
|
602
|
+
"""
|
603
|
+
|
604
|
+
# Judge.
|
605
|
+
if type(obj) != list:
|
606
|
+
return False
|
607
|
+
for element in obj:
|
608
|
+
if type(element) != dict:
|
609
|
+
return False
|
610
|
+
|
611
|
+
## Check fields of table.
|
612
|
+
if check_fields:
|
613
|
+
keys_strs = [
|
614
|
+
':'.join([str(key) for key in element.keys()])
|
615
|
+
for element in obj
|
616
|
+
]
|
617
|
+
keys_strs_only = set(keys_strs)
|
618
|
+
if len(keys_strs_only) != 1:
|
619
|
+
return False
|
620
|
+
|
621
|
+
return True
|
622
|
+
|
623
|
+
|
624
|
+
def is_num_str(
|
625
|
+
string: str
|
626
|
+
) -> bool:
|
627
|
+
"""
|
628
|
+
Judge whether it is number string.
|
629
|
+
|
630
|
+
Parameters
|
631
|
+
----------
|
632
|
+
string : String.
|
633
|
+
|
634
|
+
Returns
|
635
|
+
-------
|
636
|
+
Judgment result.
|
637
|
+
"""
|
638
|
+
|
639
|
+
# Judge.
|
640
|
+
try:
|
641
|
+
float(string)
|
642
|
+
except (ValueError, TypeError):
|
643
|
+
return False
|
644
|
+
|
645
|
+
return True
|
646
|
+
|
647
|
+
|
648
|
+
@overload
|
649
|
+
def get_first_notnone(*values: None, default: T) -> T: ...
|
650
|
+
|
651
|
+
@overload
|
652
|
+
def get_first_notnone(*values: None) -> NoReturn: ...
|
653
|
+
|
654
|
+
@overload
|
655
|
+
def get_first_notnone(*values: T) -> T: ...
|
656
|
+
|
657
|
+
def get_first_notnone(*values: T, default: U = null) -> T | U:
|
658
|
+
"""
|
659
|
+
Get the first value that is not `None`.
|
660
|
+
|
661
|
+
Parameters
|
662
|
+
----------
|
663
|
+
values : Check values.
|
664
|
+
default : When all are `None`, then return this is value, or throw exception.
|
665
|
+
- `Any`: Return this is value.
|
666
|
+
- `Literal['exception']`: Throw exception.
|
667
|
+
|
668
|
+
Returns
|
669
|
+
-------
|
670
|
+
Return first not `None` value, when all are `None`, then return default value.
|
671
|
+
"""
|
672
|
+
|
673
|
+
# Get value.
|
674
|
+
for value in values:
|
675
|
+
if value is not None:
|
676
|
+
return value
|
677
|
+
|
678
|
+
# Throw exception.
|
679
|
+
if default == null:
|
680
|
+
vars_name = get_name(values)
|
681
|
+
if vars_name is not None:
|
682
|
+
vars_name_de_dup = list(set(vars_name))
|
683
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
684
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
685
|
+
else:
|
686
|
+
vars_name_str = ''
|
687
|
+
text = f'at least one of parameters{vars_name_str} is not None'
|
688
|
+
throw(ValueError, text=text)
|
689
|
+
|
690
|
+
return default
|
691
|
+
|
692
|
+
|
693
|
+
def get_stack_text(format_: Literal['plain', 'full'] = 'plain', limit: int = 2) -> str:
|
694
|
+
"""
|
695
|
+
Get code stack text.
|
696
|
+
|
697
|
+
Parameters
|
698
|
+
----------
|
699
|
+
format_ : Stack text format.
|
700
|
+
- `Literal['plain']`: Floor stack position.
|
701
|
+
- `Literal['full']`: Full stack information.
|
702
|
+
limit : Stack limit level.
|
703
|
+
|
704
|
+
Returns
|
705
|
+
-------
|
706
|
+
Code stack text.
|
707
|
+
"""
|
708
|
+
|
709
|
+
# Get.
|
710
|
+
match format_:
|
711
|
+
|
712
|
+
## Plain.
|
713
|
+
case 'plain':
|
714
|
+
limit += 1
|
715
|
+
stacks = format_stack(limit=limit)
|
716
|
+
|
717
|
+
### Check.
|
718
|
+
if len(stacks) != limit:
|
719
|
+
throw(value=limit)
|
720
|
+
|
721
|
+
### Convert.
|
722
|
+
text = stacks[0]
|
723
|
+
index_end = text.find(', in ')
|
724
|
+
text = text[2:index_end]
|
725
|
+
|
726
|
+
## Full.
|
727
|
+
case 'full':
|
728
|
+
stacks = format_stack()
|
729
|
+
index_limit = len(stacks) - limit
|
730
|
+
stacks = stacks[:index_limit]
|
731
|
+
|
732
|
+
### Check.
|
733
|
+
if len(stacks) == 0:
|
734
|
+
throw(value=limit)
|
735
|
+
|
736
|
+
### Convert.
|
737
|
+
stacks = [
|
738
|
+
stack[2:].replace('\n ', '\n', 1)
|
739
|
+
for stack in stacks
|
740
|
+
]
|
741
|
+
text = ''.join(stacks)
|
742
|
+
text = text[:-1]
|
743
|
+
|
744
|
+
## Throw exception.
|
745
|
+
case _:
|
746
|
+
throw(ValueError, format_)
|
747
|
+
|
748
|
+
return text
|
749
|
+
|
750
|
+
|
751
|
+
@overload
|
752
|
+
def get_stack_param(format_: Literal['floor'] = 'floor', limit: int = 2) -> dict: ...
|
753
|
+
|
754
|
+
@overload
|
755
|
+
def get_stack_param(format_: Literal['full'], limit: int = 2) -> list[dict]: ...
|
756
|
+
|
757
|
+
def get_stack_param(format_: Literal['floor', 'full'] = 'floor', limit: int = 2) -> dict | list[dict]:
|
758
|
+
"""
|
759
|
+
Get code stack parameters.
|
760
|
+
|
761
|
+
Parameters
|
762
|
+
----------
|
763
|
+
format_ : Stack parameters format.
|
764
|
+
- `Literal['floor']`: Floor stack parameters.
|
765
|
+
- `Literal['full']`: Full stack parameters.
|
766
|
+
limit : Stack limit level.
|
767
|
+
|
768
|
+
Returns
|
769
|
+
-------
|
770
|
+
Code stack parameters.
|
771
|
+
"""
|
772
|
+
|
773
|
+
# Get.
|
774
|
+
stacks = extract_stack()
|
775
|
+
index_limit = len(stacks) - limit
|
776
|
+
stacks = stacks[:index_limit]
|
777
|
+
|
778
|
+
# Check.
|
779
|
+
if len(stacks) == 0:
|
780
|
+
throw(value=limit)
|
781
|
+
|
782
|
+
# Convert.
|
783
|
+
match format_:
|
784
|
+
|
785
|
+
## Floor.
|
786
|
+
case 'floor':
|
787
|
+
stack = stacks[-1]
|
788
|
+
params = {
|
789
|
+
'filename': stack.filename,
|
790
|
+
'lineno': stack.lineno,
|
791
|
+
'name': stack.name,
|
792
|
+
'line': stack.line
|
793
|
+
}
|
794
|
+
|
795
|
+
## Full.
|
796
|
+
case 'full':
|
797
|
+
params = [
|
798
|
+
{
|
799
|
+
'filename': stack.filename,
|
800
|
+
'lineno': stack.lineno,
|
801
|
+
'name': stack.name,
|
802
|
+
'line': stack.line
|
803
|
+
}
|
804
|
+
for stack in stacks
|
805
|
+
]
|
806
|
+
|
807
|
+
return params
|
808
|
+
|
809
|
+
|
810
|
+
def get_arg_info(func: Callable) -> list[
|
811
|
+
dict[
|
812
|
+
Literal['name', 'type', 'annotation', 'default'],
|
813
|
+
str | None
|
814
|
+
]
|
815
|
+
]:
|
816
|
+
"""
|
817
|
+
Get function arguments information.
|
818
|
+
|
819
|
+
Parameters
|
820
|
+
----------
|
821
|
+
func : Function.
|
822
|
+
|
823
|
+
Returns
|
824
|
+
-------
|
825
|
+
Arguments information.
|
826
|
+
- `Value of key 'name'`: Argument name.
|
827
|
+
- `Value of key 'type'`: Argument bind type.
|
828
|
+
`Literal['position_or_keyword']`: Is positional argument or keyword argument.
|
829
|
+
`Literal['var_position']`: Is variable length positional argument.
|
830
|
+
`Literal['var_keyword']`: Is variable length keyword argument.
|
831
|
+
`Literal['only_position']`: Is positional only argument.
|
832
|
+
`Literal['only_keyword']`: Is keyword only argument.
|
833
|
+
- `Value of key 'annotation'`: Argument annotation.
|
834
|
+
- `Value of key 'default'`: Argument default value.
|
835
|
+
"""
|
836
|
+
|
837
|
+
# Get signature.
|
838
|
+
signature = inspect_signature(func)
|
839
|
+
|
840
|
+
# Get information.
|
841
|
+
info = [
|
842
|
+
{
|
843
|
+
'name': name,
|
844
|
+
'type': (
|
845
|
+
'position_or_keyword'
|
846
|
+
if parameter.kind == _ParameterKind.POSITIONAL_OR_KEYWORD
|
847
|
+
else 'var_position'
|
848
|
+
if parameter.kind == _ParameterKind.VAR_POSITIONAL
|
849
|
+
else 'var_keyword'
|
850
|
+
if parameter.kind == _ParameterKind.VAR_KEYWORD
|
851
|
+
else 'only_position'
|
852
|
+
if parameter.kind == _ParameterKind.POSITIONAL_ONLY
|
853
|
+
else 'only_keyword'
|
854
|
+
if parameter.kind == _ParameterKind.KEYWORD_ONLY
|
855
|
+
else None
|
856
|
+
),
|
857
|
+
'annotation': parameter.annotation,
|
858
|
+
'default': parameter.default
|
859
|
+
}
|
860
|
+
for name, parameter in signature.parameters.items()
|
861
|
+
]
|
862
|
+
|
863
|
+
# Replace empty.
|
864
|
+
for row in info:
|
865
|
+
for key, value in row.items():
|
866
|
+
if value == _empty:
|
867
|
+
row[key] = None
|
868
|
+
|
869
|
+
return info
|
870
|
+
|
871
|
+
|
872
|
+
@overload
|
873
|
+
def get_name(obj: tuple, frame: int = 2) -> tuple[str, ...] | None: ...
|
874
|
+
|
875
|
+
@overload
|
876
|
+
def get_name(obj: Any, frame: int = 2) -> str | None: ...
|
877
|
+
|
878
|
+
def get_name(obj: Any, frame: int = 2) -> str | tuple[str, ...] | None:
|
879
|
+
"""
|
880
|
+
Get name of object or variable.
|
881
|
+
|
882
|
+
Parameters
|
883
|
+
----------
|
884
|
+
obj : Object.
|
885
|
+
- `tuple`: Variable length position parameter of previous layer.
|
886
|
+
- `Any`: Parameter of any layer.
|
887
|
+
frame : Number of code to upper level.
|
888
|
+
|
889
|
+
Returns
|
890
|
+
-------
|
891
|
+
Name or None.
|
892
|
+
"""
|
893
|
+
|
894
|
+
# Get name using built in method.
|
895
|
+
if hasattr(obj, '__name__'):
|
896
|
+
name = obj.__name__
|
897
|
+
return name
|
898
|
+
|
899
|
+
# Get name using module method.
|
900
|
+
name = 'obj'
|
901
|
+
for frame_ in range(1, frame + 1):
|
902
|
+
if type(name) != str:
|
903
|
+
return
|
904
|
+
try:
|
905
|
+
name = argname(name, frame=frame_)
|
906
|
+
except VarnameRetrievingError:
|
907
|
+
return
|
908
|
+
if type(name) == tuple:
|
909
|
+
for element in name:
|
910
|
+
if type(element) != str:
|
911
|
+
return
|
912
|
+
|
913
|
+
return name
|
914
|
+
|
915
|
+
|
916
|
+
def block() -> None:
|
917
|
+
"""
|
918
|
+
Blocking program, can be double press interrupt to end blocking.
|
919
|
+
"""
|
920
|
+
|
921
|
+
# Start.
|
922
|
+
print('Start blocking.')
|
923
|
+
while True:
|
924
|
+
try:
|
925
|
+
time_sleep(1)
|
926
|
+
except KeyboardInterrupt:
|
927
|
+
|
928
|
+
# Confirm.
|
929
|
+
try:
|
930
|
+
print('Double press interrupt to end blocking.')
|
931
|
+
time_sleep(1)
|
932
|
+
|
933
|
+
# End.
|
934
|
+
except KeyboardInterrupt:
|
935
|
+
print('End blocking.')
|
936
|
+
break
|
937
|
+
|
938
|
+
except:
|
939
|
+
continue
|
940
|
+
|
941
|
+
|
942
|
+
def at_exit(*contents: str | Callable | tuple[Callable, Iterable, Mapping]) -> list[Callable]:
|
943
|
+
"""
|
944
|
+
At exiting print text or execute function.
|
945
|
+
|
946
|
+
Parameters
|
947
|
+
----------
|
948
|
+
contents : execute contents.
|
949
|
+
- `str`: Define the print text function and execute it.
|
950
|
+
- `Callable`: Execute function.
|
951
|
+
- `tuple[Callable, Iterable, Mapping]`: Execute function and position arguments and keyword arguments.
|
952
|
+
|
953
|
+
Returns
|
954
|
+
-------
|
955
|
+
Execute functions.
|
956
|
+
"""
|
957
|
+
|
958
|
+
# Register.
|
959
|
+
funcs = []
|
960
|
+
for content in reversed(contents):
|
961
|
+
args = ()
|
962
|
+
kwargs = {}
|
963
|
+
if type(content) == str:
|
964
|
+
func = lambda : print(content)
|
965
|
+
elif callable(content):
|
966
|
+
func = content
|
967
|
+
elif type(content) == tuple:
|
968
|
+
func, args, kwargs = content
|
969
|
+
funcs.append(func)
|
970
|
+
atexit_register(func, *args, **kwargs)
|
971
|
+
funcs = list(reversed(funcs))
|
972
|
+
|
973
|
+
return funcs
|