reykit 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.
- reykit/__init__.py +41 -0
- reykit/rall.py +33 -0
- reykit/rcomm.py +431 -0
- reykit/rdata.py +395 -0
- reykit/rdll/__init__.py +17 -0
- reykit/rdll/rdll_inject.py +41 -0
- reykit/rdll/rdll_inject_core.py +202 -0
- reykit/remail.py +276 -0
- reykit/rexception.py +339 -0
- reykit/rimage.py +261 -0
- reykit/rlog.py +1061 -0
- reykit/rmonkey.py +341 -0
- reykit/rmultitask.py +871 -0
- reykit/rnumber.py +161 -0
- reykit/ros.py +1917 -0
- reykit/rrandom.py +351 -0
- reykit/rregex.py +293 -0
- reykit/rschedule.py +272 -0
- reykit/rstdout.py +356 -0
- reykit/rsystem.py +1180 -0
- reykit/rtable.py +511 -0
- reykit/rtext.py +458 -0
- reykit/rtime.py +678 -0
- reykit/rtype.py +106 -0
- reykit/rwrap.py +613 -0
- reykit/rzip.py +137 -0
- reykit-1.0.0.dist-info/METADATA +29 -0
- reykit-1.0.0.dist-info/RECORD +30 -0
- reykit-1.0.0.dist-info/WHEEL +5 -0
- reykit-1.0.0.dist-info/top_level.txt +1 -0
reykit/rsystem.py
ADDED
@@ -0,0 +1,1180 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2022-12-05 14:09:42
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Interpreter system methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from typing import Any, TypedDict, Literal, Optional, Union, overload
|
13
|
+
from collections.abc import Callable, Iterable, Sequence
|
14
|
+
from inspect import signature as inspect_signature, _ParameterKind, _empty
|
15
|
+
from sys import path as sys_path, modules as sys_modules
|
16
|
+
from os import getpid as os_getpid
|
17
|
+
from os.path import abspath as os_abspath
|
18
|
+
from psutil import (
|
19
|
+
boot_time as psutil_boot_time,
|
20
|
+
cpu_count as psutil_cpu_count,
|
21
|
+
cpu_freq as psutil_cpu_freq,
|
22
|
+
cpu_percent as psutil_cpu_percent,
|
23
|
+
virtual_memory as psutil_virtual_memory,
|
24
|
+
disk_partitions as psutil_disk_partitions,
|
25
|
+
disk_usage as psutil_disk_usage,
|
26
|
+
pids as psutil_pids,
|
27
|
+
net_connections as psutil_net_connections,
|
28
|
+
users as psutil_users,
|
29
|
+
net_connections as psutil_net_connections,
|
30
|
+
process_iter as psutil_process_iter,
|
31
|
+
pid_exists as psutil_pid_exists,
|
32
|
+
Process
|
33
|
+
)
|
34
|
+
from traceback import format_stack, extract_stack
|
35
|
+
from subprocess import Popen, PIPE
|
36
|
+
from pymem import Pymem
|
37
|
+
from argparse import ArgumentParser
|
38
|
+
from time import sleep as time_sleep
|
39
|
+
from datetime import datetime
|
40
|
+
from varname import VarnameRetrievingError, argname
|
41
|
+
from webbrowser import open as webbrowser_open
|
42
|
+
|
43
|
+
from .rexception import throw
|
44
|
+
from .rtype import RConfigMeta
|
45
|
+
|
46
|
+
|
47
|
+
__all__ = (
|
48
|
+
'RConfigSystem',
|
49
|
+
'add_env_path',
|
50
|
+
'reset_env_path',
|
51
|
+
'del_modules',
|
52
|
+
'dos_command',
|
53
|
+
'dos_command_var',
|
54
|
+
'block',
|
55
|
+
'is_iterable',
|
56
|
+
'is_table',
|
57
|
+
'is_number_str',
|
58
|
+
'get_first_notnull',
|
59
|
+
'get_name',
|
60
|
+
'get_stack_text',
|
61
|
+
'get_stack_param',
|
62
|
+
'get_arg_info',
|
63
|
+
'get_computer_info',
|
64
|
+
'get_network_table',
|
65
|
+
'get_process_table',
|
66
|
+
'search_process',
|
67
|
+
'kill_process',
|
68
|
+
'stop_process',
|
69
|
+
'start_process',
|
70
|
+
'get_idle_port',
|
71
|
+
'open_browser'
|
72
|
+
)
|
73
|
+
|
74
|
+
|
75
|
+
LoginUsers = TypedDict('LoginUsers', {'time': datetime, 'name': str, 'host': str})
|
76
|
+
ComputerInfo = TypedDict(
|
77
|
+
'ComputerInfo',
|
78
|
+
{
|
79
|
+
'boot_time': float,
|
80
|
+
'cpu_count': int,
|
81
|
+
'cpu_frequency': int,
|
82
|
+
'cpu_percent': float,
|
83
|
+
'memory_total': float,
|
84
|
+
'memory_percent': float,
|
85
|
+
'disk_total': float,
|
86
|
+
'disk_percent': float,
|
87
|
+
'process_count': int,
|
88
|
+
'network_count': int,
|
89
|
+
'login_users':LoginUsers
|
90
|
+
}
|
91
|
+
)
|
92
|
+
NetWorkInfo = TypedDict(
|
93
|
+
'NetWorkTable',
|
94
|
+
{
|
95
|
+
'family': Optional[str],
|
96
|
+
'socket': Optional[str],
|
97
|
+
'local_ip': str,
|
98
|
+
'local_port': int,
|
99
|
+
'remote_ip': Optional[str],
|
100
|
+
'remote_port': Optional[int],
|
101
|
+
'status': Optional[str],
|
102
|
+
'pid': Optional[int]
|
103
|
+
}
|
104
|
+
)
|
105
|
+
ProcessInfo = TypedDict('ProcessInfo', {'create_time': datetime, 'id': int, 'name': str, 'ports': Optional[list[int]]})
|
106
|
+
|
107
|
+
|
108
|
+
class RConfigSystem(object, metaclass=RConfigMeta):
|
109
|
+
"""
|
110
|
+
Rey's `config system` type.
|
111
|
+
"""
|
112
|
+
|
113
|
+
# Added environment path.
|
114
|
+
_add_env_paths: list[str] = []
|
115
|
+
|
116
|
+
|
117
|
+
def add_env_path(path: str) -> list[str]:
|
118
|
+
"""
|
119
|
+
Add environment variable path.
|
120
|
+
|
121
|
+
Parameters
|
122
|
+
----------
|
123
|
+
path : Path, can be a relative path.
|
124
|
+
|
125
|
+
Returns
|
126
|
+
-------
|
127
|
+
Added environment variables list.
|
128
|
+
"""
|
129
|
+
|
130
|
+
# Absolute path.
|
131
|
+
abs_path = os_abspath(path)
|
132
|
+
|
133
|
+
# Add.
|
134
|
+
RConfigSystem._add_env_paths.append(abs_path)
|
135
|
+
sys_path.append(abs_path)
|
136
|
+
|
137
|
+
return sys_path
|
138
|
+
|
139
|
+
|
140
|
+
def reset_env_path() -> None:
|
141
|
+
"""
|
142
|
+
Reset environment variable path.
|
143
|
+
"""
|
144
|
+
|
145
|
+
# Delete.
|
146
|
+
for path in RConfigSystem._add_env_paths:
|
147
|
+
sys_path.remove(path)
|
148
|
+
RConfigSystem._add_env_paths = []
|
149
|
+
|
150
|
+
|
151
|
+
def del_modules(path: str) -> list[str]:
|
152
|
+
"""
|
153
|
+
Delete record of modules import dictionary.
|
154
|
+
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
path : Module path, use regular match.
|
158
|
+
|
159
|
+
Returns
|
160
|
+
-------
|
161
|
+
Deleted modules dictionary.
|
162
|
+
"""
|
163
|
+
|
164
|
+
# Import.
|
165
|
+
from .rregex import search
|
166
|
+
|
167
|
+
# Set parameter.
|
168
|
+
deleted_dict = {}
|
169
|
+
module_keys = tuple(sys_modules.keys())
|
170
|
+
|
171
|
+
# Delete.
|
172
|
+
for key in module_keys:
|
173
|
+
module = sys_modules.get(key)
|
174
|
+
|
175
|
+
## Filter non file module.
|
176
|
+
if (
|
177
|
+
not hasattr(module, '__file__')
|
178
|
+
or module.__file__ is None
|
179
|
+
):
|
180
|
+
continue
|
181
|
+
|
182
|
+
## Match.
|
183
|
+
result = search(path, module.__file__)
|
184
|
+
if result is None:
|
185
|
+
continue
|
186
|
+
|
187
|
+
## Take out.
|
188
|
+
deleted_dict[key] = sys_modules.pop(key)
|
189
|
+
|
190
|
+
return deleted_dict
|
191
|
+
|
192
|
+
|
193
|
+
def dos_command(command: Union[str, Iterable[str]]) -> str:
|
194
|
+
"""
|
195
|
+
Execute DOS command.
|
196
|
+
|
197
|
+
Parameters
|
198
|
+
----------
|
199
|
+
command : DOS command.
|
200
|
+
- `str`: Use this command.
|
201
|
+
- `Iterable[str]`: Join strings with space as command.
|
202
|
+
When space in the string, automatic add quotation mark (e.g., ['echo', 'a b'] -> 'echo 'a b'').
|
203
|
+
|
204
|
+
Returns
|
205
|
+
-------
|
206
|
+
Command standard output.
|
207
|
+
"""
|
208
|
+
|
209
|
+
# Execute.
|
210
|
+
popen = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
|
211
|
+
|
212
|
+
# Check.
|
213
|
+
error_bytes: bytes = popen.stderr.read()
|
214
|
+
if error_bytes != b'':
|
215
|
+
error = error_bytes.decode('GBK')
|
216
|
+
throw(value=error)
|
217
|
+
|
218
|
+
# Standard output.
|
219
|
+
output_bytes: bytes = popen.stdout.read()
|
220
|
+
output = output_bytes.decode('GBK')
|
221
|
+
|
222
|
+
return output
|
223
|
+
|
224
|
+
|
225
|
+
def dos_command_var(*vars: Any) -> list[Any]:
|
226
|
+
"""
|
227
|
+
Use DOS command to input arguments to variables.
|
228
|
+
Use DOS command `python file --help` to view help information.
|
229
|
+
|
230
|
+
Parameters
|
231
|
+
----------
|
232
|
+
vars : Variables.
|
233
|
+
|
234
|
+
Returns
|
235
|
+
-------
|
236
|
+
Value of variables.
|
237
|
+
|
238
|
+
Examples
|
239
|
+
--------
|
240
|
+
>>> var1 = 1
|
241
|
+
>>> var2 = 2
|
242
|
+
>>> var3 = 3
|
243
|
+
>>> var1, var2, var3 = dos_command(var1, var2, var3)
|
244
|
+
>>> print(var1, var2, var3)
|
245
|
+
>>> # Use DOS command 'python file.py 10 --var2 20 21'
|
246
|
+
10 [20, 21] 3
|
247
|
+
"""
|
248
|
+
|
249
|
+
# Get parameter.
|
250
|
+
vars_name = get_name(vars)
|
251
|
+
vars_info = tuple(zip(vars_name, vars))
|
252
|
+
|
253
|
+
# Set DOS command.
|
254
|
+
usage = 'input arguments to variables'
|
255
|
+
parser = ArgumentParser(usage=usage)
|
256
|
+
for name, value in vars_info:
|
257
|
+
if value is None:
|
258
|
+
var_type = str
|
259
|
+
var_help = None
|
260
|
+
else:
|
261
|
+
var_type = value.__class__
|
262
|
+
var_help = str(value.__class__)
|
263
|
+
|
264
|
+
## Position argument.
|
265
|
+
parser.add_argument(
|
266
|
+
name,
|
267
|
+
nargs='?',
|
268
|
+
type=var_type,
|
269
|
+
help=var_help
|
270
|
+
)
|
271
|
+
|
272
|
+
## Keyword argument.
|
273
|
+
kw_name = '--' + name
|
274
|
+
parser.add_argument(
|
275
|
+
kw_name,
|
276
|
+
nargs='*',
|
277
|
+
type=var_type,
|
278
|
+
help=var_help,
|
279
|
+
metavar='value',
|
280
|
+
dest=kw_name
|
281
|
+
)
|
282
|
+
|
283
|
+
# Get argument.
|
284
|
+
namespace = parser.parse_args()
|
285
|
+
values = []
|
286
|
+
for name, value in vars_info:
|
287
|
+
kw_name = '--' + name
|
288
|
+
|
289
|
+
## Position argument.
|
290
|
+
dos_value = getattr(namespace, name)
|
291
|
+
if dos_value is not None:
|
292
|
+
values.append(dos_value)
|
293
|
+
continue
|
294
|
+
|
295
|
+
## Keyword argument.
|
296
|
+
dos_value = getattr(namespace, kw_name)
|
297
|
+
if dos_value.__class__ == list:
|
298
|
+
value_len = len(dos_value)
|
299
|
+
match value_len:
|
300
|
+
case 0:
|
301
|
+
dos_value = None
|
302
|
+
case 1:
|
303
|
+
dos_value = dos_value[0]
|
304
|
+
values.append(dos_value)
|
305
|
+
continue
|
306
|
+
|
307
|
+
values.append(value)
|
308
|
+
|
309
|
+
return values
|
310
|
+
|
311
|
+
|
312
|
+
def block() -> None:
|
313
|
+
"""
|
314
|
+
Blocking program, can be double press interrupt to end blocking.
|
315
|
+
"""
|
316
|
+
|
317
|
+
# Start.
|
318
|
+
print('Start blocking.')
|
319
|
+
while True:
|
320
|
+
try:
|
321
|
+
time_sleep(1)
|
322
|
+
except KeyboardInterrupt:
|
323
|
+
|
324
|
+
# Confirm.
|
325
|
+
try:
|
326
|
+
print('Double press interrupt to end blocking.')
|
327
|
+
time_sleep(1)
|
328
|
+
|
329
|
+
# End.
|
330
|
+
except KeyboardInterrupt:
|
331
|
+
print('End blocking.')
|
332
|
+
break
|
333
|
+
|
334
|
+
except:
|
335
|
+
continue
|
336
|
+
|
337
|
+
|
338
|
+
def is_iterable(
|
339
|
+
obj: Any,
|
340
|
+
exclude_types: Iterable[type] = [str, bytes]
|
341
|
+
) -> bool:
|
342
|
+
"""
|
343
|
+
Judge whether it is iterable.
|
344
|
+
|
345
|
+
Parameters
|
346
|
+
----------
|
347
|
+
obj : Judge object.
|
348
|
+
exclude_types : Non iterative types.
|
349
|
+
|
350
|
+
Returns
|
351
|
+
-------
|
352
|
+
Judgment result.
|
353
|
+
"""
|
354
|
+
|
355
|
+
# Exclude types.
|
356
|
+
if obj.__class__ in exclude_types:
|
357
|
+
return False
|
358
|
+
|
359
|
+
# Judge.
|
360
|
+
if hasattr(obj, '__iter__'):
|
361
|
+
return True
|
362
|
+
else:
|
363
|
+
return False
|
364
|
+
|
365
|
+
|
366
|
+
def is_table(
|
367
|
+
obj: Any,
|
368
|
+
check_fields: bool = True
|
369
|
+
) -> bool:
|
370
|
+
"""
|
371
|
+
Judge whether it is `list[dict]` table format and keys and keys sort of the dict are the same.
|
372
|
+
|
373
|
+
Parameters
|
374
|
+
----------
|
375
|
+
obj : Judge object.
|
376
|
+
check_fields : Do you want to check the keys and keys sort of the dict are the same.
|
377
|
+
|
378
|
+
Returns
|
379
|
+
-------
|
380
|
+
Judgment result.
|
381
|
+
"""
|
382
|
+
|
383
|
+
# Judge.
|
384
|
+
if obj.__class__ != list:
|
385
|
+
return False
|
386
|
+
for element in obj:
|
387
|
+
if element.__class__ != dict:
|
388
|
+
return False
|
389
|
+
|
390
|
+
## Check fields of table.
|
391
|
+
if check_fields:
|
392
|
+
keys_strs = [
|
393
|
+
':'.join([str(key) for key in element.keys()])
|
394
|
+
for element in obj
|
395
|
+
]
|
396
|
+
keys_strs_only = set(keys_strs)
|
397
|
+
if len(keys_strs_only) != 1:
|
398
|
+
return False
|
399
|
+
|
400
|
+
return True
|
401
|
+
|
402
|
+
|
403
|
+
def is_number_str(
|
404
|
+
string: str
|
405
|
+
) -> bool:
|
406
|
+
"""
|
407
|
+
Judge whether it is number string.
|
408
|
+
|
409
|
+
Parameters
|
410
|
+
----------
|
411
|
+
string : String.
|
412
|
+
|
413
|
+
Returns
|
414
|
+
-------
|
415
|
+
Judgment result.
|
416
|
+
"""
|
417
|
+
|
418
|
+
# Judge.
|
419
|
+
try:
|
420
|
+
float(string)
|
421
|
+
except (ValueError, TypeError):
|
422
|
+
return False
|
423
|
+
|
424
|
+
return True
|
425
|
+
|
426
|
+
|
427
|
+
def get_first_notnull(
|
428
|
+
*values: Any,
|
429
|
+
default: Union[None, Any, Literal['exception']] = None,
|
430
|
+
nulls: tuple = (None,)) -> Any:
|
431
|
+
"""
|
432
|
+
Get the first value that is not null.
|
433
|
+
|
434
|
+
Parameters
|
435
|
+
----------
|
436
|
+
values : Check values.
|
437
|
+
default : When all are null, then return this is value, or throw exception.
|
438
|
+
- `Any`: Return this is value.
|
439
|
+
- `Literal['exception']`: Throw `exception`.
|
440
|
+
nulls : Range of null values.
|
441
|
+
|
442
|
+
Returns
|
443
|
+
-------
|
444
|
+
Return first not null value, when all are `None`, then return default value.
|
445
|
+
"""
|
446
|
+
|
447
|
+
# Get value.
|
448
|
+
for value in values:
|
449
|
+
if value not in nulls:
|
450
|
+
return value
|
451
|
+
|
452
|
+
# Throw exception.
|
453
|
+
if default == 'exception':
|
454
|
+
vars_name = get_name(values)
|
455
|
+
if vars_name is not None:
|
456
|
+
vars_name_de_dup = list(set(vars_name))
|
457
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
458
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
459
|
+
else:
|
460
|
+
vars_name_str = ''
|
461
|
+
raise ValueError(f'at least one of parameters{vars_name_str} is not None')
|
462
|
+
|
463
|
+
return default
|
464
|
+
|
465
|
+
|
466
|
+
@overload
|
467
|
+
def get_name(obj: tuple, frame: int = 2) -> Optional[tuple[str, ...]]: ...
|
468
|
+
|
469
|
+
@overload
|
470
|
+
def get_name(obj: Any, frame: int = 2) -> Optional[str]: ...
|
471
|
+
|
472
|
+
def get_name(obj: Any, frame: int = 2) -> Optional[Union[str, tuple[str, ...]]]:
|
473
|
+
"""
|
474
|
+
Get name of object or variable.
|
475
|
+
|
476
|
+
Parameters
|
477
|
+
----------
|
478
|
+
obj : Object.
|
479
|
+
- `tuple`: Variable length position parameter of previous layer.
|
480
|
+
- `Any`: Parameter of any layer.
|
481
|
+
frame : Number of code to upper level.
|
482
|
+
|
483
|
+
Returns
|
484
|
+
-------
|
485
|
+
Name or None.
|
486
|
+
"""
|
487
|
+
|
488
|
+
# Get name using built in method.
|
489
|
+
if hasattr(obj, '__name__'):
|
490
|
+
name = obj.__name__
|
491
|
+
return name
|
492
|
+
|
493
|
+
# Get name using module method.
|
494
|
+
name = 'obj'
|
495
|
+
for frame_ in range(1, frame + 1):
|
496
|
+
if name.__class__ != str:
|
497
|
+
return
|
498
|
+
try:
|
499
|
+
name = argname(name, frame=frame_)
|
500
|
+
except VarnameRetrievingError:
|
501
|
+
return
|
502
|
+
if name.__class__ == tuple:
|
503
|
+
for element in name:
|
504
|
+
if element.__class__ != str:
|
505
|
+
return
|
506
|
+
|
507
|
+
return name
|
508
|
+
|
509
|
+
|
510
|
+
def get_stack_text(format_: Literal['plain', 'full'] = 'plain', limit: int = 2) -> str:
|
511
|
+
"""
|
512
|
+
Get code stack text.
|
513
|
+
|
514
|
+
Parameters
|
515
|
+
----------
|
516
|
+
format_ : Stack text format.
|
517
|
+
- `Literal['plain']`: Floor stack position.
|
518
|
+
- `Literal['full']`: Full stack information.
|
519
|
+
limit : Stack limit level.
|
520
|
+
|
521
|
+
Returns
|
522
|
+
-------
|
523
|
+
Code stack text.
|
524
|
+
"""
|
525
|
+
|
526
|
+
match format_:
|
527
|
+
|
528
|
+
# Plain.
|
529
|
+
case 'plain':
|
530
|
+
limit += 1
|
531
|
+
stacks = format_stack(limit=limit)
|
532
|
+
|
533
|
+
## Check.
|
534
|
+
if len(stacks) != limit:
|
535
|
+
throw(value=limit)
|
536
|
+
|
537
|
+
## Convert.
|
538
|
+
text = stacks[0]
|
539
|
+
index_end = text.find(', in ')
|
540
|
+
text = text[2:index_end]
|
541
|
+
|
542
|
+
# Full.
|
543
|
+
case 'full':
|
544
|
+
stacks = format_stack()
|
545
|
+
index_limit = len(stacks) - limit
|
546
|
+
stacks = stacks[:index_limit]
|
547
|
+
|
548
|
+
## Check.
|
549
|
+
if len(stacks) == 0:
|
550
|
+
throw(value=limit)
|
551
|
+
|
552
|
+
## Convert.
|
553
|
+
stacks = [
|
554
|
+
stack[2:].replace('\n ', '\n', 1)
|
555
|
+
for stack in stacks
|
556
|
+
]
|
557
|
+
text = ''.join(stacks)
|
558
|
+
text = text[:-1]
|
559
|
+
|
560
|
+
# Throw exception.
|
561
|
+
case _:
|
562
|
+
throw(ValueError, format_)
|
563
|
+
|
564
|
+
return text
|
565
|
+
|
566
|
+
|
567
|
+
@overload
|
568
|
+
def get_stack_param(format_: Literal['floor'] = 'floor', limit: int = 2) -> dict: ...
|
569
|
+
|
570
|
+
@overload
|
571
|
+
def get_stack_param(format_: Literal['full'] = 'floor', limit: int = 2) -> list[dict]: ...
|
572
|
+
|
573
|
+
def get_stack_param(format_: Literal['floor', 'full'] = 'floor', limit: int = 2) -> Union[dict, list[dict]]:
|
574
|
+
"""
|
575
|
+
Get code stack parameters.
|
576
|
+
|
577
|
+
Parameters
|
578
|
+
----------
|
579
|
+
format_ : Stack parameters format.
|
580
|
+
- `Literal['floor']`: Floor stack parameters.
|
581
|
+
- `Literal['full']`: Full stack parameters.
|
582
|
+
limit : Stack limit level.
|
583
|
+
|
584
|
+
Returns
|
585
|
+
-------
|
586
|
+
Code stack parameters.
|
587
|
+
"""
|
588
|
+
|
589
|
+
# Get.
|
590
|
+
stacks = extract_stack()
|
591
|
+
index_limit = len(stacks) - limit
|
592
|
+
stacks = stacks[:index_limit]
|
593
|
+
|
594
|
+
# Check.
|
595
|
+
if len(stacks) == 0:
|
596
|
+
throw(value=limit)
|
597
|
+
|
598
|
+
# Convert.
|
599
|
+
match format_:
|
600
|
+
|
601
|
+
## Floor.
|
602
|
+
case 'floor':
|
603
|
+
stack = stacks[-1]
|
604
|
+
params = {
|
605
|
+
'filename': stack.filename,
|
606
|
+
'lineno': stack.lineno,
|
607
|
+
'name': stack.name,
|
608
|
+
'line': stack.line
|
609
|
+
}
|
610
|
+
|
611
|
+
## Full.
|
612
|
+
case 'full':
|
613
|
+
params = [
|
614
|
+
{
|
615
|
+
'filename': stack.filename,
|
616
|
+
'lineno': stack.lineno,
|
617
|
+
'name': stack.name,
|
618
|
+
'line': stack.line
|
619
|
+
}
|
620
|
+
for stack in stacks
|
621
|
+
]
|
622
|
+
|
623
|
+
return params
|
624
|
+
|
625
|
+
|
626
|
+
def get_arg_info(func: Callable) -> list[
|
627
|
+
dict[
|
628
|
+
Literal['name', 'type', 'annotation', 'default'],
|
629
|
+
Optional[str]
|
630
|
+
]
|
631
|
+
]:
|
632
|
+
"""
|
633
|
+
Get function arguments information.
|
634
|
+
|
635
|
+
Parameters
|
636
|
+
----------
|
637
|
+
func : Function.
|
638
|
+
|
639
|
+
Returns
|
640
|
+
-------
|
641
|
+
Arguments information.
|
642
|
+
- `Value of key 'name'`: Argument name.
|
643
|
+
- `Value of key 'type'`: Argument bind type.
|
644
|
+
`Literal['position_or_keyword']`: Is positional argument or keyword argument.
|
645
|
+
`Literal['var_position']`: Is variable length positional argument.
|
646
|
+
`Literal['var_keyword']`: Is variable length keyword argument.
|
647
|
+
`Literal['only_position']`: Is positional only argument.
|
648
|
+
`Literal['only_keyword']`: Is keyword only argument.
|
649
|
+
- `Value of key 'annotation'`: Argument annotation.
|
650
|
+
- `Value of key 'default'`: Argument default value.
|
651
|
+
"""
|
652
|
+
|
653
|
+
# Get signature.
|
654
|
+
signature = inspect_signature(func)
|
655
|
+
|
656
|
+
# Get information.
|
657
|
+
info = [
|
658
|
+
{
|
659
|
+
'name': name,
|
660
|
+
'type': (
|
661
|
+
'position_or_keyword'
|
662
|
+
if parameter.kind == _ParameterKind.POSITIONAL_OR_KEYWORD
|
663
|
+
else 'var_position'
|
664
|
+
if parameter.kind == _ParameterKind.VAR_POSITIONAL
|
665
|
+
else 'var_keyword'
|
666
|
+
if parameter.kind == _ParameterKind.VAR_KEYWORD
|
667
|
+
else 'only_position'
|
668
|
+
if parameter.kind == _ParameterKind.POSITIONAL_ONLY
|
669
|
+
else 'only_keyword'
|
670
|
+
if parameter.kind == _ParameterKind.KEYWORD_ONLY
|
671
|
+
else None
|
672
|
+
),
|
673
|
+
'annotation': parameter.annotation,
|
674
|
+
'default': parameter.default
|
675
|
+
}
|
676
|
+
for name, parameter in signature.parameters.items()
|
677
|
+
]
|
678
|
+
|
679
|
+
# Replace empty.
|
680
|
+
for row in info:
|
681
|
+
for key, value in row.items():
|
682
|
+
if value == _empty:
|
683
|
+
row[key] = None
|
684
|
+
|
685
|
+
return info
|
686
|
+
|
687
|
+
|
688
|
+
def get_computer_info() -> ComputerInfo:
|
689
|
+
"""
|
690
|
+
Get computer information.
|
691
|
+
|
692
|
+
Returns
|
693
|
+
-------
|
694
|
+
Computer information dictionary.
|
695
|
+
- `Key 'boot_time'`: Computer boot time.
|
696
|
+
- `Key 'cpu_count'`: Computer logical CPU count.
|
697
|
+
- `Key 'cpu_frequency'`: Computer current CPU frequency.
|
698
|
+
- `Key 'cpu_percent'`: Computer CPU usage percent.
|
699
|
+
- `Key 'memory_total'`: Computer memory total gigabyte.
|
700
|
+
- `Key 'memory_percent'`: Computer memory usage percent.
|
701
|
+
- `Key 'disk_total'`: Computer disk total gigabyte.
|
702
|
+
- `Key 'disk_percent'`: Computer disk usage percent.
|
703
|
+
- `Key 'process_count'`: Computer process count.
|
704
|
+
- `Key 'network_count'`: Computer network count.
|
705
|
+
- `Key 'login_users'`: Computer login users information.
|
706
|
+
"""
|
707
|
+
|
708
|
+
# Set parameter.
|
709
|
+
info = {}
|
710
|
+
|
711
|
+
# Get.
|
712
|
+
|
713
|
+
## Boot time.
|
714
|
+
boot_time = psutil_boot_time()
|
715
|
+
info['boot_time'] = datetime.fromtimestamp(
|
716
|
+
boot_time
|
717
|
+
).strftime(
|
718
|
+
'%Y-%m-%d %H:%M:%S'
|
719
|
+
)
|
720
|
+
|
721
|
+
## CPU.
|
722
|
+
info['cpu_count'] = psutil_cpu_count()
|
723
|
+
info['cpu_frequency'] = int(psutil_cpu_freq().current)
|
724
|
+
info['cpu_percent'] = round(psutil_cpu_percent(), 1)
|
725
|
+
|
726
|
+
## Memory.
|
727
|
+
memory_info = psutil_virtual_memory()
|
728
|
+
info['memory_total'] = round(memory_info.total / 1024 / 1024 / 1024, 1)
|
729
|
+
info['memory_percent'] = round(memory_info.percent, 1)
|
730
|
+
|
731
|
+
## Disk.
|
732
|
+
disk_total = []
|
733
|
+
disk_used = []
|
734
|
+
partitions_info = psutil_disk_partitions()
|
735
|
+
for partition_info in partitions_info:
|
736
|
+
try:
|
737
|
+
partition_usage_info = psutil_disk_usage(partition_info.device)
|
738
|
+
except PermissionError:
|
739
|
+
continue
|
740
|
+
disk_total.append(partition_usage_info.total)
|
741
|
+
disk_used.append(partition_usage_info.used)
|
742
|
+
disk_total = sum(disk_total)
|
743
|
+
disk_used = sum(disk_used)
|
744
|
+
info['disk_total'] = round(disk_total / 1024 / 1024 / 1024, 1)
|
745
|
+
info['disk_percent'] = round(disk_used / disk_total * 100, 1)
|
746
|
+
|
747
|
+
## Process.
|
748
|
+
pids = psutil_pids()
|
749
|
+
info['process_count'] = len(pids)
|
750
|
+
|
751
|
+
## Network.
|
752
|
+
net_info = psutil_net_connections()
|
753
|
+
info['network_count'] = len(net_info)
|
754
|
+
|
755
|
+
## User.
|
756
|
+
users_info = psutil_users()
|
757
|
+
info['login_users'] = [
|
758
|
+
{
|
759
|
+
'time': datetime.fromtimestamp(
|
760
|
+
user_info.started
|
761
|
+
).strftime(
|
762
|
+
'%Y-%m-%d %H:%M:%S'
|
763
|
+
),
|
764
|
+
'name': user_info.name,
|
765
|
+
'host': user_info.host
|
766
|
+
}
|
767
|
+
for user_info in users_info
|
768
|
+
]
|
769
|
+
sort_func = lambda row: row['time']
|
770
|
+
info['login_users'].sort(key=sort_func, reverse=True)
|
771
|
+
|
772
|
+
return info
|
773
|
+
|
774
|
+
|
775
|
+
def get_network_table() -> list[NetWorkInfo]:
|
776
|
+
"""
|
777
|
+
Get network information table.
|
778
|
+
|
779
|
+
Returns
|
780
|
+
-------
|
781
|
+
Network information table.
|
782
|
+
"""
|
783
|
+
|
784
|
+
# Get.
|
785
|
+
connections = psutil_net_connections('all')
|
786
|
+
table = [
|
787
|
+
{
|
788
|
+
'family': (
|
789
|
+
'IPv4'
|
790
|
+
if connection.family.name == 'AF_INET'
|
791
|
+
else 'IPv6'
|
792
|
+
if connection.family.name == 'AF_INET6'
|
793
|
+
else None
|
794
|
+
),
|
795
|
+
'socket': (
|
796
|
+
'TCP'
|
797
|
+
if connection.type.name == 'SOCK_STREAM'
|
798
|
+
else 'UDP'
|
799
|
+
if connection.type.name == 'SOCK_DGRAM'
|
800
|
+
else None
|
801
|
+
),
|
802
|
+
'local_ip': connection.laddr.ip,
|
803
|
+
'local_port': connection.laddr.port,
|
804
|
+
'remote_ip': (
|
805
|
+
None
|
806
|
+
if connection.raddr == ()
|
807
|
+
else connection.raddr.ip
|
808
|
+
),
|
809
|
+
'remote_port': (
|
810
|
+
None
|
811
|
+
if connection.raddr == ()
|
812
|
+
else connection.raddr.port
|
813
|
+
),
|
814
|
+
'status': (
|
815
|
+
None
|
816
|
+
if connection.status == 'NONE'
|
817
|
+
else connection.status.lower()
|
818
|
+
),
|
819
|
+
'pid': connection.pid
|
820
|
+
}
|
821
|
+
for connection in connections
|
822
|
+
]
|
823
|
+
|
824
|
+
# Sort.
|
825
|
+
sort_func = lambda row: row['local_port']
|
826
|
+
table.sort(key=sort_func)
|
827
|
+
sort_func = lambda row: row['local_ip']
|
828
|
+
table.sort(key=sort_func)
|
829
|
+
|
830
|
+
return table
|
831
|
+
|
832
|
+
|
833
|
+
def get_process_table() -> list[ProcessInfo]:
|
834
|
+
"""
|
835
|
+
Get process information table.
|
836
|
+
|
837
|
+
Returns
|
838
|
+
-------
|
839
|
+
Process information table.
|
840
|
+
"""
|
841
|
+
|
842
|
+
# Get.
|
843
|
+
process_iter = psutil_process_iter()
|
844
|
+
table = []
|
845
|
+
for process in process_iter:
|
846
|
+
info = {}
|
847
|
+
with process.oneshot():
|
848
|
+
info['create_time'] = datetime.fromtimestamp(
|
849
|
+
process.create_time()
|
850
|
+
).strftime(
|
851
|
+
'%Y-%m-%d %H:%M:%S'
|
852
|
+
)
|
853
|
+
info['id'] = process.pid
|
854
|
+
info['name'] = process.name()
|
855
|
+
connections = process.connections()
|
856
|
+
if connections == []:
|
857
|
+
info['ports'] = None
|
858
|
+
else:
|
859
|
+
info['ports'] = [
|
860
|
+
connection.laddr.port
|
861
|
+
for connection in connections
|
862
|
+
]
|
863
|
+
table.append(info)
|
864
|
+
|
865
|
+
# Sort.
|
866
|
+
sort_func = lambda row: row['id']
|
867
|
+
table.sort(key=sort_func)
|
868
|
+
sort_func = lambda row: row['create_time']
|
869
|
+
table.sort(key=sort_func)
|
870
|
+
|
871
|
+
return table
|
872
|
+
|
873
|
+
|
874
|
+
def search_process(
|
875
|
+
id_: Optional[Union[int, Sequence[int]]] = None,
|
876
|
+
name: Optional[Union[str, Sequence[str]]] = None,
|
877
|
+
port: Optional[Union[str, int, Sequence[Union[str, int]]]] = None,
|
878
|
+
) -> list[Process]:
|
879
|
+
"""
|
880
|
+
Search process by ID or name or port.
|
881
|
+
|
882
|
+
Parameters
|
883
|
+
----------
|
884
|
+
id_ : Search condition, a value or sequence of process ID.
|
885
|
+
name : Search condition, a value or sequence of process name.
|
886
|
+
port : Search condition, a value or sequence of process port.
|
887
|
+
|
888
|
+
Returns
|
889
|
+
-------
|
890
|
+
List of process instances that match any condition.
|
891
|
+
"""
|
892
|
+
|
893
|
+
# Handle parameter.
|
894
|
+
match id_:
|
895
|
+
case None:
|
896
|
+
ids = []
|
897
|
+
case int():
|
898
|
+
ids = [id_]
|
899
|
+
case _:
|
900
|
+
ids = id_
|
901
|
+
match name:
|
902
|
+
case None:
|
903
|
+
names = []
|
904
|
+
case str():
|
905
|
+
names = [name]
|
906
|
+
case _:
|
907
|
+
names = name
|
908
|
+
match port:
|
909
|
+
case None:
|
910
|
+
ports = []
|
911
|
+
case str() | int():
|
912
|
+
ports = [port]
|
913
|
+
case _:
|
914
|
+
ports = port
|
915
|
+
ports = [
|
916
|
+
int(port)
|
917
|
+
for port in ports
|
918
|
+
]
|
919
|
+
|
920
|
+
# Search.
|
921
|
+
processes = []
|
922
|
+
if (
|
923
|
+
names != []
|
924
|
+
or ports != []
|
925
|
+
):
|
926
|
+
table = get_process_table()
|
927
|
+
else:
|
928
|
+
table = []
|
929
|
+
|
930
|
+
## ID.
|
931
|
+
for id__ in ids:
|
932
|
+
if psutil_pid_exists(id__):
|
933
|
+
process = Process(id__)
|
934
|
+
processes.append(process)
|
935
|
+
|
936
|
+
## Name.
|
937
|
+
for info in table:
|
938
|
+
if (
|
939
|
+
info['name'] in names
|
940
|
+
and psutil_pid_exists(info['id'])
|
941
|
+
):
|
942
|
+
process = Process(info['id'])
|
943
|
+
processes.append(process)
|
944
|
+
|
945
|
+
## Port.
|
946
|
+
for info in table:
|
947
|
+
for port in ports:
|
948
|
+
if (
|
949
|
+
info['ports'] is not None
|
950
|
+
and port in info['ports']
|
951
|
+
and psutil_pid_exists(info['id'])
|
952
|
+
):
|
953
|
+
process = Process(info['id'])
|
954
|
+
processes.append(process)
|
955
|
+
break
|
956
|
+
|
957
|
+
return processes
|
958
|
+
|
959
|
+
|
960
|
+
def kill_process(
|
961
|
+
id_: Optional[Union[int, Sequence[int]]] = None,
|
962
|
+
name: Optional[Union[str, Sequence[str]]] = None,
|
963
|
+
port: Optional[Union[str, int, Sequence[Union[str, int]]]] = None,
|
964
|
+
) -> list[Process]:
|
965
|
+
"""
|
966
|
+
Search and kill process by ID or name or port.
|
967
|
+
|
968
|
+
Parameters
|
969
|
+
----------
|
970
|
+
id_ : Search condition, a value or sequence of process ID.
|
971
|
+
name : Search condition, a value or sequence of process name.
|
972
|
+
port : Search condition, a value or sequence of process port.
|
973
|
+
|
974
|
+
Returns
|
975
|
+
-------
|
976
|
+
List of process instances that match any condition.
|
977
|
+
"""
|
978
|
+
|
979
|
+
# Get parameter.
|
980
|
+
self_pid = os_getpid()
|
981
|
+
|
982
|
+
# Search.
|
983
|
+
processes = search_process(id_, name, port)
|
984
|
+
|
985
|
+
# Start.
|
986
|
+
for process in processes:
|
987
|
+
with process.oneshot():
|
988
|
+
|
989
|
+
## Filter self process.
|
990
|
+
if process.pid == self_pid:
|
991
|
+
continue
|
992
|
+
|
993
|
+
process.kill()
|
994
|
+
|
995
|
+
return processes
|
996
|
+
|
997
|
+
|
998
|
+
def stop_process(
|
999
|
+
id_: Optional[Union[int, Sequence[int]]] = None,
|
1000
|
+
name: Optional[Union[str, Sequence[str]]] = None,
|
1001
|
+
port: Optional[Union[str, int, Sequence[Union[str, int]]]] = None,
|
1002
|
+
) -> list[Process]:
|
1003
|
+
"""
|
1004
|
+
Search and stop process by ID or name or port.
|
1005
|
+
|
1006
|
+
Parameters
|
1007
|
+
----------
|
1008
|
+
id_ : Search condition, a value or sequence of process ID.
|
1009
|
+
name : Search condition, a value or sequence of process name.
|
1010
|
+
port : Search condition, a value or sequence of process port.
|
1011
|
+
|
1012
|
+
Returns
|
1013
|
+
-------
|
1014
|
+
List of process instances that match any condition.
|
1015
|
+
"""
|
1016
|
+
|
1017
|
+
# Get parameter.
|
1018
|
+
self_pid = os_getpid()
|
1019
|
+
|
1020
|
+
# Search.
|
1021
|
+
processes = search_process(id_, name, port)
|
1022
|
+
|
1023
|
+
# Start.
|
1024
|
+
for process in processes:
|
1025
|
+
with process.oneshot():
|
1026
|
+
|
1027
|
+
## Filter self process.
|
1028
|
+
if process.pid == self_pid:
|
1029
|
+
continue
|
1030
|
+
|
1031
|
+
process.suspend()
|
1032
|
+
|
1033
|
+
return processes
|
1034
|
+
|
1035
|
+
|
1036
|
+
def start_process(
|
1037
|
+
id_: Optional[Union[int, Sequence[int]]] = None,
|
1038
|
+
name: Optional[Union[str, Sequence[str]]] = None,
|
1039
|
+
port: Optional[Union[str, int, Sequence[Union[str, int]]]] = None,
|
1040
|
+
) -> list[Process]:
|
1041
|
+
"""
|
1042
|
+
Search and start process by ID or name or port.
|
1043
|
+
|
1044
|
+
Parameters
|
1045
|
+
----------
|
1046
|
+
id_ : Search condition, a value or sequence of process ID.
|
1047
|
+
name : Search condition, a value or sequence of process name.
|
1048
|
+
port : Search condition, a value or sequence of process port.
|
1049
|
+
|
1050
|
+
Returns
|
1051
|
+
-------
|
1052
|
+
List of process instances that match any condition.
|
1053
|
+
"""
|
1054
|
+
|
1055
|
+
# Search.
|
1056
|
+
processes = search_process(id_, name, port)
|
1057
|
+
|
1058
|
+
# Start.
|
1059
|
+
for process in processes:
|
1060
|
+
with process.oneshot():
|
1061
|
+
process.resume()
|
1062
|
+
|
1063
|
+
return processes
|
1064
|
+
|
1065
|
+
|
1066
|
+
def get_idle_port(min: int = 49152) -> int:
|
1067
|
+
"""
|
1068
|
+
Judge and get an idle port number.
|
1069
|
+
|
1070
|
+
Parameters
|
1071
|
+
----------
|
1072
|
+
min : Minimum port number.
|
1073
|
+
|
1074
|
+
Returns
|
1075
|
+
-------
|
1076
|
+
Idle port number.
|
1077
|
+
"""
|
1078
|
+
|
1079
|
+
# Get parameter.
|
1080
|
+
network_table = get_network_table()
|
1081
|
+
ports = [
|
1082
|
+
info['local_port']
|
1083
|
+
for info in network_table
|
1084
|
+
]
|
1085
|
+
|
1086
|
+
# Judge.
|
1087
|
+
while True:
|
1088
|
+
if min in ports:
|
1089
|
+
min += 1
|
1090
|
+
else:
|
1091
|
+
return min
|
1092
|
+
|
1093
|
+
|
1094
|
+
def memory_read(
|
1095
|
+
process: Union[int, str],
|
1096
|
+
dll: str,
|
1097
|
+
offset: int
|
1098
|
+
) -> int:
|
1099
|
+
"""
|
1100
|
+
Read memory value.
|
1101
|
+
|
1102
|
+
Parameters
|
1103
|
+
----------
|
1104
|
+
process : Process ID or name.
|
1105
|
+
dll : DLL file name.
|
1106
|
+
offset : Memory address offset.
|
1107
|
+
|
1108
|
+
Returns
|
1109
|
+
-------
|
1110
|
+
Memory value.
|
1111
|
+
"""
|
1112
|
+
|
1113
|
+
# Get DLL address.
|
1114
|
+
pymem = Pymem(process)
|
1115
|
+
for module in pymem.list_modules():
|
1116
|
+
if module.name == dll:
|
1117
|
+
dll_address: int = module.lpBaseOfDll
|
1118
|
+
break
|
1119
|
+
|
1120
|
+
## Throw exception.
|
1121
|
+
else:
|
1122
|
+
throw(value=dll_address)
|
1123
|
+
|
1124
|
+
# Get memory address.
|
1125
|
+
memory_address = dll_address + offset
|
1126
|
+
|
1127
|
+
# Read.
|
1128
|
+
value = pymem.read_int(memory_address)
|
1129
|
+
|
1130
|
+
return value
|
1131
|
+
|
1132
|
+
|
1133
|
+
def memory_write(
|
1134
|
+
process: Union[int, str],
|
1135
|
+
dll: str,
|
1136
|
+
offset: int,
|
1137
|
+
value: int
|
1138
|
+
) -> None:
|
1139
|
+
"""
|
1140
|
+
Write memory value.
|
1141
|
+
|
1142
|
+
Parameters
|
1143
|
+
----------
|
1144
|
+
process : Process ID or name.
|
1145
|
+
dll : DLL file name.
|
1146
|
+
offset : Memory address offset.
|
1147
|
+
value : Memory value.
|
1148
|
+
"""
|
1149
|
+
|
1150
|
+
# Get DLL address.
|
1151
|
+
pymem = Pymem(process)
|
1152
|
+
for module in pymem.list_modules():
|
1153
|
+
if module.name == dll:
|
1154
|
+
dll_address: int = module.lpBaseOfDll
|
1155
|
+
break
|
1156
|
+
|
1157
|
+
# Get memory address.
|
1158
|
+
memory_address = dll_address + offset
|
1159
|
+
|
1160
|
+
# Read.
|
1161
|
+
pymem.write_int(memory_address, value)
|
1162
|
+
|
1163
|
+
|
1164
|
+
def open_browser(url: str) -> bool:
|
1165
|
+
"""
|
1166
|
+
Open browser and URL.
|
1167
|
+
|
1168
|
+
Parameters
|
1169
|
+
----------
|
1170
|
+
url : URL.
|
1171
|
+
|
1172
|
+
Returns
|
1173
|
+
-------
|
1174
|
+
Is it successful.
|
1175
|
+
"""
|
1176
|
+
|
1177
|
+
# Open.
|
1178
|
+
succeeded = webbrowser_open(url)
|
1179
|
+
|
1180
|
+
return succeeded
|