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/remail.py
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2022-12-05 14:10:19
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Email methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from typing import Optional, Union
|
13
|
+
from io import BufferedIOBase
|
14
|
+
from smtplib import SMTP
|
15
|
+
from email.mime.multipart import MIMEMultipart
|
16
|
+
from email.mime.text import MIMEText
|
17
|
+
from email.mime.application import MIMEApplication
|
18
|
+
|
19
|
+
from .rdata import unique
|
20
|
+
from .rexception import throw
|
21
|
+
from .ros import FileBytes, get_file_bytes
|
22
|
+
|
23
|
+
|
24
|
+
__all__ = (
|
25
|
+
'REmail',
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
class REmail(object):
|
30
|
+
"""
|
31
|
+
Rey's `email` type.
|
32
|
+
"""
|
33
|
+
|
34
|
+
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
username: str,
|
38
|
+
password: str
|
39
|
+
) -> None:
|
40
|
+
"""
|
41
|
+
Build `email` attributes.
|
42
|
+
|
43
|
+
Parameters
|
44
|
+
----------
|
45
|
+
username : Email username.
|
46
|
+
password : Email password.
|
47
|
+
"""
|
48
|
+
|
49
|
+
# Get parameter.
|
50
|
+
host, port = self.get_server_address(username)
|
51
|
+
|
52
|
+
# Set attribute.
|
53
|
+
self.username = username
|
54
|
+
self.password = password
|
55
|
+
self.host = host
|
56
|
+
self.port = port
|
57
|
+
self.smtp = SMTP(host, port)
|
58
|
+
|
59
|
+
|
60
|
+
def get_server_address(
|
61
|
+
self,
|
62
|
+
email: str
|
63
|
+
) -> tuple[str, str]:
|
64
|
+
"""
|
65
|
+
Get server address of email.
|
66
|
+
|
67
|
+
Parameters
|
68
|
+
----------
|
69
|
+
email : Email address.
|
70
|
+
|
71
|
+
Returns
|
72
|
+
-------
|
73
|
+
Server address.
|
74
|
+
"""
|
75
|
+
|
76
|
+
# Get.
|
77
|
+
domain_name = email.split('@')[-1]
|
78
|
+
host = 'smtp.' + domain_name
|
79
|
+
port = 25
|
80
|
+
|
81
|
+
return host, port
|
82
|
+
|
83
|
+
|
84
|
+
def get_smtp(self) -> SMTP:
|
85
|
+
"""
|
86
|
+
Get `SMTP` connection instance and login.
|
87
|
+
|
88
|
+
Returns
|
89
|
+
-------
|
90
|
+
Instance.
|
91
|
+
"""
|
92
|
+
|
93
|
+
# Login.
|
94
|
+
response = self.smtp.login(self.username, self.password)
|
95
|
+
code = response[0]
|
96
|
+
if code != 235:
|
97
|
+
throw(ConnectionError, response)
|
98
|
+
|
99
|
+
return self.smtp
|
100
|
+
|
101
|
+
|
102
|
+
def create_email(
|
103
|
+
self,
|
104
|
+
title: Optional[str],
|
105
|
+
text: Optional[str],
|
106
|
+
attachment: dict[str, bytes],
|
107
|
+
show_from: Optional[str],
|
108
|
+
show_to: Optional[list[str]],
|
109
|
+
show_cc: Optional[list[str]]
|
110
|
+
) -> str:
|
111
|
+
"""
|
112
|
+
create email content.
|
113
|
+
|
114
|
+
Parameters
|
115
|
+
----------
|
116
|
+
title : Email title.
|
117
|
+
text : Email text.
|
118
|
+
attachment : Email attachments dictionary.
|
119
|
+
- `Key`: File name.
|
120
|
+
- `Value`: File bytes data.
|
121
|
+
show_from : Show from email address.
|
122
|
+
show_to : Show to email addresses list.
|
123
|
+
show_cc : Show carbon copy email addresses list.
|
124
|
+
"""
|
125
|
+
|
126
|
+
# Handle parameter.
|
127
|
+
if show_to.__class__ == list:
|
128
|
+
show_to = ','.join(show_to)
|
129
|
+
if show_cc.__class__ == list:
|
130
|
+
show_cc = ','.join(show_cc)
|
131
|
+
|
132
|
+
# Instance.
|
133
|
+
mimes = MIMEMultipart()
|
134
|
+
|
135
|
+
# Add.
|
136
|
+
|
137
|
+
## Title.
|
138
|
+
if title is not None:
|
139
|
+
mimes['subject'] = title
|
140
|
+
|
141
|
+
## Text.
|
142
|
+
if text is not None:
|
143
|
+
mime_text = MIMEText(text)
|
144
|
+
mimes.attach(mime_text)
|
145
|
+
|
146
|
+
## Attachment.
|
147
|
+
for file_name, file_bytes in attachment.items():
|
148
|
+
mime_file = MIMEApplication(file_bytes)
|
149
|
+
mime_file.add_header('Content-Disposition', 'attachment', filename=file_name)
|
150
|
+
mimes.attach(mime_file)
|
151
|
+
|
152
|
+
## Show from.
|
153
|
+
if show_from is not None:
|
154
|
+
mimes['from'] = show_from
|
155
|
+
|
156
|
+
## Show to.
|
157
|
+
if show_to is not None:
|
158
|
+
mimes['to'] = show_to
|
159
|
+
|
160
|
+
## Show cc.
|
161
|
+
if show_cc is not None:
|
162
|
+
mimes['cc'] = show_cc
|
163
|
+
|
164
|
+
# Create.
|
165
|
+
email = mimes.as_string()
|
166
|
+
|
167
|
+
return email
|
168
|
+
|
169
|
+
|
170
|
+
def send_email(
|
171
|
+
self,
|
172
|
+
to: Union[str, list[str]],
|
173
|
+
title: Optional[str] = None,
|
174
|
+
text: Optional[str] = None,
|
175
|
+
attachment: dict[str, FileBytes] = {},
|
176
|
+
cc: Optional[Union[str, list[str]]] = None,
|
177
|
+
show_from: Optional[str] = None,
|
178
|
+
show_to: Optional[Union[str, list[str]]] = None,
|
179
|
+
show_cc: Optional[Union[str, list[str]]] = None
|
180
|
+
) -> None:
|
181
|
+
"""
|
182
|
+
Send email.
|
183
|
+
|
184
|
+
Parameters
|
185
|
+
----------
|
186
|
+
to : To email addresses.
|
187
|
+
- `str`: Email address, multiple comma interval.
|
188
|
+
- `list[str]`: Email addresses list.
|
189
|
+
title : Email title.
|
190
|
+
text : Email text.
|
191
|
+
attachment : Email attachments dictionary.
|
192
|
+
- `Key`: File name.
|
193
|
+
- `Value`: File bytes data source.
|
194
|
+
`bytes`: File bytes data.
|
195
|
+
`str`: File path.
|
196
|
+
`BufferedIOBase`: File bytes IO.
|
197
|
+
cc : Carbon copy email addresses.
|
198
|
+
- `str`: Email address, multiple comma interval.
|
199
|
+
- `list[str]`: Email addresses list.
|
200
|
+
show_from : Show from email address.
|
201
|
+
- `None`: Use attribute `self.username`.
|
202
|
+
- `str`: Email address.
|
203
|
+
show_to : Show to email addresses.
|
204
|
+
- `None`: Use parameter `to`.
|
205
|
+
- `str`: Email address, multiple comma interval.
|
206
|
+
- `list[str]`: Email addresses list.
|
207
|
+
show_cc : Show carbon copy email addresses.
|
208
|
+
- `None`: Use parameter `cc`.
|
209
|
+
- `str`: Email address, multiple comma interval.
|
210
|
+
- `list[str]`: Email addresses list.
|
211
|
+
"""
|
212
|
+
|
213
|
+
# Handle parameter.
|
214
|
+
|
215
|
+
## To.
|
216
|
+
if to.__class__ == str:
|
217
|
+
to = to.split(',')
|
218
|
+
|
219
|
+
## Cc.
|
220
|
+
match cc:
|
221
|
+
case None:
|
222
|
+
cc = []
|
223
|
+
case str():
|
224
|
+
cc = cc.split(',')
|
225
|
+
|
226
|
+
## Show from.
|
227
|
+
show_from = show_from or self.username
|
228
|
+
|
229
|
+
## Show to.
|
230
|
+
show_to = show_to or to
|
231
|
+
if show_to.__class__ == str:
|
232
|
+
show_to = show_to.split(',')
|
233
|
+
|
234
|
+
## Show cc.
|
235
|
+
show_cc = show_cc or cc
|
236
|
+
if show_cc.__class__ == str:
|
237
|
+
show_cc = show_cc.split(',')
|
238
|
+
|
239
|
+
## Attachment.
|
240
|
+
for file_name, file_source in attachment.items():
|
241
|
+
file_bytes = get_file_bytes(file_source)
|
242
|
+
attachment[file_name] = file_bytes
|
243
|
+
|
244
|
+
# Create email.
|
245
|
+
email = self.create_email(
|
246
|
+
title,
|
247
|
+
text,
|
248
|
+
attachment,
|
249
|
+
show_from,
|
250
|
+
show_to,
|
251
|
+
show_cc
|
252
|
+
)
|
253
|
+
|
254
|
+
# Get SMTP.
|
255
|
+
smtp = self.get_smtp()
|
256
|
+
|
257
|
+
# Send email.
|
258
|
+
to += cc
|
259
|
+
to = unique(to)
|
260
|
+
smtp.sendmail(
|
261
|
+
self.username,
|
262
|
+
to,
|
263
|
+
email
|
264
|
+
)
|
265
|
+
|
266
|
+
|
267
|
+
__call__ = send_email
|
268
|
+
|
269
|
+
|
270
|
+
def __del__(self) -> None:
|
271
|
+
"""
|
272
|
+
Delete instance.
|
273
|
+
"""
|
274
|
+
|
275
|
+
# Quit.
|
276
|
+
self.smtp.quit()
|
reykit/rexception.py
ADDED
@@ -0,0 +1,339 @@
|
|
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 : Exception methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
# !/usr/bin/env python
|
13
|
+
# -*- coding: utf-8 -*-
|
14
|
+
|
15
|
+
"""
|
16
|
+
@Time : 2022-12-05 14:09:42
|
17
|
+
@Author : Rey
|
18
|
+
@Contact : reyxbo@163.com
|
19
|
+
@Explain : Interpreter system methods.
|
20
|
+
"""
|
21
|
+
|
22
|
+
|
23
|
+
from typing import Any, Optional, Union, NoReturn
|
24
|
+
from types import TracebackType
|
25
|
+
from collections.abc import Iterable
|
26
|
+
from sys import exc_info as sys_exc_info
|
27
|
+
from os.path import exists as os_exists
|
28
|
+
from traceback import format_exc
|
29
|
+
from warnings import warn as warnings_warn
|
30
|
+
|
31
|
+
from .rtype import RNull
|
32
|
+
|
33
|
+
|
34
|
+
__all__ = (
|
35
|
+
'RError',
|
36
|
+
'RActiveError',
|
37
|
+
'throw',
|
38
|
+
'warn',
|
39
|
+
'catch_exc',
|
40
|
+
'check_least_one',
|
41
|
+
'check_most_one',
|
42
|
+
'check_file_found',
|
43
|
+
'check_file_exist',
|
44
|
+
'check_response_code'
|
45
|
+
)
|
46
|
+
|
47
|
+
|
48
|
+
class RError(Exception):
|
49
|
+
"""
|
50
|
+
Rey `error` type.
|
51
|
+
"""
|
52
|
+
|
53
|
+
|
54
|
+
class RActiveError(RError):
|
55
|
+
"""
|
56
|
+
Rey's `active error` type.
|
57
|
+
"""
|
58
|
+
|
59
|
+
|
60
|
+
def throw(
|
61
|
+
exception: type[BaseException] = AssertionError,
|
62
|
+
value: Any = RNull,
|
63
|
+
*values: Any,
|
64
|
+
text: Optional[str] = None,
|
65
|
+
frame: int = 2
|
66
|
+
) -> NoReturn:
|
67
|
+
"""
|
68
|
+
Throw exception.
|
69
|
+
|
70
|
+
Parameters
|
71
|
+
----------
|
72
|
+
exception : Exception Type.
|
73
|
+
value : Exception value.
|
74
|
+
values : Exception values.
|
75
|
+
text : Exception text.
|
76
|
+
frame : Number of code to upper level.
|
77
|
+
"""
|
78
|
+
|
79
|
+
# Text.
|
80
|
+
if text is None:
|
81
|
+
if exception.__doc__ is not None:
|
82
|
+
text = exception.__doc__.strip()
|
83
|
+
if (
|
84
|
+
text is None
|
85
|
+
or text == ''
|
86
|
+
):
|
87
|
+
text = 'use error'
|
88
|
+
else:
|
89
|
+
text = text[0].lower() + text[1:]
|
90
|
+
|
91
|
+
## Value.
|
92
|
+
if value is not RNull:
|
93
|
+
values = (value,) + values
|
94
|
+
|
95
|
+
### Name.
|
96
|
+
from .rsystem import get_name
|
97
|
+
name = get_name(value, frame)
|
98
|
+
names = (name,)
|
99
|
+
if values != ():
|
100
|
+
names_values = get_name(values)
|
101
|
+
if names_values is not None:
|
102
|
+
names += names_values
|
103
|
+
|
104
|
+
### Convert.
|
105
|
+
match exception:
|
106
|
+
case TypeError():
|
107
|
+
values = [
|
108
|
+
value.__class__
|
109
|
+
for value in values
|
110
|
+
if value is not None
|
111
|
+
]
|
112
|
+
case TimeoutError():
|
113
|
+
values = [
|
114
|
+
int(value)
|
115
|
+
if value % 1 == 0
|
116
|
+
else round(value, 3)
|
117
|
+
for value in values
|
118
|
+
if value.__class__ == float
|
119
|
+
]
|
120
|
+
values = [
|
121
|
+
repr(value)
|
122
|
+
for value in values
|
123
|
+
]
|
124
|
+
|
125
|
+
### Join.
|
126
|
+
if names == ():
|
127
|
+
values_len = len(values)
|
128
|
+
text_value = ', '.join(values)
|
129
|
+
if values_len == 1:
|
130
|
+
text_value = 'value is ' + text_value
|
131
|
+
else:
|
132
|
+
text_value = 'values is (%s)' % text_value
|
133
|
+
else:
|
134
|
+
names_values = zip(names, values)
|
135
|
+
text_value = ', '.join(
|
136
|
+
[
|
137
|
+
'parameter "%s" is %s' % (name, value)
|
138
|
+
for name, value in names_values
|
139
|
+
]
|
140
|
+
)
|
141
|
+
text += ' %s.' % text_value
|
142
|
+
|
143
|
+
# Throw exception.
|
144
|
+
exception = exception(text)
|
145
|
+
raise exception
|
146
|
+
|
147
|
+
|
148
|
+
def warn(
|
149
|
+
*infos: Any,
|
150
|
+
exception: type[BaseException] = UserWarning,
|
151
|
+
stacklevel: int = 3
|
152
|
+
) -> None:
|
153
|
+
"""
|
154
|
+
Throw warning.
|
155
|
+
|
156
|
+
Parameters
|
157
|
+
----------
|
158
|
+
infos : Warn informations.
|
159
|
+
exception : Exception type.
|
160
|
+
stacklevel : Warning code location, number of recursions up the code level.
|
161
|
+
"""
|
162
|
+
|
163
|
+
# Handle parameter.
|
164
|
+
if infos == ():
|
165
|
+
infos = 'Warning!'
|
166
|
+
elif len(infos) == 1:
|
167
|
+
if infos[0].__class__ == str:
|
168
|
+
infos = infos[0]
|
169
|
+
else:
|
170
|
+
infos = str(infos[0])
|
171
|
+
else:
|
172
|
+
infos = str(infos)
|
173
|
+
|
174
|
+
# Throw warning.
|
175
|
+
warnings_warn(infos, exception, stacklevel)
|
176
|
+
|
177
|
+
|
178
|
+
def catch_exc(
|
179
|
+
title: Optional[str] = None
|
180
|
+
) -> tuple[str, type[BaseException], BaseException, TracebackType]:
|
181
|
+
"""
|
182
|
+
Catch exception information and print, must used in `except` syntax.
|
183
|
+
|
184
|
+
Parameters
|
185
|
+
----------
|
186
|
+
title : Print title.
|
187
|
+
- `None`: Not print.
|
188
|
+
- `str`: Print and use this title.
|
189
|
+
|
190
|
+
Returns
|
191
|
+
-------
|
192
|
+
Exception data.
|
193
|
+
- `str`: Exception report text.
|
194
|
+
- `type[BaseException]`: Exception type.
|
195
|
+
- `BaseException`: Exception instance.
|
196
|
+
- `TracebackType`: Exception traceback instance.
|
197
|
+
"""
|
198
|
+
|
199
|
+
# Get parameter.
|
200
|
+
exc_report = format_exc()
|
201
|
+
exc_report = exc_report.strip()
|
202
|
+
exc_type, exc_instance, exc_traceback = sys_exc_info()
|
203
|
+
|
204
|
+
# Print.
|
205
|
+
if title is not None:
|
206
|
+
|
207
|
+
## Import.
|
208
|
+
from .rstdout import echo
|
209
|
+
|
210
|
+
## Execute.
|
211
|
+
echo(exc_report, title=title, frame='half')
|
212
|
+
|
213
|
+
return exc_report, exc_type, exc_instance, exc_traceback
|
214
|
+
|
215
|
+
|
216
|
+
def check_least_one(*values: Any) -> None:
|
217
|
+
"""
|
218
|
+
Check that at least one of multiple values is not null, when check fail, then throw exception.
|
219
|
+
|
220
|
+
Parameters
|
221
|
+
----------
|
222
|
+
values : Check values.
|
223
|
+
"""
|
224
|
+
|
225
|
+
# Check.
|
226
|
+
for value in values:
|
227
|
+
if value is not None:
|
228
|
+
return
|
229
|
+
|
230
|
+
# Throw exception.
|
231
|
+
from .rsystem import get_name
|
232
|
+
vars_name = get_name(values)
|
233
|
+
if vars_name is not None:
|
234
|
+
vars_name_de_dup = list(set(vars_name))
|
235
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
236
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
237
|
+
else:
|
238
|
+
vars_name_str = ''
|
239
|
+
raise TypeError(f'at least one of parameters{vars_name_str} is not None')
|
240
|
+
|
241
|
+
|
242
|
+
def check_most_one(*values: Any) -> None:
|
243
|
+
"""
|
244
|
+
Check that at most one of multiple values is not null, when check fail, then throw exception.
|
245
|
+
|
246
|
+
Parameters
|
247
|
+
----------
|
248
|
+
values : Check values.
|
249
|
+
"""
|
250
|
+
|
251
|
+
# Check.
|
252
|
+
none_count = 0
|
253
|
+
for value in values:
|
254
|
+
if value is not None:
|
255
|
+
none_count += 1
|
256
|
+
|
257
|
+
# Throw exception.
|
258
|
+
if none_count > 1:
|
259
|
+
from .rsystem import get_name
|
260
|
+
vars_name = get_name(values)
|
261
|
+
if vars_name is not None:
|
262
|
+
vars_name_de_dup = list(set(vars_name))
|
263
|
+
vars_name_de_dup.sort(key=vars_name.index)
|
264
|
+
vars_name_str = ' ' + ' and '.join([f'"{var_name}"' for var_name in vars_name_de_dup])
|
265
|
+
else:
|
266
|
+
vars_name_str = ''
|
267
|
+
raise TypeError(f'at most one of parameters{vars_name_str} is not None')
|
268
|
+
|
269
|
+
|
270
|
+
def check_file_found(path: str) -> None:
|
271
|
+
"""
|
272
|
+
Check if file path found, if not, throw exception.
|
273
|
+
|
274
|
+
Parameters
|
275
|
+
----------
|
276
|
+
path : File path.
|
277
|
+
"""
|
278
|
+
|
279
|
+
# Check.
|
280
|
+
exist = os_exists(path)
|
281
|
+
|
282
|
+
# Throw exception.
|
283
|
+
if not exist:
|
284
|
+
throw(FileNotFoundError, path)
|
285
|
+
|
286
|
+
|
287
|
+
def check_file_exist(path: str) -> None:
|
288
|
+
"""
|
289
|
+
Check if file path exist, if exist, throw exception.
|
290
|
+
|
291
|
+
Parameters
|
292
|
+
----------
|
293
|
+
path : File path.
|
294
|
+
"""
|
295
|
+
|
296
|
+
# Check.
|
297
|
+
exist = os_exists(path)
|
298
|
+
|
299
|
+
# Throw exception.
|
300
|
+
if exist:
|
301
|
+
throw(FileExistsError, path)
|
302
|
+
|
303
|
+
|
304
|
+
def check_response_code(
|
305
|
+
code: int,
|
306
|
+
range_: Optional[Union[int, Iterable[int]]] = None
|
307
|
+
) -> bool:
|
308
|
+
"""
|
309
|
+
Check if the response code is in range.
|
310
|
+
|
311
|
+
Parameters
|
312
|
+
----------
|
313
|
+
code : Response code.
|
314
|
+
range_ : Pass the code range.
|
315
|
+
- `None`: Check if is between 200 and 299.
|
316
|
+
- `int`: Check if is this value.
|
317
|
+
- `Iterable`: Check if is in sequence.
|
318
|
+
|
319
|
+
Returns
|
320
|
+
-------
|
321
|
+
Check result.
|
322
|
+
"""
|
323
|
+
|
324
|
+
# Check.
|
325
|
+
match range_:
|
326
|
+
case None:
|
327
|
+
result = code // 100 == 2
|
328
|
+
case int():
|
329
|
+
result = code == range_
|
330
|
+
case _ if hasattr(range_, '__contains__'):
|
331
|
+
result = code in range_
|
332
|
+
case _:
|
333
|
+
throw(TypeError, range_)
|
334
|
+
|
335
|
+
# Throw exception.
|
336
|
+
if not result:
|
337
|
+
throw(value=code)
|
338
|
+
|
339
|
+
return result
|