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 ADDED
@@ -0,0 +1,41 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2022-12-05 14:09:21
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Rey's kit method set.
9
+
10
+ Modules
11
+ -------
12
+ rall : All methods.
13
+ rcomm : Network communication methods.
14
+ rdata : Data methods.
15
+ rdll : DLL file methods.
16
+ remail : E-mail methods.
17
+ rexception : Exception methods.
18
+ rimage : Image methods.
19
+ rlog : Log methods.
20
+ rmonkey : Monkey patch methods.
21
+ rmultitask : Multi task methods.
22
+ rnumber : Number methods.
23
+ ros: Operation system methods.
24
+ rrandom : Random methods.
25
+ rregex : Regular expression methods.
26
+ rschedule : Schedule methods.
27
+ rstdout : Standard output methods.
28
+ rsystem : Interpreter system methods.
29
+ rtable : Table methods.
30
+ rtext : Text methods.
31
+ rtime : Time methods.
32
+ rtype : Type methods.
33
+ rwrap : Decorators.
34
+ rzip : File compress methods.
35
+ """
36
+
37
+
38
+ from typing import Final
39
+
40
+
41
+ __version__: Final[str] = '1.0.0'
reykit/rall.py ADDED
@@ -0,0 +1,33 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2022-12-08 13:11:09
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : All methods.
9
+ """
10
+
11
+
12
+ from .rcomm import *
13
+ from .rdata import *
14
+ from .rdll import *
15
+ from .remail import *
16
+ from .rexception import *
17
+ from .rimage import *
18
+ from .rlog import *
19
+ from .rmonkey import *
20
+ from .rmultitask import *
21
+ from .rnumber import *
22
+ from .ros import *
23
+ from .rrandom import *
24
+ from .rregex import *
25
+ from .rschedule import *
26
+ from .rstdout import *
27
+ from .rsystem import *
28
+ from .rtable import *
29
+ from .rtext import *
30
+ from .rtime import *
31
+ from .rtype import *
32
+ from .rwrap import *
33
+ from .rzip import *
reykit/rcomm.py ADDED
@@ -0,0 +1,431 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2022-12-08 11:07:25
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Network communication methods.
9
+ """
10
+
11
+
12
+ from typing import Any, Literal, Optional, Union
13
+ from collections.abc import Callable, Iterable
14
+ from warnings import filterwarnings
15
+ from os.path import abspath as os_abspath, isfile as os_isfile
16
+ from socket import socket as Socket
17
+ from urllib.parse import urlsplit as urllib_urlsplit, quote as urllib_quote, unquote as urllib_unquote
18
+ from requests.api import request as requests_request
19
+ from requests.models import Response
20
+ from mimetypes import guess_type
21
+ from filetype import guess as filetype_guess
22
+
23
+ from .rexception import throw, check_response_code
24
+ from .ros import RFile
25
+ from .rregex import search
26
+
27
+
28
+ __all__ = (
29
+ 'join_url',
30
+ 'split_url',
31
+ 'join_cookie',
32
+ 'split_cookie',
33
+ 'get_content_type',
34
+ 'request',
35
+ 'download',
36
+ 'get_file_stream_time',
37
+ 'listen_socket'
38
+ )
39
+
40
+
41
+ def join_url(url: str, params: dict) -> str:
42
+ """
43
+ Join URL and parameters.
44
+
45
+ Parameters
46
+ ----------
47
+ url : URL.
48
+ params : Parameters of URL.
49
+
50
+ Returns
51
+ -------
52
+ Joined URL.
53
+ """
54
+
55
+ # Join parameter.
56
+ params_str = '&'.join(
57
+ [
58
+ f'{key}={urllib_quote(value)}'
59
+ for key, value in params.items()
60
+ ]
61
+ )
62
+
63
+ # Join URL.
64
+ if '?' not in url:
65
+ url += '?'
66
+ elif url[-1] != '?':
67
+ url += '&'
68
+ url += params_str
69
+
70
+ return url
71
+
72
+
73
+ def split_url(url: str) -> tuple[str, dict[str, str]]:
74
+ """
75
+ Split URL and parameters.
76
+
77
+ Parameters
78
+ ----------
79
+ url : URL.
80
+
81
+ Returns
82
+ -------
83
+ Split URL and parameters.
84
+ """
85
+
86
+ # Split URL.
87
+ split_result = urllib_urlsplit(url)
88
+ params_str = split_result.query
89
+ url = split_result.scheme + '://' + split_result.netloc + split_result.path
90
+
91
+ # Split parameter.
92
+ params = {
93
+ key: urllib_unquote(value)
94
+ for key, value in map(
95
+ lambda item: item.split('=', 1),
96
+ params_str.split('&')
97
+ )
98
+ }
99
+
100
+ return url, params
101
+
102
+
103
+ def join_cookie(params: dict[str, str]) -> str:
104
+ """
105
+ Join parameters of Cookie.
106
+
107
+ Parameters
108
+ ----------
109
+ params : Parameters.
110
+
111
+ Returns
112
+ -------
113
+ Joined cookie.
114
+ """
115
+
116
+ # Join.
117
+ cookie = '; '.join(
118
+ [
119
+ f'{key}={value}'
120
+ for key, value in params.items()
121
+ ]
122
+ )
123
+
124
+ return cookie
125
+
126
+
127
+ def split_cookie(cookie: str) -> dict[str, str]:
128
+ """
129
+ Split parameters of Cookie.
130
+
131
+ Parameters
132
+ ----------
133
+ cookie : Cookie.
134
+
135
+ Returns
136
+ -------
137
+ Split parameters
138
+ """
139
+
140
+ # Split parameter.
141
+ params = {
142
+ key: value
143
+ for key, value in map(
144
+ lambda item: item.split('=', 1),
145
+ cookie.split('; ')
146
+ )
147
+ }
148
+
149
+ return params
150
+
151
+
152
+ def get_content_type(file: Union[str, bytes]) -> Optional[str]:
153
+ """
154
+ Get HTTP content type of file.
155
+
156
+ Parameters
157
+ ----------
158
+ file : File path or bytes data.
159
+
160
+ Returns
161
+ -------
162
+ HTTP content type.
163
+ """
164
+
165
+ # Guess.
166
+ if (
167
+ (
168
+ file.__class__ == str
169
+ and os_isfile(file)
170
+ ) or file.__class__ == bytes
171
+ ):
172
+ file_type_obj = filetype_guess(file)
173
+ else:
174
+ file_type_obj = None
175
+ if file_type_obj is not None:
176
+ file_type = file_type_obj.MIME
177
+ elif file.__class__ == str:
178
+ file_type, _ = guess_type(file)
179
+ else:
180
+ file_type = None
181
+
182
+ return file_type
183
+
184
+
185
+ def request(
186
+ url: str,
187
+ params: Optional[dict] = None,
188
+ data: Optional[Union[dict, str, bytes]] = None,
189
+ json: Optional[dict] = None,
190
+ files: Optional[dict[str, Union[str, bytes, tuple[Union[str, bytes], dict]]]] = None,
191
+ headers: dict[str, str] = {},
192
+ timeout: Optional[float] = None,
193
+ proxies: dict[str, str] = {},
194
+ stream: bool = False,
195
+ verify: bool = False,
196
+ method: Optional[Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']] = None,
197
+ check: Union[bool, int, Iterable[int]] = False
198
+ ) -> Response:
199
+ """
200
+ Send request.
201
+
202
+ Parameters
203
+ ----------
204
+ url : Request URL.
205
+ params : Request URL add parameters.
206
+ data : Request body data.
207
+ - `dict`, Convert to `key=value&...`: format bytes.
208
+ Automatic set `Content-Type` to `application/x-www-form-urlencoded`.
209
+ - `str`: File path to read file bytes data.
210
+ Automatic set `Content-Type` to file media type, and `filename` to file name.
211
+ - `bytes`: File bytes data.
212
+ Automatic set `Content-Type` to file media type.
213
+ json : Request body data, convert to `JSON` format.
214
+ Automatic set `Content-Type` to `application/json`.
215
+ files : Request body data, convert to `multi form` format.
216
+ Automatic set `Content-Type` to `multipart/form-data`.
217
+ - `dict[str, str]`: Parameter name and File path to read file bytes data.
218
+ Automatic set `Content-Type` to file media type, and `filename` to file name.
219
+ - `dict[str, bytes]`: Parameter name and file bytes data.
220
+ - `dict[str, tuple[str, dict]`: Parameter name and File path to read file bytes data and other parameters.
221
+ Automatic set `Content-Type` to file media type, and `filename` to file name.
222
+ - `dict[str, tuple[bytes, dict]`: Parameter name and file bytes data and other parameters.
223
+ headers : Request header data.
224
+ timeout : Request maximun waiting time.
225
+ proxies : Proxy IP setup.
226
+ - `None`: No setup.
227
+ - `dict[str, str]`: Name and use IP of each protocol.
228
+ stream : Whether use stream request.
229
+ verify : Whether verify certificate.
230
+ method : Request method.
231
+ - `None`: Automatic judge.
232
+ When parameter `data` or `json` or `files` not has value, then request method is `get`.
233
+ When parameter `data` or `json` or `files` has value, then request method is `post`.
234
+ - `Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']`: Use this request method.
235
+ check : Check response code, and throw exception.
236
+ - `Literal[False]`: Not check.
237
+ - `Literal[True]`: Check if is between 200 and 299.
238
+ - `int`: Check if is this value.
239
+ - `Iterable`: Check if is in sequence.
240
+
241
+ Returns
242
+ -------
243
+ Response object of requests package.
244
+ """
245
+
246
+ # Handle parameter.
247
+ if method is None:
248
+ if data is None and json is None and files is None:
249
+ method = 'get'
250
+ else:
251
+ method = 'post'
252
+ if files is None:
253
+ if data.__class__ == str:
254
+ rfile = RFile(data)
255
+ data = rfile.bytes
256
+ if 'Content-Disposition' not in headers:
257
+ file_name = rfile.name_suffix
258
+ headers['Content-Disposition'] = f'attachment; filename={file_name}'
259
+ if data.__class__ == bytes:
260
+ if 'Content-Type' not in headers:
261
+ headers['Content-Type'] = get_content_type(data)
262
+ else:
263
+ for key, value in files.items():
264
+ if value.__class__ == tuple:
265
+ item_data, item_headers = value
266
+ else:
267
+ item_data, item_headers = value, {}
268
+ if item_data.__class__ == str:
269
+ rfile = RFile(item_data)
270
+ data = rfile.bytes
271
+ item_headers.setdefault('filename', rfile.name_suffix)
272
+ if item_data.__class__ == bytes:
273
+ if 'Content-Type' not in item_headers:
274
+ item_headers['Content-Type'] = get_content_type(item_data)
275
+ files[key] = (
276
+ item_headers.get('filename', key),
277
+ item_data,
278
+ item_headers.get('Content-Type'),
279
+ item_headers
280
+ )
281
+ if not verify:
282
+ filterwarnings(
283
+ 'ignore',
284
+ 'Unverified HTTPS request is being made to host'
285
+ )
286
+
287
+ # Request.
288
+ response = requests_request(
289
+ method,
290
+ url,
291
+ params=params,
292
+ data=data,
293
+ json=json,
294
+ files=files,
295
+ headers=headers,
296
+ timeout=timeout,
297
+ proxies=proxies,
298
+ verify=verify,
299
+ stream=stream
300
+ )
301
+
302
+ # Set encod type.
303
+ if response.encoding == 'ISO-8859-1':
304
+ pattern = r'<meta [^>]*charset=([\w-]+)[^>]*>'
305
+ charset = search(pattern, response.text)
306
+ if charset is None:
307
+ charset = 'utf-8'
308
+ response.encoding = charset
309
+
310
+ # Check code.
311
+ if check is not False:
312
+ if check is True:
313
+ range_ = None
314
+ else:
315
+ range_ = check
316
+ check_response_code(response.status_code, range_)
317
+
318
+ return response
319
+
320
+
321
+ def download(url: str, path: Optional[str] = None) -> str:
322
+ """
323
+ Download file from URL.
324
+
325
+ Parameters
326
+ ----------
327
+ url : Download URL.
328
+ path : Save path.
329
+ - `None`, File name is `download`: and automatic judge file type.
330
+
331
+ Returns
332
+ -------
333
+ File absolute path.
334
+ """
335
+
336
+ # Download.
337
+ response = request(url)
338
+ content = response.content
339
+
340
+ # Judge file type and path.
341
+ if path is None:
342
+ Content_disposition = response.headers.get('Content-Disposition', '')
343
+ if 'filename' in Content_disposition:
344
+ file_name = search(
345
+ 'filename=[\'"]?([^\\s\'"]+)',
346
+ Content_disposition
347
+ )
348
+ else:
349
+ file_name = None
350
+ if file_name is None:
351
+ file_type_obj = get_content_type(content)
352
+ if file_type_obj is not None:
353
+ file_name = 'download.' + file_type_obj.EXTENSION
354
+ if file_name is None:
355
+ file_name = 'download'
356
+ path = os_abspath(file_name)
357
+
358
+ # Save.
359
+ rfile = RFile(path)
360
+ rfile(content)
361
+
362
+ return path
363
+
364
+
365
+ def get_file_stream_time(
366
+ file: Union[str, bytes, int],
367
+ bandwidth: float
368
+ ) -> float:
369
+ """
370
+ Get file stream transfer time, unit second.
371
+
372
+ Parameters
373
+ ----------
374
+ file : File data.
375
+ - `str`: File path.
376
+ - `bytes`: File bytes data.
377
+ - `int`: File bytes size.
378
+ bandwidth : Bandwidth, unit Mpbs.
379
+
380
+ Returns
381
+ -------
382
+ File send seconds.
383
+ """
384
+
385
+ # Get parameter.
386
+ match file:
387
+ case str():
388
+ rfile = RFile(file)
389
+ file_size = rfile.size
390
+ case bytes() | bytearray():
391
+ file_size = len(file)
392
+ case int():
393
+ file_size = file
394
+ case _:
395
+ throw(TypeError, file)
396
+
397
+ # Calculate.
398
+ seconds = file_size / 125_000 / bandwidth
399
+
400
+ return seconds
401
+
402
+
403
+ def listen_socket(
404
+ host: str,
405
+ port: Union[str, int],
406
+ handler: Callable[[bytes], Any]
407
+ ) -> None:
408
+ """
409
+ Listen socket and handle data.
410
+
411
+ Parameters
412
+ ----------
413
+ host : Socket host.
414
+ port : Socket port.
415
+ handler : Handler function.
416
+ """
417
+
418
+ # Handle parameter.
419
+ port = int(port)
420
+ rece_size = 1024 * 1024 * 1024
421
+
422
+ # Instance.
423
+ socket = Socket()
424
+ socket.bind((host, port))
425
+ socket.listen()
426
+
427
+ # Loop.
428
+ while True:
429
+ socket_conn, _ = socket.accept()
430
+ data = socket_conn.recv(rece_size)
431
+ handler(data)