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/rschedule.py ADDED
@@ -0,0 +1,272 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2024-01-09 21:44:48
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Schedule methods.
9
+ """
10
+
11
+
12
+ from typing import Any, Literal, Union, Optional
13
+ from collections.abc import Callable
14
+ from apscheduler.executors.pool import ThreadPoolExecutor
15
+ from apscheduler.schedulers.background import BackgroundScheduler
16
+ from apscheduler.schedulers.blocking import BlockingScheduler
17
+ from apscheduler.job import Job
18
+
19
+
20
+ __all__ = (
21
+ 'RSchedule',
22
+ )
23
+
24
+
25
+ class RSchedule(object):
26
+ """
27
+ Rey's `schedule` type.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ max_workers: int = 10,
33
+ max_instances: int = 1,
34
+ coalesce: bool = True,
35
+ block: bool = False
36
+ ) -> None:
37
+ """
38
+ Build `schedule` attributes.
39
+
40
+ Parameters
41
+ ----------
42
+ max_workers : Maximum number of synchronized executions.
43
+ max_instances : Maximum number of synchronized executions of tasks with the same ID.
44
+ coalesce : Whether to coalesce tasks with the same ID.
45
+ block : Whether to block.
46
+ """
47
+
48
+ # Set parameter.
49
+ executor = ThreadPoolExecutor(max_workers)
50
+ executors = {'default': executor}
51
+ job_defaults = {
52
+ 'coalesce': coalesce,
53
+ 'max_instances': max_instances
54
+ }
55
+
56
+ # Instance.
57
+ if block:
58
+ scheduler = BlockingScheduler(
59
+ executors=executors,
60
+ job_defaults=job_defaults
61
+ )
62
+ else:
63
+ scheduler = BackgroundScheduler(
64
+ executors=executors,
65
+ job_defaults=job_defaults
66
+ )
67
+
68
+ # Set attribute.
69
+ self.scheduler = scheduler
70
+
71
+
72
+ def start(self) -> None:
73
+ """
74
+ Start scheduler.
75
+ """
76
+
77
+ # Start.
78
+ self.scheduler.start()
79
+
80
+
81
+ def pause(self) -> None:
82
+ """
83
+ Pause scheduler.
84
+
85
+ Parameters
86
+ ----------
87
+ task : Task instance.
88
+ """
89
+
90
+ # Pause.
91
+ self.scheduler.pause()
92
+
93
+
94
+ def resume(self) -> None:
95
+ """
96
+ Resume scheduler.
97
+
98
+ Parameters
99
+ ----------
100
+ task : Task instance.
101
+ """
102
+
103
+ # Resume.
104
+ self.scheduler.resume()
105
+
106
+
107
+ def tasks(self) -> list[Job]:
108
+ """
109
+ Return to task list.
110
+
111
+ Returns
112
+ -------
113
+ Task list.
114
+ """
115
+
116
+ # Get.
117
+ jobs = self.scheduler.get_jobs()
118
+
119
+ return jobs
120
+
121
+
122
+ def add_task(
123
+ self,
124
+ func: Callable,
125
+ trigger: Literal['date', 'interval', 'cron'] = 'date',
126
+ args: Optional[tuple] = None,
127
+ kwargs: Optional[dict] = None,
128
+ **trigger_kwargs: Any
129
+ ) -> Job:
130
+ """
131
+ Add task.
132
+
133
+ Parameters
134
+ ----------
135
+ func : Task function.
136
+ trigger : Trigger type.
137
+ args : Task position arguments.
138
+ kwargs : Task keyword arguments.
139
+ trigger_kwargs : Trigger keyword arguments.
140
+
141
+ Returns
142
+ -------
143
+ Task instance.
144
+ """
145
+
146
+ # Add.
147
+ job = self.scheduler.add_job(
148
+ func,
149
+ trigger,
150
+ args,
151
+ kwargs,
152
+ **trigger_kwargs
153
+ )
154
+
155
+ return job
156
+
157
+
158
+ def modify_task(
159
+ self,
160
+ task: Union[Job, str],
161
+ trigger: Optional[Literal['date', 'interval', 'cron']] = None,
162
+ args: Optional[tuple] = None,
163
+ kwargs: Optional[dict] = None,
164
+ **trigger_kwargs: Any
165
+ ) -> None:
166
+ """
167
+ Modify task.
168
+
169
+ Parameters
170
+ ----------
171
+ task : Task instance or ID.
172
+ trigger : Trigger type.
173
+ args : Task position arguments.
174
+ kwargs : Task keyword arguments.
175
+ trigger_kwargs : Trigger keyword arguments.
176
+ """
177
+
178
+ # Arguments.
179
+
180
+ ## Get parameter.
181
+ params_arg = {}
182
+ if args is not None:
183
+ params_arg['args'] = args
184
+ if kwargs is not None:
185
+ params_arg['kwargs'] = kwargs
186
+
187
+ ## Modify.
188
+ if params_arg != {}:
189
+ self.scheduler.modify_job(
190
+ task.id,
191
+ **params_arg
192
+ )
193
+
194
+ # Trigger.
195
+ if (
196
+ trigger is not None
197
+ or trigger_kwargs != {}
198
+ ):
199
+ self.scheduler.reschedule_job(
200
+ task.id,
201
+ trigger=trigger,
202
+ **trigger_kwargs
203
+ )
204
+
205
+
206
+ def remove_task(
207
+ self,
208
+ task: Union[Job, str]
209
+ ) -> None:
210
+ """
211
+ Remove task.
212
+
213
+ Parameters
214
+ ----------
215
+ task : Task instance or ID.
216
+ """
217
+
218
+ # Get parameter.
219
+ if task.__class__ == Job:
220
+ id_ = task.id
221
+ else:
222
+ id_ = task
223
+
224
+ # Remove.
225
+ self.scheduler.remove_job(id_)
226
+
227
+
228
+ def pause_task(
229
+ self,
230
+ task: Union[Job, str]
231
+ ) -> None:
232
+ """
233
+ Pause task.
234
+
235
+ Parameters
236
+ ----------
237
+ task : Task instance or ID.
238
+ """
239
+
240
+ # Get parameter.
241
+ if task.__class__ == Job:
242
+ id_ = task.id
243
+ else:
244
+ id_ = task
245
+
246
+ # Pause.
247
+ self.scheduler.pause_job(id_)
248
+
249
+
250
+ def resume_task(
251
+ self,
252
+ task: Union[Job, str]
253
+ ) -> None:
254
+ """
255
+ Resume task.
256
+
257
+ Parameters
258
+ ----------
259
+ task : Task instance or ID.
260
+ """
261
+
262
+ # Get parameter.
263
+ if task.__class__ == Job:
264
+ id_ = task.id
265
+ else:
266
+ id_ = task
267
+
268
+ # Resume.
269
+ self.scheduler.resume_job(id_)
270
+
271
+
272
+ __iter__ = tasks
reykit/rstdout.py ADDED
@@ -0,0 +1,356 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2023-10-01 14:47:47
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Standard output methods.
9
+ """
10
+
11
+
12
+ from typing import Any, Literal, Optional, Union, Final, Self
13
+ from collections.abc import Callable
14
+ import sys
15
+ from io import TextIOWrapper
16
+ from os import devnull as os_devnull
17
+ from os.path import abspath as os_abspath
18
+
19
+ from .rsystem import get_first_notnull, get_name, get_stack_param
20
+ from .rtext import to_text, add_text_frame
21
+ from .rtype import RConfigMeta
22
+
23
+
24
+ __all__ = (
25
+ 'RConfigStdout',
26
+ 'beautify_text',
27
+ 'echo',
28
+ 'rinput',
29
+ 'stop_print',
30
+ 'start_print',
31
+ 'modify_print',
32
+ 'reset_print',
33
+ 'add_print_position'
34
+ )
35
+
36
+
37
+ class RConfigStdout(object, metaclass=RConfigMeta):
38
+ """
39
+ Rey's `config standard output` type.
40
+ """
41
+
42
+ # Module path.
43
+ _path_rstdout: Final[str] = os_abspath(__file__)
44
+
45
+ # Status.
46
+ _stoped: bool = False
47
+ _modified: bool = False
48
+
49
+ # IO.
50
+ _io_null: TextIOWrapper = open(os_devnull, 'w')
51
+ _io_stdout: TextIOWrapper = sys.stdout
52
+ _io_stdout_write: Callable[[str], int] = sys.stdout.write
53
+
54
+ # Is the print frame plain.
55
+ is_frame_plain: bool = False
56
+
57
+ # print default width.
58
+ default_width: int = 100
59
+
60
+
61
+ def beautify_text(
62
+ data: tuple[Any],
63
+ title: Union[bool, str] = True,
64
+ width: Optional[int] = None,
65
+ frame: Optional[Literal['full', 'half', 'top', 'half_plain', 'top_plain']] = 'full'
66
+ ) -> str:
67
+ """
68
+ Beautify data to text.
69
+
70
+ Parameters
71
+ ----------
72
+ data : Text data.
73
+ title : Text title.
74
+ - `Literal[True]`: Automatic get data variable name.
75
+ - `Literal[False]`: No title.
76
+ - `str`: Use this value as the title.
77
+ width : Text width.
78
+ - `None`: Use attribute `RConfigStdout.default_width`.
79
+ - `int`: Use this value.
80
+ frame : Text frame type.
81
+ - `Literal[`full`]`: Add beautiful four side frame and limit length.
82
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
83
+ When throw `exception`, then frame is `half` type.
84
+ - `Literal[`half`]`: Add beautiful top and bottom side frame.
85
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
86
+ - `Literal[`top`]`: Add beautiful top side frame.
87
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `top_plain` type.
88
+ - `Literal[`half_plain`]`: Add plain top and bottom side frame.
89
+ - `Literal[`top_plain`]`: Add plain top side frame.
90
+
91
+ Returns
92
+ -------
93
+ Beautify text.
94
+ """
95
+
96
+ # Get parameter.
97
+
98
+ ## Title.
99
+ if title is True:
100
+ titles = get_name(data, 3)
101
+ if titles is not None:
102
+ titles = [title if not title.startswith('`') else '' for title in titles]
103
+ if set(titles) != {''}:
104
+ title = ' │ '.join(titles)
105
+ if title.__class__ != str:
106
+ title = None
107
+
108
+ ## Width.
109
+ width = get_first_notnull(width, RConfigStdout.default_width, default='exception')
110
+
111
+ ## Frame.
112
+ if RConfigStdout.is_frame_plain:
113
+ match frame:
114
+ case 'full':
115
+ frame = 'half_plain'
116
+ case 'half':
117
+ frame = 'half_plain'
118
+ case 'top':
119
+ frame = 'top_plain'
120
+
121
+ # To text.
122
+ text_list = [
123
+ to_text(content, width=width)
124
+ for content in data
125
+ ]
126
+
127
+ # Add frame.
128
+ text = add_text_frame(*text_list, title=title, width=width, frame=frame)
129
+
130
+ return text
131
+
132
+
133
+ def echo(
134
+ *data: Any,
135
+ title: Union[bool, str] = True,
136
+ width: Optional[int] = None,
137
+ frame: Optional[Literal['full', 'half', 'top', 'half_plain', 'top_plain']] = 'full'
138
+ ) -> str:
139
+ """
140
+ Beautify data to text, and print.
141
+
142
+ Parameters
143
+ ----------
144
+ data : Text data.
145
+ title : Text title.
146
+ - `Literal[True]`: Automatic get data variable name.
147
+ - `Literal[False]`: No title.
148
+ - `str`: Use this value as the title.
149
+ width : Text width.
150
+ - `None`: Use attribute `RConfigStdout.default_width`.
151
+ - `int`: Use this value.
152
+ frame : Text frame type.
153
+ - `Literal[`full`]`: Add beautiful four side frame and limit length.
154
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
155
+ When throw `exception`, then frame is `half` type.
156
+ - `Literal[`half`]`: Add beautiful top and bottom side frame.
157
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
158
+ - `Literal[`top`]`: Add beautiful top side frame.
159
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `top_plain` type.
160
+ - `Literal[`half_plain`]`: Add plain top and bottom side frame.
161
+ - `Literal[`top_plain`]`: Add plain top side frame.
162
+
163
+ Returns
164
+ -------
165
+ Beautify text.
166
+ """
167
+
168
+ # Beautify.
169
+ text = beautify_text(data, title=title, width=width, frame=frame)
170
+
171
+ # Print.
172
+ print(text)
173
+
174
+ return text
175
+
176
+
177
+ def rinput(
178
+ *data: Any,
179
+ title: Union[bool, str] = True,
180
+ width: Optional[int] = None,
181
+ frame: Optional[Literal['full', 'half', 'top', 'half_plain', 'top_plain']] = 'full',
182
+ extra: Optional[str] = None
183
+ ) -> str:
184
+ """
185
+ Beautify data to text, and print data, and read string from standard input.
186
+
187
+ Parameters
188
+ ----------
189
+ data : Text data.
190
+ title : Text title.
191
+ - `Literal[True]`: Automatic get data variable name.
192
+ - `Literal[False]`: No title.
193
+ - `str`: Use this value as the title.
194
+ width : Text width.
195
+ - `None`: Use attribute `RConfigStdout.default_width`.
196
+ - `int`: Use this value.
197
+ frame : Text frame type.
198
+ - `Literal[`full`]`: Add beautiful four side frame and limit length.
199
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
200
+ When throw `exception`, then frame is `half` type.
201
+ - `Literal[`half`]`: Add beautiful top and bottom side frame.
202
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `half_plain` type.
203
+ - `Literal[`top`]`: Add beautiful top side frame.
204
+ When attribute `RConfigStdout.is_frame_plain` is True, then frame is `top_plain` type.
205
+ - `Literal[`half_plain`]`: Add plain top and bottom side frame.
206
+ - `Literal[`top_plain`]`: Add plain top side frame.
207
+ extra : Extra print text at the end.
208
+
209
+ Returns
210
+ -------
211
+ Standard input string.
212
+ """
213
+
214
+ # Beautify.
215
+ text = beautify_text(data, title=title, width=width, frame=frame)
216
+
217
+ # Extra.
218
+ if extra is not None:
219
+ text += extra
220
+
221
+ # Print.
222
+ stdin = input(text)
223
+
224
+ return stdin
225
+
226
+
227
+ def stop_print() -> None:
228
+ """
229
+ Stop standard output print.
230
+ """
231
+
232
+ # Stop.
233
+ sys.stdout = RConfigStdout._io_null
234
+
235
+ # Update status.
236
+ RConfigStdout._stoped = True
237
+
238
+
239
+ def start_print() -> None:
240
+ """
241
+ Start standard output print.
242
+ """
243
+
244
+ # Check.
245
+ if not RConfigStdout._stoped: return
246
+
247
+ # Start.
248
+ sys.stdout = RConfigStdout._io_stdout
249
+
250
+ # Update status.
251
+ RConfigStdout._stoped = False
252
+
253
+
254
+ def modify_print(preprocess: Callable[[str], Optional[str]]) -> None:
255
+ """
256
+ Modify standard output print write method.
257
+
258
+ Parameters
259
+ ----------
260
+ preprocess : Preprocess function.
261
+ - `Callable[[str], str]`: Input old text, output new text, will trigger printing.
262
+ - `Callable[[str], None]`: Input old text, no output, will not trigger printing.
263
+ """
264
+
265
+
266
+ # Define.
267
+ def write(__s: str) -> Optional[int]:
268
+ """
269
+ Modified standard output write method.
270
+
271
+ Parameters
272
+ ----------
273
+ __s : Write text.
274
+
275
+ Returns
276
+ -------
277
+ Number of text characters.
278
+ """
279
+
280
+ # Preprocess.
281
+ __s = preprocess(__s)
282
+
283
+ # Write.
284
+ if __s.__class__ == str:
285
+ write_len = RConfigStdout._io_stdout_write(__s)
286
+ return write_len
287
+
288
+
289
+ # Modify.
290
+ RConfigStdout._io_stdout.write = write
291
+
292
+ # Update status.
293
+ RConfigStdout._modified = True
294
+
295
+
296
+ def reset_print() -> None:
297
+ """
298
+ Reset standard output print write method.
299
+ """
300
+
301
+ # Check.
302
+ if not RConfigStdout._modified: return
303
+
304
+ # Reset.
305
+ RConfigStdout._io_stdout.write = RConfigStdout._io_stdout_write
306
+
307
+ # Update status.
308
+ RConfigStdout._modified = False
309
+
310
+
311
+ def add_print_position() -> None:
312
+ """
313
+ Add position text to standard output.
314
+ """
315
+
316
+
317
+ # Define.
318
+ def preprocess(__s: str) -> str:
319
+ """
320
+ Preprocess function.
321
+
322
+ Parameters
323
+ ----------
324
+ __s : Standard ouput text.
325
+
326
+ Returns
327
+ -------
328
+ Preprocessed text.
329
+ """
330
+
331
+ # Break.
332
+ if __s in ('\n', ' ', ''): return __s
333
+
334
+ # Get parameter.
335
+ stack_params = get_stack_param('full', 3)
336
+ stack_floor = stack_params[-1]
337
+
338
+ ## Compatible 'echo'.
339
+ if (
340
+ stack_floor['filename'] == RConfigStdout._path_rstdout
341
+ and stack_floor['name'] == 'echo'
342
+ ):
343
+ stack_floor = stack_params[-2]
344
+
345
+ # Add.
346
+ __s = 'File "%s", line %s\n%s' % (
347
+ stack_floor['filename'],
348
+ stack_floor['lineno'],
349
+ __s
350
+ )
351
+
352
+ return __s
353
+
354
+
355
+ # Modify.
356
+ modify_print(preprocess)