task-scheduling 2.3.0__tar.gz → 2.3.2__tar.gz
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.
- {task_scheduling-2.3.0/task_scheduling.egg-info → task_scheduling-2.3.2}/PKG-INFO +85 -4
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/README.md +84 -3
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/pyproject.toml +1 -1
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/setup.py +1 -1
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/__init__.py +2 -2
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/pause_run.py +4 -4
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/process_manager.py +9 -9
- task_scheduling-2.3.2/task_scheduling/control/terminate_run.py +109 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/thread_manager.py +9 -9
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/cpu_asyncio_task.py +5 -3
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/cpu_liner_task.py +10 -9
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/io_asyncio_task.py +1 -1
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/io_liner_task.py +10 -8
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/timer_task.py +17 -14
- {task_scheduling-2.3.0 → task_scheduling-2.3.2/task_scheduling.egg-info}/PKG-INFO +85 -4
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling.egg-info/SOURCES.txt +1 -1
- task_scheduling-2.3.0/task_scheduling/control/skip_run.py +0 -39
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/LICENSE +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/MANIFEST.in +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/setup.cfg +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/common/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/common/log_config.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/config/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/config/config.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/config/config.yaml +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/timeout_run.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/control/utils.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/manager/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/manager/task_details_queue.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler_management.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler_tools/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler_tools/task_scheduler.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_creation.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_data/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_data/function_type.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_data/tag_added.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_info/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_info/control_ui.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_info/queue_info_display.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/utils/__init__.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/utils/is_async_function.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/utils/random_name.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/utils/sleep.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/utils/worker_initializer.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/variable.py +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling.egg-info/dependency_links.txt +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling.egg-info/requires.txt +0 -0
- {task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: task_scheduling
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: It is mainly used for task scheduling
|
|
5
5
|
Home-page: https://github.com/fallingmeteorite/task_scheduling
|
|
6
6
|
Author: fallingmeteorite
|
|
@@ -81,7 +81,9 @@ pip install --upgrade task_scheduling
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
# Interface UI
|
|
84
|
+
|
|
84
85
|
Use the function in code
|
|
86
|
+
|
|
85
87
|
```
|
|
86
88
|
|
|
87
89
|
from task_scheduling.task_info import start_task_status_ui
|
|
@@ -95,13 +97,16 @@ Task status UI available at http://localhost:8000
|
|
|
95
97
|
# Function introduction
|
|
96
98
|
|
|
97
99
|
## Tips
|
|
100
|
+
|
|
98
101
|
1.Detailed information requires changing the log level to `debug`.
|
|
99
102
|
|
|
100
|
-
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
103
|
+
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
104
|
+
tasks by injecting exceptions.
|
|
101
105
|
|
|
102
106
|
3.Most of the API calls for this task scheduling can be added to the scheduler for execution as tasks.
|
|
103
107
|
|
|
104
108
|
4.The log display level is available
|
|
109
|
+
|
|
105
110
|
```
|
|
106
111
|
from task_scheduling.common import set_log_level
|
|
107
112
|
|
|
@@ -110,7 +115,9 @@ set_log_level("DEBUG") # INFO, DEBUG, ERROR, WARNING
|
|
|
110
115
|
if __name__ == "__main__":
|
|
111
116
|
......
|
|
112
117
|
```
|
|
113
|
-
|
|
118
|
+
|
|
119
|
+
5.The task priority is modified to high when necessary, and the task will start executing directly after submission.
|
|
120
|
+
However, this can also cause some running tasks to pause to free up resources to run high-priority tasks
|
|
114
121
|
|
|
115
122
|
### Function: task_creation(delay: int or None, daily_time: str or None, function_type: str, timeout_processing: bool, task_name: str, func: Callable, *args, **kwargs) -> str or None:
|
|
116
123
|
|
|
@@ -376,6 +383,78 @@ if __name__ == "__main__":
|
|
|
376
383
|
|
|
377
384
|
```
|
|
378
385
|
|
|
386
|
+
### pause_and_resume_task(self, task_id: str, action: str) -> bool:
|
|
387
|
+
|
|
388
|
+
A function is used to pause a task and resume running at a specified time.
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
import asyncio
|
|
392
|
+
import time
|
|
393
|
+
|
|
394
|
+
from task_scheduling.utils import interruptible_sleep
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def line_task(input_info):
|
|
398
|
+
for i in range(10):
|
|
399
|
+
interruptible_sleep(1)
|
|
400
|
+
print(input_info)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def asyncio_task(input_info):
|
|
404
|
+
for i in range(10):
|
|
405
|
+
await asyncio.sleep(1)
|
|
406
|
+
print(input_info)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
input_info = "test"
|
|
410
|
+
|
|
411
|
+
from task_scheduling.common import set_log_level
|
|
412
|
+
|
|
413
|
+
set_log_level("DEBUG")
|
|
414
|
+
if __name__ == "__main__":
|
|
415
|
+
from task_scheduling.task_creation import task_creation, shutdown
|
|
416
|
+
from task_scheduling.task_info import start_task_status_ui
|
|
417
|
+
from task_scheduling.scheduler import io_liner_task
|
|
418
|
+
|
|
419
|
+
start_task_status_ui()
|
|
420
|
+
from task_scheduling.variable import *
|
|
421
|
+
|
|
422
|
+
task_id1 = task_creation(None,
|
|
423
|
+
# This is how long the delay is executed (in seconds)
|
|
424
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the daily_time is not required
|
|
425
|
+
None,
|
|
426
|
+
# This is to be performed at what point (24-hour clock)
|
|
427
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the delay is not required
|
|
428
|
+
scheduler_io,
|
|
429
|
+
# Running function type, there are "io, cpu, timer"
|
|
430
|
+
True,
|
|
431
|
+
# Set to True to enable timeout detection, tasks that do not finish within the runtime will be forcibly terminated
|
|
432
|
+
"task2",
|
|
433
|
+
# Task ID, in linear tasks, tasks with the same ID will be queued, different IDs will be executed directly, the same applies to asynchronous tasks
|
|
434
|
+
line_task,
|
|
435
|
+
# The function to be executed, parameters should not be passed here
|
|
436
|
+
priority_low,
|
|
437
|
+
# The task priority is modified to high when necessary, and the task will start executing directly after submission
|
|
438
|
+
input_info
|
|
439
|
+
# Pass the parameters required by the function, no restrictions
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# cf478b6e-5e02-49b8-9031-4adc6ff915c2, cf478b6e-5e02-49b8-9031-4adc6ff915c2
|
|
443
|
+
time.sleep(2.0)
|
|
444
|
+
print("Pause")
|
|
445
|
+
io_liner_task.pause_and_resume_task(task_id1, "pause")
|
|
446
|
+
time.sleep(4.0)
|
|
447
|
+
print("Resume")
|
|
448
|
+
io_liner_task.pause_and_resume_task(task_id1, "resume")
|
|
449
|
+
|
|
450
|
+
try:
|
|
451
|
+
while True:
|
|
452
|
+
time.sleep(1.0)
|
|
453
|
+
except KeyboardInterrupt:
|
|
454
|
+
shutdown(True)
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
|
|
379
458
|
### FunctionRunner(self, func: Callable, task_name: str, *args, **kwargs) -> None:
|
|
380
459
|
|
|
381
460
|
A task that detects what type of function is executed. The detected parameters are stored in a file and read directly at
|
|
@@ -818,9 +897,11 @@ if __name__ == "__main__":
|
|
|
818
897
|
### force_stop_task(task_id: str) -> bool:
|
|
819
898
|
|
|
820
899
|
All 4 schedulers have this function for terminating a running task. Warning: If the function is executing a
|
|
821
|
-
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
900
|
+
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
901
|
+
function
|
|
822
902
|
|
|
823
903
|
Warn!!! `cpu_liner_task.force_stop_task()` is different and requires the addition of additional parameters
|
|
904
|
+
|
|
824
905
|
```
|
|
825
906
|
import asyncio
|
|
826
907
|
import time
|
|
@@ -58,7 +58,9 @@ pip install --upgrade task_scheduling
|
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
# Interface UI
|
|
61
|
+
|
|
61
62
|
Use the function in code
|
|
63
|
+
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
from task_scheduling.task_info import start_task_status_ui
|
|
@@ -72,13 +74,16 @@ Task status UI available at http://localhost:8000
|
|
|
72
74
|
# Function introduction
|
|
73
75
|
|
|
74
76
|
## Tips
|
|
77
|
+
|
|
75
78
|
1.Detailed information requires changing the log level to `debug`.
|
|
76
79
|
|
|
77
|
-
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
80
|
+
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
81
|
+
tasks by injecting exceptions.
|
|
78
82
|
|
|
79
83
|
3.Most of the API calls for this task scheduling can be added to the scheduler for execution as tasks.
|
|
80
84
|
|
|
81
85
|
4.The log display level is available
|
|
86
|
+
|
|
82
87
|
```
|
|
83
88
|
from task_scheduling.common import set_log_level
|
|
84
89
|
|
|
@@ -87,7 +92,9 @@ set_log_level("DEBUG") # INFO, DEBUG, ERROR, WARNING
|
|
|
87
92
|
if __name__ == "__main__":
|
|
88
93
|
......
|
|
89
94
|
```
|
|
90
|
-
|
|
95
|
+
|
|
96
|
+
5.The task priority is modified to high when necessary, and the task will start executing directly after submission.
|
|
97
|
+
However, this can also cause some running tasks to pause to free up resources to run high-priority tasks
|
|
91
98
|
|
|
92
99
|
### Function: task_creation(delay: int or None, daily_time: str or None, function_type: str, timeout_processing: bool, task_name: str, func: Callable, *args, **kwargs) -> str or None:
|
|
93
100
|
|
|
@@ -353,6 +360,78 @@ if __name__ == "__main__":
|
|
|
353
360
|
|
|
354
361
|
```
|
|
355
362
|
|
|
363
|
+
### pause_and_resume_task(self, task_id: str, action: str) -> bool:
|
|
364
|
+
|
|
365
|
+
A function is used to pause a task and resume running at a specified time.
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
import asyncio
|
|
369
|
+
import time
|
|
370
|
+
|
|
371
|
+
from task_scheduling.utils import interruptible_sleep
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def line_task(input_info):
|
|
375
|
+
for i in range(10):
|
|
376
|
+
interruptible_sleep(1)
|
|
377
|
+
print(input_info)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
async def asyncio_task(input_info):
|
|
381
|
+
for i in range(10):
|
|
382
|
+
await asyncio.sleep(1)
|
|
383
|
+
print(input_info)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
input_info = "test"
|
|
387
|
+
|
|
388
|
+
from task_scheduling.common import set_log_level
|
|
389
|
+
|
|
390
|
+
set_log_level("DEBUG")
|
|
391
|
+
if __name__ == "__main__":
|
|
392
|
+
from task_scheduling.task_creation import task_creation, shutdown
|
|
393
|
+
from task_scheduling.task_info import start_task_status_ui
|
|
394
|
+
from task_scheduling.scheduler import io_liner_task
|
|
395
|
+
|
|
396
|
+
start_task_status_ui()
|
|
397
|
+
from task_scheduling.variable import *
|
|
398
|
+
|
|
399
|
+
task_id1 = task_creation(None,
|
|
400
|
+
# This is how long the delay is executed (in seconds)
|
|
401
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the daily_time is not required
|
|
402
|
+
None,
|
|
403
|
+
# This is to be performed at what point (24-hour clock)
|
|
404
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the delay is not required
|
|
405
|
+
scheduler_io,
|
|
406
|
+
# Running function type, there are "io, cpu, timer"
|
|
407
|
+
True,
|
|
408
|
+
# Set to True to enable timeout detection, tasks that do not finish within the runtime will be forcibly terminated
|
|
409
|
+
"task2",
|
|
410
|
+
# Task ID, in linear tasks, tasks with the same ID will be queued, different IDs will be executed directly, the same applies to asynchronous tasks
|
|
411
|
+
line_task,
|
|
412
|
+
# The function to be executed, parameters should not be passed here
|
|
413
|
+
priority_low,
|
|
414
|
+
# The task priority is modified to high when necessary, and the task will start executing directly after submission
|
|
415
|
+
input_info
|
|
416
|
+
# Pass the parameters required by the function, no restrictions
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# cf478b6e-5e02-49b8-9031-4adc6ff915c2, cf478b6e-5e02-49b8-9031-4adc6ff915c2
|
|
420
|
+
time.sleep(2.0)
|
|
421
|
+
print("Pause")
|
|
422
|
+
io_liner_task.pause_and_resume_task(task_id1, "pause")
|
|
423
|
+
time.sleep(4.0)
|
|
424
|
+
print("Resume")
|
|
425
|
+
io_liner_task.pause_and_resume_task(task_id1, "resume")
|
|
426
|
+
|
|
427
|
+
try:
|
|
428
|
+
while True:
|
|
429
|
+
time.sleep(1.0)
|
|
430
|
+
except KeyboardInterrupt:
|
|
431
|
+
shutdown(True)
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
|
|
356
435
|
### FunctionRunner(self, func: Callable, task_name: str, *args, **kwargs) -> None:
|
|
357
436
|
|
|
358
437
|
A task that detects what type of function is executed. The detected parameters are stored in a file and read directly at
|
|
@@ -795,9 +874,11 @@ if __name__ == "__main__":
|
|
|
795
874
|
### force_stop_task(task_id: str) -> bool:
|
|
796
875
|
|
|
797
876
|
All 4 schedulers have this function for terminating a running task. Warning: If the function is executing a
|
|
798
|
-
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
877
|
+
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
878
|
+
function
|
|
799
879
|
|
|
800
880
|
Warn!!! `cpu_liner_task.force_stop_task()` is different and requires the addition of additional parameters
|
|
881
|
+
|
|
801
882
|
```
|
|
802
883
|
import asyncio
|
|
803
884
|
import time
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Author: fallingmeteorite
|
|
3
3
|
from .process_manager import ProcessTaskManager
|
|
4
|
-
from .
|
|
4
|
+
from .terminate_run import ThreadTerminator, StopException
|
|
5
5
|
|
|
6
6
|
from .thread_manager import ThreadTaskManager
|
|
7
7
|
from .timeout_run import TimeoutException, ThreadingTimeout
|
|
8
8
|
|
|
9
|
-
from .pause_run import
|
|
9
|
+
from .pause_run import ThreadSuspender
|
|
@@ -9,7 +9,7 @@ from typing import Dict
|
|
|
9
9
|
from ..common import logger
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class ThreadSuspender:
|
|
13
13
|
"""Simplified thread controller, fully controlled through context management"""
|
|
14
14
|
|
|
15
15
|
def __init__(self):
|
|
@@ -31,7 +31,7 @@ class ThreadController:
|
|
|
31
31
|
raise NotImplementedError(f"Unsupported platform: {self.platform}")
|
|
32
32
|
|
|
33
33
|
@contextmanager
|
|
34
|
-
def
|
|
34
|
+
def suspend_context(self):
|
|
35
35
|
"""Thread control context manager"""
|
|
36
36
|
current_thread = threading.current_thread()
|
|
37
37
|
tid = current_thread.ident
|
|
@@ -125,7 +125,7 @@ class ThreadController:
|
|
|
125
125
|
class _ThreadControl:
|
|
126
126
|
"""Thread control interface (for internal use only)"""
|
|
127
127
|
|
|
128
|
-
def __init__(self, controller:
|
|
128
|
+
def __init__(self, controller: ThreadSuspender, tid: int):
|
|
129
129
|
self._controller = controller
|
|
130
130
|
self._tid = tid
|
|
131
131
|
self._paused = False
|
|
@@ -153,4 +153,4 @@ class _ThreadControl:
|
|
|
153
153
|
@property
|
|
154
154
|
def is_paused(self) -> bool:
|
|
155
155
|
"""Check if thread is paused"""
|
|
156
|
-
return self._paused
|
|
156
|
+
return self._paused
|
|
@@ -23,10 +23,10 @@ class ProcessTaskManager:
|
|
|
23
23
|
self._start: bool = True
|
|
24
24
|
self._start_monitor_thread() # Start the monitor thread
|
|
25
25
|
|
|
26
|
-
def add(self,
|
|
26
|
+
def add(self, terminate_obj: Any, pause_ctx: Any, task_id: str) -> None:
|
|
27
27
|
"""
|
|
28
28
|
Add task control objects to the dictionary.
|
|
29
|
-
:param
|
|
29
|
+
:param terminate_obj: An object that has a terminate method.
|
|
30
30
|
:param pause_ctx: An object that has a pause method.
|
|
31
31
|
:param task_id: Task ID, used as the key in the dictionary.
|
|
32
32
|
"""
|
|
@@ -34,7 +34,7 @@ class ProcessTaskManager:
|
|
|
34
34
|
if task_id in self._tasks:
|
|
35
35
|
logger.warning(f"Task with task_id '{task_id}' already exists, overwriting")
|
|
36
36
|
self._tasks[task_id] = {
|
|
37
|
-
'
|
|
37
|
+
'terminate': terminate_obj,
|
|
38
38
|
'pause': pause_ctx
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -59,17 +59,17 @@ class ProcessTaskManager:
|
|
|
59
59
|
with self._operation_lock: # Lock for thread-safe dictionary access
|
|
60
60
|
return task_id in self._tasks
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def terminate_task(self, task_id: str) -> None:
|
|
63
63
|
"""
|
|
64
|
-
|
|
64
|
+
Terminate the task based on task_id.
|
|
65
65
|
:param task_id: Task ID.
|
|
66
66
|
"""
|
|
67
67
|
with self._operation_lock: # Lock for thread-safe dictionary access
|
|
68
68
|
if task_id in self._tasks:
|
|
69
69
|
try:
|
|
70
|
-
self._tasks[task_id]['
|
|
70
|
+
self._tasks[task_id]['terminate'].terminate() # Perform the terminate operation outside the lock
|
|
71
71
|
except Exception as error:
|
|
72
|
-
logger.error(f"Error
|
|
72
|
+
logger.error(f"Error terminating task '{task_id}': {error}")
|
|
73
73
|
|
|
74
74
|
else:
|
|
75
75
|
logger.warning(f"No task found with task_id '{task_id}', operation invalid")
|
|
@@ -118,7 +118,7 @@ class ProcessTaskManager:
|
|
|
118
118
|
|
|
119
119
|
if self.check(task_id): # Check if the task_id exists in the dictionary
|
|
120
120
|
if target == "kill":
|
|
121
|
-
self.
|
|
121
|
+
self.terminate_task(task_id) # Terminate the task if it exists
|
|
122
122
|
if target == "pause":
|
|
123
123
|
self.pause_task(task_id) # Pause the task if it exists
|
|
124
124
|
if target == "resume":
|
|
@@ -130,7 +130,7 @@ class ProcessTaskManager:
|
|
|
130
130
|
break # Stop the loop if tasks dictionary is empty
|
|
131
131
|
|
|
132
132
|
else:
|
|
133
|
-
self._task_queue.put(task_id)
|
|
133
|
+
self._task_queue.put((task_id, target))
|
|
134
134
|
except queue.Empty:
|
|
135
135
|
pass # Ignore empty queue exceptions
|
|
136
136
|
except Exception as error:
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import ctypes
|
|
3
|
+
import platform
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StopException(Exception):
|
|
9
|
+
"""Custom exception for thread termination"""
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ThreadTerminator:
|
|
14
|
+
"""Thread terminator using context manager that raises StopException in target thread"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._handles: Dict[int, int] = {}
|
|
18
|
+
self._lock = threading.Lock()
|
|
19
|
+
self._setup_platform()
|
|
20
|
+
|
|
21
|
+
def _setup_platform(self):
|
|
22
|
+
self.platform = platform.system()
|
|
23
|
+
if self.platform == "Windows":
|
|
24
|
+
self._kernel32 = ctypes.windll.kernel32
|
|
25
|
+
self.THREAD_ACCESS = 0x0001 | 0x0002 # TERMINATE + SUSPEND
|
|
26
|
+
elif self.platform in ("Linux", "Darwin"):
|
|
27
|
+
lib_name = "libc.so.6" if self.platform == "Linux" else "libSystem.dylib"
|
|
28
|
+
self._libc = ctypes.CDLL(lib_name)
|
|
29
|
+
else:
|
|
30
|
+
raise NotImplementedError(f"Unsupported platform: {self.platform}")
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def terminate_control(self):
|
|
34
|
+
"""Context manager for thread termination"""
|
|
35
|
+
tid = threading.current_thread().ident
|
|
36
|
+
if not tid:
|
|
37
|
+
raise RuntimeError("Thread not running")
|
|
38
|
+
|
|
39
|
+
if not self._register_thread(tid):
|
|
40
|
+
raise RuntimeError("Thread registration failed")
|
|
41
|
+
|
|
42
|
+
controller = TerminateController(self, tid)
|
|
43
|
+
try:
|
|
44
|
+
yield controller
|
|
45
|
+
finally:
|
|
46
|
+
self._unregister_thread(tid)
|
|
47
|
+
|
|
48
|
+
def _register_thread(self, tid: int) -> bool:
|
|
49
|
+
"""Register thread with OS"""
|
|
50
|
+
with self._lock:
|
|
51
|
+
if tid in self._handles:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
if self.platform == "Windows":
|
|
56
|
+
handle = self._kernel32.OpenThread(self.THREAD_ACCESS, False, tid)
|
|
57
|
+
if not handle:
|
|
58
|
+
raise ctypes.WinError()
|
|
59
|
+
self._handles[tid] = handle
|
|
60
|
+
else:
|
|
61
|
+
self._handles[tid] = tid
|
|
62
|
+
return True
|
|
63
|
+
except Exception:
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def _unregister_thread(self, tid: int) -> bool:
|
|
67
|
+
"""Unregister thread from OS"""
|
|
68
|
+
with self._lock:
|
|
69
|
+
if tid not in self._handles:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
if self.platform == "Windows":
|
|
74
|
+
self._kernel32.CloseHandle(self._handles[tid])
|
|
75
|
+
del self._handles[tid]
|
|
76
|
+
return True
|
|
77
|
+
except Exception:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def _raise_stop_exception(self, tid: int):
|
|
81
|
+
"""Raise StopException in target thread"""
|
|
82
|
+
if self.platform == "Windows":
|
|
83
|
+
# Use async exception on Windows
|
|
84
|
+
ret = ctypes.pythonapi.PyThreadState_SetAsyncExc(
|
|
85
|
+
ctypes.c_long(tid),
|
|
86
|
+
ctypes.py_object(StopException))
|
|
87
|
+
|
|
88
|
+
if ret == 0:
|
|
89
|
+
raise ValueError("Invalid thread ID")
|
|
90
|
+
elif ret > 1:
|
|
91
|
+
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
|
|
92
|
+
raise SystemError("Async exception failed")
|
|
93
|
+
else:
|
|
94
|
+
# Use pthread_cancel on POSIX
|
|
95
|
+
if self._libc.pthread_cancel(tid) != 0:
|
|
96
|
+
raise RuntimeError("Failed to cancel thread")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TerminateController:
|
|
100
|
+
"""Controller for terminating the current thread"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, terminator: ThreadTerminator, tid: int):
|
|
103
|
+
self._terminator = terminator
|
|
104
|
+
self._tid = tid
|
|
105
|
+
|
|
106
|
+
def terminate(self):
|
|
107
|
+
"""Terminate the current thread by raising StopException in it"""
|
|
108
|
+
self._terminator._raise_stop_exception(self._tid)
|
|
109
|
+
# This line won't be reached as the thread will be terminated
|
|
@@ -12,12 +12,12 @@ class ThreadTaskManager:
|
|
|
12
12
|
self._tasks: Dict[str, Dict[str, Any]] = {}
|
|
13
13
|
self._lock = threading.Lock() # Use a lock to control concurrent operations
|
|
14
14
|
|
|
15
|
-
def add(self, cancel_obj: Any,
|
|
15
|
+
def add(self, cancel_obj: Any, terminate_obj: Any, pause_ctx: Any, task_id: str) -> None:
|
|
16
16
|
"""
|
|
17
17
|
Add task control objects to the dictionary.
|
|
18
18
|
|
|
19
19
|
:param cancel_obj: An object that has a cancel method.
|
|
20
|
-
:param
|
|
20
|
+
:param terminate_obj: An object that has a terminate method.
|
|
21
21
|
:param pause_ctx: An object that has a pause method.
|
|
22
22
|
:param task_id: Task ID, used as the key in the dictionary.
|
|
23
23
|
"""
|
|
@@ -26,7 +26,7 @@ class ThreadTaskManager:
|
|
|
26
26
|
logger.warning(f"Task with task_id '{task_id}' already exists, overwriting")
|
|
27
27
|
self._tasks[task_id] = {
|
|
28
28
|
'cancel': cancel_obj,
|
|
29
|
-
'
|
|
29
|
+
'terminate': terminate_obj,
|
|
30
30
|
'pause': pause_ctx
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -74,28 +74,28 @@ class ThreadTaskManager:
|
|
|
74
74
|
self._tasks.keys()): # Use list(self._tasks.keys()) to avoid errors due to dictionary size changes
|
|
75
75
|
self.cancel_task(task_id)
|
|
76
76
|
|
|
77
|
-
def
|
|
77
|
+
def terminate_task(self, task_id: str) -> None:
|
|
78
78
|
"""
|
|
79
|
-
|
|
79
|
+
Terminate the task based on task_id.
|
|
80
80
|
|
|
81
81
|
:param task_id: Task ID.
|
|
82
82
|
"""
|
|
83
83
|
with self._lock: # Acquire the lock to ensure thread-safe operations
|
|
84
84
|
if task_id in self._tasks:
|
|
85
85
|
try:
|
|
86
|
-
self._tasks[task_id]['
|
|
86
|
+
self._tasks[task_id]['terminate'].terminate()
|
|
87
87
|
except Exception as error:
|
|
88
88
|
logger.error(error)
|
|
89
89
|
else:
|
|
90
90
|
logger.warning(f"No task found with task_id '{task_id}', operation invalid")
|
|
91
91
|
|
|
92
|
-
def
|
|
92
|
+
def terminate_all_tasks(self) -> None:
|
|
93
93
|
"""
|
|
94
|
-
|
|
94
|
+
Terminate all tasks in the dictionary.
|
|
95
95
|
"""
|
|
96
96
|
for task_id in list(
|
|
97
97
|
self._tasks.keys()): # Use list(self._tasks.keys()) to avoid errors due to dictionary size changes
|
|
98
|
-
self.
|
|
98
|
+
self.terminate_task(task_id)
|
|
99
99
|
|
|
100
100
|
def pause_task(self, task_id: str) -> None:
|
|
101
101
|
"""
|
{task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler/cpu_asyncio_task.py
RENAMED
|
@@ -12,9 +12,11 @@ from typing import Callable, Dict, Tuple, Optional, Any
|
|
|
12
12
|
from ..common import logger
|
|
13
13
|
from ..config import config
|
|
14
14
|
from ..manager import task_status_manager
|
|
15
|
-
from ..control import ProcessTaskManager,
|
|
15
|
+
from ..control import ProcessTaskManager, ThreadTerminator, StopException
|
|
16
16
|
from ..utils import worker_initializer_asyncio
|
|
17
17
|
|
|
18
|
+
_threadterminator = ThreadTerminator()
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
async def _execute_task_async(task: Tuple[bool, str, str, Callable, Tuple, Dict],
|
|
20
22
|
task_status_queue: queue.Queue, task_manager: Any) -> Any:
|
|
@@ -36,8 +38,8 @@ async def _execute_task_async(task: Tuple[bool, str, str, Callable, Tuple, Dict]
|
|
|
36
38
|
"""
|
|
37
39
|
timeout_processing, task_name, task_id, func, args, kwargs = task
|
|
38
40
|
|
|
39
|
-
with
|
|
40
|
-
task_manager.add(
|
|
41
|
+
with _threadterminator.terminate_control() as terminate_ctx:
|
|
42
|
+
task_manager.add(terminate_ctx, None, task_id)
|
|
41
43
|
|
|
42
44
|
task_status_queue.put(("running", task_id, None, time.time(), None, None, None))
|
|
43
45
|
logger.debug(f"Start running cpu asyncio task, task ID: {task_id}")
|
|
@@ -12,13 +12,14 @@ from typing import Callable, Dict, Tuple, Optional, Any
|
|
|
12
12
|
from ..common import logger
|
|
13
13
|
from ..config import config
|
|
14
14
|
from ..manager import task_status_manager
|
|
15
|
-
from ..control import
|
|
16
|
-
|
|
15
|
+
from ..control import ThreadTerminator, ProcessTaskManager, StopException, ThreadingTimeout, TimeoutException, \
|
|
16
|
+
ThreadSuspender
|
|
17
17
|
from ..utils import worker_initializer_liner
|
|
18
18
|
from ..scheduler_tools import TaskCounter
|
|
19
19
|
|
|
20
20
|
_task_counter = TaskCounter("cpu_liner_task")
|
|
21
|
-
|
|
21
|
+
_threadsuspender = ThreadSuspender()
|
|
22
|
+
_threadterminator = ThreadTerminator()
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict],
|
|
@@ -47,9 +48,9 @@ def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict],
|
|
|
47
48
|
return_results = None
|
|
48
49
|
|
|
49
50
|
try:
|
|
50
|
-
with
|
|
51
|
-
with
|
|
52
|
-
task_manager.add(
|
|
51
|
+
with _threadterminator.terminate_control() as terminate_ctx:
|
|
52
|
+
with _threadsuspender.suspend_context() as pause_ctx:
|
|
53
|
+
task_manager.add(terminate_ctx, pause_ctx, task_id)
|
|
53
54
|
|
|
54
55
|
task_status_queue.put(("running", task_id, None, time.time(), None, None, None))
|
|
55
56
|
logger.debug(f"Start running cpu linear task, task ID: {task_id}")
|
|
@@ -386,7 +387,7 @@ class CpuLinerTask:
|
|
|
386
387
|
if not future.running():
|
|
387
388
|
future.cancel()
|
|
388
389
|
else:
|
|
389
|
-
self._task_signal_transmission.put(task_id, "kill")
|
|
390
|
+
self._task_signal_transmission.put((task_id, "kill"))
|
|
390
391
|
|
|
391
392
|
self._task_status_queue.put(("cancelled", task_id, None, None, None, None, None))
|
|
392
393
|
with self._lock:
|
|
@@ -410,10 +411,10 @@ class CpuLinerTask:
|
|
|
410
411
|
|
|
411
412
|
else:
|
|
412
413
|
if action == "pause":
|
|
413
|
-
self._task_signal_transmission.put(task_id, "pause")
|
|
414
|
+
self._task_signal_transmission.put((task_id, "pause"))
|
|
414
415
|
self._task_status_queue.put(("waiting", task_id, None, None, None, None, None))
|
|
415
416
|
elif action == "resume":
|
|
416
|
-
self._task_signal_transmission.put(task_id, "resume")
|
|
417
|
+
self._task_signal_transmission.put((task_id, "resume"))
|
|
417
418
|
self._task_status_queue.put(("running", task_id, None, None, None, None, None))
|
|
418
419
|
|
|
419
420
|
return True
|
|
@@ -11,14 +11,15 @@ from typing import Callable, Dict, Tuple, Optional, Any
|
|
|
11
11
|
from ..common import logger
|
|
12
12
|
from ..config import config
|
|
13
13
|
from ..manager import task_status_manager
|
|
14
|
-
from ..control import ThreadTaskManager,
|
|
15
|
-
|
|
14
|
+
from ..control import ThreadTaskManager, ThreadTerminator, StopException, ThreadingTimeout, TimeoutException, \
|
|
15
|
+
ThreadSuspender
|
|
16
16
|
from ..scheduler_tools import TaskCounter
|
|
17
17
|
|
|
18
18
|
# Create Manager instance
|
|
19
19
|
_task_manager = ThreadTaskManager()
|
|
20
20
|
_task_counter = TaskCounter("io_liner_task")
|
|
21
|
-
|
|
21
|
+
_threadsuspender = ThreadSuspender()
|
|
22
|
+
_threadterminator = ThreadTerminator()
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict]) -> Any:
|
|
@@ -41,13 +42,14 @@ def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict]) -> Any:
|
|
|
41
42
|
|
|
42
43
|
return_results = None
|
|
43
44
|
try:
|
|
44
|
-
with
|
|
45
|
-
with
|
|
45
|
+
with _threadterminator.terminate_control() as terminate_ctx:
|
|
46
|
+
with _threadsuspender.suspend_context() as pause_ctx:
|
|
46
47
|
|
|
47
|
-
_task_manager.add(None,
|
|
48
|
+
_task_manager.add(None, terminate_ctx, pause_ctx, task_id)
|
|
48
49
|
|
|
49
50
|
task_status_manager.add_task_status(task_id, None, "running", time.time(), None, None, None, None)
|
|
50
51
|
logger.debug(f"Start running io linear task, task ID: {task_id}")
|
|
52
|
+
|
|
51
53
|
if timeout_processing:
|
|
52
54
|
with ThreadingTimeout(seconds=config["watch_dog_time"], swallow_exc=False):
|
|
53
55
|
return_results = func(*args, **kwargs)
|
|
@@ -196,7 +198,7 @@ class IoLinerTask:
|
|
|
196
198
|
if force_cleanup:
|
|
197
199
|
logger.debug("Force stopping scheduler and cleaning up tasks")
|
|
198
200
|
# Force stop all running tasks
|
|
199
|
-
_task_manager.
|
|
201
|
+
_task_manager.terminate_all_tasks()
|
|
200
202
|
self._scheduler_stop_event.set()
|
|
201
203
|
else:
|
|
202
204
|
self._scheduler_stop_event.set()
|
|
@@ -334,7 +336,7 @@ class IoLinerTask:
|
|
|
334
336
|
if not future.running():
|
|
335
337
|
future.cancel()
|
|
336
338
|
else:
|
|
337
|
-
_task_manager.
|
|
339
|
+
_task_manager.terminate_task(task_id)
|
|
338
340
|
|
|
339
341
|
task_status_manager.add_task_status(task_id, None, "cancelled", None, None, None, None, None)
|
|
340
342
|
with self._lock:
|
|
@@ -11,10 +11,11 @@ from typing import Callable, Dict, List, Tuple, Optional, Any
|
|
|
11
11
|
from ..common import logger
|
|
12
12
|
from ..config import config
|
|
13
13
|
from ..manager import task_status_manager
|
|
14
|
-
from ..control import ThreadTaskManager,
|
|
14
|
+
from ..control import ThreadTaskManager, ThreadTerminator, StopException, ThreadingTimeout, TimeoutException
|
|
15
15
|
|
|
16
16
|
# Create Manager instance
|
|
17
17
|
_task_manager = ThreadTaskManager()
|
|
18
|
+
_threadterminator = ThreadTerminator()
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict]) -> Any:
|
|
@@ -37,20 +38,22 @@ def _execute_task(task: Tuple[bool, str, str, Callable, Tuple, Dict]) -> Any:
|
|
|
37
38
|
|
|
38
39
|
return_results = None
|
|
39
40
|
try:
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
with _threadterminator.terminate_control() as terminate_ctx:
|
|
42
|
+
_task_manager.add(None, terminate_ctx, None, task_id)
|
|
43
|
+
task_status_manager.add_task_status(task_id, None, "running", time.time(), None, None,
|
|
44
|
+
None, None)
|
|
45
|
+
|
|
46
|
+
logger.debug(f"Start running timer task, task ID: {task_id}")
|
|
47
|
+
|
|
48
|
+
if timeout_processing:
|
|
49
|
+
with ThreadingTimeout(seconds=config["watch_dog_time"], swallow_exc=False):
|
|
42
50
|
|
|
43
|
-
logger.debug(f"Start running timer task, task ID: {task_id}")
|
|
44
|
-
if timeout_processing:
|
|
45
|
-
with ThreadingTimeout(seconds=config["watch_dog_time"], swallow_exc=False):
|
|
46
|
-
with skip_on_demand() as skip_ctx:
|
|
47
|
-
_task_manager.add(None, skip_ctx, None, task_id)
|
|
48
51
|
return_results = func(*args, **kwargs)
|
|
49
|
-
|
|
50
|
-
with skip_on_demand() as skip_ctx:
|
|
51
|
-
_task_manager.add(None, skip_ctx, task_id)
|
|
52
|
+
else:
|
|
52
53
|
return_results = func(*args, **kwargs)
|
|
53
|
-
|
|
54
|
+
|
|
55
|
+
_task_manager.remove(task_id)
|
|
56
|
+
|
|
54
57
|
except TimeoutException:
|
|
55
58
|
logger.debug(f"Timer task | {task_id} | timed out, forced termination")
|
|
56
59
|
task_status_manager.add_task_status(task_id, None, "timeout", None, None, None,
|
|
@@ -200,7 +203,7 @@ class TimerTask:
|
|
|
200
203
|
if force_cleanup:
|
|
201
204
|
logger.debug("Force stopping scheduler and cleaning up tasks")
|
|
202
205
|
# Force stop all running tasks
|
|
203
|
-
_task_manager.
|
|
206
|
+
_task_manager.terminate_all_tasks()
|
|
204
207
|
self._scheduler_stop_event.set()
|
|
205
208
|
else:
|
|
206
209
|
self._scheduler_stop_event.set()
|
|
@@ -358,7 +361,7 @@ class TimerTask:
|
|
|
358
361
|
if not future.running():
|
|
359
362
|
future.cancel()
|
|
360
363
|
else:
|
|
361
|
-
_task_manager.
|
|
364
|
+
_task_manager.terminate_task(task_id)
|
|
362
365
|
|
|
363
366
|
task_status_manager.add_task_status(task_id, None, "cancelled", None, None, None, None, None)
|
|
364
367
|
with self._lock:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: task_scheduling
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: It is mainly used for task scheduling
|
|
5
5
|
Home-page: https://github.com/fallingmeteorite/task_scheduling
|
|
6
6
|
Author: fallingmeteorite
|
|
@@ -81,7 +81,9 @@ pip install --upgrade task_scheduling
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
# Interface UI
|
|
84
|
+
|
|
84
85
|
Use the function in code
|
|
86
|
+
|
|
85
87
|
```
|
|
86
88
|
|
|
87
89
|
from task_scheduling.task_info import start_task_status_ui
|
|
@@ -95,13 +97,16 @@ Task status UI available at http://localhost:8000
|
|
|
95
97
|
# Function introduction
|
|
96
98
|
|
|
97
99
|
## Tips
|
|
100
|
+
|
|
98
101
|
1.Detailed information requires changing the log level to `debug`.
|
|
99
102
|
|
|
100
|
-
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
103
|
+
2.This scheduler does not support terminating blocking tasks due to various factors, so it only supports terminating
|
|
104
|
+
tasks by injecting exceptions.
|
|
101
105
|
|
|
102
106
|
3.Most of the API calls for this task scheduling can be added to the scheduler for execution as tasks.
|
|
103
107
|
|
|
104
108
|
4.The log display level is available
|
|
109
|
+
|
|
105
110
|
```
|
|
106
111
|
from task_scheduling.common import set_log_level
|
|
107
112
|
|
|
@@ -110,7 +115,9 @@ set_log_level("DEBUG") # INFO, DEBUG, ERROR, WARNING
|
|
|
110
115
|
if __name__ == "__main__":
|
|
111
116
|
......
|
|
112
117
|
```
|
|
113
|
-
|
|
118
|
+
|
|
119
|
+
5.The task priority is modified to high when necessary, and the task will start executing directly after submission.
|
|
120
|
+
However, this can also cause some running tasks to pause to free up resources to run high-priority tasks
|
|
114
121
|
|
|
115
122
|
### Function: task_creation(delay: int or None, daily_time: str or None, function_type: str, timeout_processing: bool, task_name: str, func: Callable, *args, **kwargs) -> str or None:
|
|
116
123
|
|
|
@@ -376,6 +383,78 @@ if __name__ == "__main__":
|
|
|
376
383
|
|
|
377
384
|
```
|
|
378
385
|
|
|
386
|
+
### pause_and_resume_task(self, task_id: str, action: str) -> bool:
|
|
387
|
+
|
|
388
|
+
A function is used to pause a task and resume running at a specified time.
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
import asyncio
|
|
392
|
+
import time
|
|
393
|
+
|
|
394
|
+
from task_scheduling.utils import interruptible_sleep
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def line_task(input_info):
|
|
398
|
+
for i in range(10):
|
|
399
|
+
interruptible_sleep(1)
|
|
400
|
+
print(input_info)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def asyncio_task(input_info):
|
|
404
|
+
for i in range(10):
|
|
405
|
+
await asyncio.sleep(1)
|
|
406
|
+
print(input_info)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
input_info = "test"
|
|
410
|
+
|
|
411
|
+
from task_scheduling.common import set_log_level
|
|
412
|
+
|
|
413
|
+
set_log_level("DEBUG")
|
|
414
|
+
if __name__ == "__main__":
|
|
415
|
+
from task_scheduling.task_creation import task_creation, shutdown
|
|
416
|
+
from task_scheduling.task_info import start_task_status_ui
|
|
417
|
+
from task_scheduling.scheduler import io_liner_task
|
|
418
|
+
|
|
419
|
+
start_task_status_ui()
|
|
420
|
+
from task_scheduling.variable import *
|
|
421
|
+
|
|
422
|
+
task_id1 = task_creation(None,
|
|
423
|
+
# This is how long the delay is executed (in seconds)
|
|
424
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the daily_time is not required
|
|
425
|
+
None,
|
|
426
|
+
# This is to be performed at what point (24-hour clock)
|
|
427
|
+
# This parameter is required when the function_type is "timer",if this parameter is used, the delay is not required
|
|
428
|
+
scheduler_io,
|
|
429
|
+
# Running function type, there are "io, cpu, timer"
|
|
430
|
+
True,
|
|
431
|
+
# Set to True to enable timeout detection, tasks that do not finish within the runtime will be forcibly terminated
|
|
432
|
+
"task2",
|
|
433
|
+
# Task ID, in linear tasks, tasks with the same ID will be queued, different IDs will be executed directly, the same applies to asynchronous tasks
|
|
434
|
+
line_task,
|
|
435
|
+
# The function to be executed, parameters should not be passed here
|
|
436
|
+
priority_low,
|
|
437
|
+
# The task priority is modified to high when necessary, and the task will start executing directly after submission
|
|
438
|
+
input_info
|
|
439
|
+
# Pass the parameters required by the function, no restrictions
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# cf478b6e-5e02-49b8-9031-4adc6ff915c2, cf478b6e-5e02-49b8-9031-4adc6ff915c2
|
|
443
|
+
time.sleep(2.0)
|
|
444
|
+
print("Pause")
|
|
445
|
+
io_liner_task.pause_and_resume_task(task_id1, "pause")
|
|
446
|
+
time.sleep(4.0)
|
|
447
|
+
print("Resume")
|
|
448
|
+
io_liner_task.pause_and_resume_task(task_id1, "resume")
|
|
449
|
+
|
|
450
|
+
try:
|
|
451
|
+
while True:
|
|
452
|
+
time.sleep(1.0)
|
|
453
|
+
except KeyboardInterrupt:
|
|
454
|
+
shutdown(True)
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
|
|
379
458
|
### FunctionRunner(self, func: Callable, task_name: str, *args, **kwargs) -> None:
|
|
380
459
|
|
|
381
460
|
A task that detects what type of function is executed. The detected parameters are stored in a file and read directly at
|
|
@@ -818,9 +897,11 @@ if __name__ == "__main__":
|
|
|
818
897
|
### force_stop_task(task_id: str) -> bool:
|
|
819
898
|
|
|
820
899
|
All 4 schedulers have this function for terminating a running task. Warning: If the function is executing a
|
|
821
|
-
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
900
|
+
process-blocking function such as `time.sleep`, it will have to wait for the execution to finish before terminating the
|
|
901
|
+
function
|
|
822
902
|
|
|
823
903
|
Warn!!! `cpu_liner_task.force_stop_task()` is different and requires the addition of additional parameters
|
|
904
|
+
|
|
824
905
|
```
|
|
825
906
|
import asyncio
|
|
826
907
|
import time
|
|
@@ -20,7 +20,7 @@ task_scheduling/config/config.yaml
|
|
|
20
20
|
task_scheduling/control/__init__.py
|
|
21
21
|
task_scheduling/control/pause_run.py
|
|
22
22
|
task_scheduling/control/process_manager.py
|
|
23
|
-
task_scheduling/control/
|
|
23
|
+
task_scheduling/control/terminate_run.py
|
|
24
24
|
task_scheduling/control/thread_manager.py
|
|
25
25
|
task_scheduling/control/timeout_run.py
|
|
26
26
|
task_scheduling/control/utils.py
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# Author: fallingmeteorite
|
|
3
|
-
|
|
4
|
-
import ctypes
|
|
5
|
-
import threading
|
|
6
|
-
from contextlib import contextmanager
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class StopException(Exception):
|
|
10
|
-
"""Custom timeout exception"""
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _async_raise(target_tid, exception):
|
|
15
|
-
"""Raise an asynchronous exception in the target thread"""
|
|
16
|
-
ret = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(target_tid), ctypes.py_object(exception))
|
|
17
|
-
if ret == 0:
|
|
18
|
-
raise ValueError("Invalid thread ID {}".format(target_tid))
|
|
19
|
-
elif ret > 1:
|
|
20
|
-
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(target_tid), None)
|
|
21
|
-
raise SystemError("PyThreadState_SetAsyncExc failed")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class _SkipContext:
|
|
25
|
-
"""Context object for skipping the current line"""
|
|
26
|
-
|
|
27
|
-
def __init__(self):
|
|
28
|
-
self._target_tid = threading.current_thread().ident
|
|
29
|
-
|
|
30
|
-
def skip(self):
|
|
31
|
-
"""Skip the current line"""
|
|
32
|
-
_async_raise(self._target_tid, StopException)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@contextmanager
|
|
36
|
-
def skip_on_demand():
|
|
37
|
-
"""Context manager that supports manual skipping of the current line"""
|
|
38
|
-
_skip_ctx = _SkipContext()
|
|
39
|
-
yield _skip_ctx # Provide _skip_ctx to external code
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/manager/task_details_queue.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/scheduler_tools/task_scheduler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling/task_info/queue_info_display.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{task_scheduling-2.3.0 → task_scheduling-2.3.2}/task_scheduling.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|