kew 0.1.0__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.
- kew-0.1.0/.gitignore +31 -0
- kew-0.1.0/PKG-INFO +131 -0
- kew-0.1.0/README.md +112 -0
- kew-0.1.0/example.py +36 -0
- kew-0.1.0/kew/__init__.py +5 -0
- kew-0.1.0/kew/exceptions.py +11 -0
- kew-0.1.0/kew/manager.py +150 -0
- kew-0.1.0/kew/models.py +35 -0
- kew-0.1.0/pyproject.toml +35 -0
- kew-0.1.0/setup.py +21 -0
- kew-0.1.0/test_multiple.py +60 -0
kew-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Optional: create a .gitignore (save as kew/.gitignore)
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
.env
|
|
24
|
+
.venv
|
|
25
|
+
env/
|
|
26
|
+
venv/
|
|
27
|
+
ENV/
|
|
28
|
+
env.bak/
|
|
29
|
+
venv.bak/
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
kew-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: kew
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A flexible async task queue manager for Python applications
|
|
5
|
+
Project-URL: Homepage, https://github.com/justrach/kew
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/justrach/kew/issues
|
|
7
|
+
Author-email: Rach Pradhan <rach@rachit.ai>
|
|
8
|
+
Keywords: async,concurrent,manager,queue,task
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: AsyncIO
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.7
|
|
17
|
+
Requires-Dist: typing; python_version < '3.5'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Kew Task Queue Manager
|
|
21
|
+
|
|
22
|
+
A flexible and robust asynchronous task queue manager for Python applications.
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- Asynchronous task execution
|
|
27
|
+
- Configurable worker pool
|
|
28
|
+
- Task status tracking and monitoring
|
|
29
|
+
- Automatic cleanup of completed tasks
|
|
30
|
+
- Thread-safe operations
|
|
31
|
+
- Comprehensive logging
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install kew
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import asyncio
|
|
43
|
+
from kew import TaskQueueManager
|
|
44
|
+
|
|
45
|
+
async def example_task(x: int):
|
|
46
|
+
await asyncio.sleep(1)
|
|
47
|
+
return x * 2
|
|
48
|
+
|
|
49
|
+
async def main():
|
|
50
|
+
# Initialize the task queue manager
|
|
51
|
+
manager = TaskQueueManager(max_workers=2)
|
|
52
|
+
|
|
53
|
+
# Submit a task
|
|
54
|
+
task_info = await manager.submit_task(
|
|
55
|
+
task_id="task1",
|
|
56
|
+
task_type="multiplication",
|
|
57
|
+
task_func=example_task,
|
|
58
|
+
x=5
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Wait for result
|
|
62
|
+
await asyncio.sleep(2)
|
|
63
|
+
status = manager.get_task_status("task1")
|
|
64
|
+
print(f"Result: {status.result}")
|
|
65
|
+
|
|
66
|
+
# Cleanup
|
|
67
|
+
await manager.shutdown()
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
asyncio.run(main())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Advanced Usage
|
|
74
|
+
|
|
75
|
+
### Concurrent Task Execution
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
async def main():
|
|
79
|
+
manager = TaskQueueManager(max_workers=2)
|
|
80
|
+
|
|
81
|
+
# Submit multiple tasks
|
|
82
|
+
tasks = []
|
|
83
|
+
for i in range(5):
|
|
84
|
+
task_info = await manager.submit_task(
|
|
85
|
+
task_id=f"task{i}",
|
|
86
|
+
task_type="example",
|
|
87
|
+
task_func=example_task,
|
|
88
|
+
x=i
|
|
89
|
+
)
|
|
90
|
+
tasks.append(task_info)
|
|
91
|
+
|
|
92
|
+
# Wait for all tasks to complete
|
|
93
|
+
await manager.wait_for_all_tasks()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Task Status Monitoring
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
status = manager.get_task_status("task1")
|
|
100
|
+
print(f"Status: {status.status}")
|
|
101
|
+
print(f"Result: {status.result}")
|
|
102
|
+
print(f"Error: {status.error}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## API Reference
|
|
106
|
+
|
|
107
|
+
### TaskQueueManager
|
|
108
|
+
|
|
109
|
+
- `__init__(max_workers=2, queue_size=1000, task_timeout=3600)`
|
|
110
|
+
- `async submit_task(task_id, task_type, task_func, *args, **kwargs)`
|
|
111
|
+
- `get_task_status(task_id)`
|
|
112
|
+
- `async wait_for_task(task_id, timeout=None)`
|
|
113
|
+
- `async wait_for_all_tasks(timeout=None)`
|
|
114
|
+
- `cleanup_old_tasks(max_age_hours=24)`
|
|
115
|
+
- `async shutdown()`
|
|
116
|
+
|
|
117
|
+
### TaskStatus
|
|
118
|
+
|
|
119
|
+
Enum with states:
|
|
120
|
+
- `QUEUED`
|
|
121
|
+
- `PROCESSING`
|
|
122
|
+
- `COMPLETED`
|
|
123
|
+
- `FAILED`
|
|
124
|
+
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
kew-0.1.0/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Kew Task Queue Manager
|
|
2
|
+
|
|
3
|
+
A flexible and robust asynchronous task queue manager for Python applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Asynchronous task execution
|
|
8
|
+
- Configurable worker pool
|
|
9
|
+
- Task status tracking and monitoring
|
|
10
|
+
- Automatic cleanup of completed tasks
|
|
11
|
+
- Thread-safe operations
|
|
12
|
+
- Comprehensive logging
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kew
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import asyncio
|
|
24
|
+
from kew import TaskQueueManager
|
|
25
|
+
|
|
26
|
+
async def example_task(x: int):
|
|
27
|
+
await asyncio.sleep(1)
|
|
28
|
+
return x * 2
|
|
29
|
+
|
|
30
|
+
async def main():
|
|
31
|
+
# Initialize the task queue manager
|
|
32
|
+
manager = TaskQueueManager(max_workers=2)
|
|
33
|
+
|
|
34
|
+
# Submit a task
|
|
35
|
+
task_info = await manager.submit_task(
|
|
36
|
+
task_id="task1",
|
|
37
|
+
task_type="multiplication",
|
|
38
|
+
task_func=example_task,
|
|
39
|
+
x=5
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Wait for result
|
|
43
|
+
await asyncio.sleep(2)
|
|
44
|
+
status = manager.get_task_status("task1")
|
|
45
|
+
print(f"Result: {status.result}")
|
|
46
|
+
|
|
47
|
+
# Cleanup
|
|
48
|
+
await manager.shutdown()
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
asyncio.run(main())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Advanced Usage
|
|
55
|
+
|
|
56
|
+
### Concurrent Task Execution
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
async def main():
|
|
60
|
+
manager = TaskQueueManager(max_workers=2)
|
|
61
|
+
|
|
62
|
+
# Submit multiple tasks
|
|
63
|
+
tasks = []
|
|
64
|
+
for i in range(5):
|
|
65
|
+
task_info = await manager.submit_task(
|
|
66
|
+
task_id=f"task{i}",
|
|
67
|
+
task_type="example",
|
|
68
|
+
task_func=example_task,
|
|
69
|
+
x=i
|
|
70
|
+
)
|
|
71
|
+
tasks.append(task_info)
|
|
72
|
+
|
|
73
|
+
# Wait for all tasks to complete
|
|
74
|
+
await manager.wait_for_all_tasks()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Task Status Monitoring
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
status = manager.get_task_status("task1")
|
|
81
|
+
print(f"Status: {status.status}")
|
|
82
|
+
print(f"Result: {status.result}")
|
|
83
|
+
print(f"Error: {status.error}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API Reference
|
|
87
|
+
|
|
88
|
+
### TaskQueueManager
|
|
89
|
+
|
|
90
|
+
- `__init__(max_workers=2, queue_size=1000, task_timeout=3600)`
|
|
91
|
+
- `async submit_task(task_id, task_type, task_func, *args, **kwargs)`
|
|
92
|
+
- `get_task_status(task_id)`
|
|
93
|
+
- `async wait_for_task(task_id, timeout=None)`
|
|
94
|
+
- `async wait_for_all_tasks(timeout=None)`
|
|
95
|
+
- `cleanup_old_tasks(max_age_hours=24)`
|
|
96
|
+
- `async shutdown()`
|
|
97
|
+
|
|
98
|
+
### TaskStatus
|
|
99
|
+
|
|
100
|
+
Enum with states:
|
|
101
|
+
- `QUEUED`
|
|
102
|
+
- `PROCESSING`
|
|
103
|
+
- `COMPLETED`
|
|
104
|
+
- `FAILED`
|
|
105
|
+
|
|
106
|
+
## Contributing
|
|
107
|
+
|
|
108
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
kew-0.1.0/example.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# example.py
|
|
2
|
+
import asyncio
|
|
3
|
+
from kew import TaskQueueManager
|
|
4
|
+
|
|
5
|
+
async def example_task(x: int):
|
|
6
|
+
await asyncio.sleep(1)
|
|
7
|
+
return x * 2
|
|
8
|
+
|
|
9
|
+
async def main():
|
|
10
|
+
# Initialize the task queue manager
|
|
11
|
+
manager = TaskQueueManager(max_workers=2)
|
|
12
|
+
|
|
13
|
+
# Submit a task
|
|
14
|
+
task_info = await manager.submit_task(
|
|
15
|
+
task_id="task1",
|
|
16
|
+
task_type="multiplication",
|
|
17
|
+
task_func=example_task,
|
|
18
|
+
x=5
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Get task status
|
|
22
|
+
status = manager.get_task_status("task1")
|
|
23
|
+
print(f"Task status: {status.status}")
|
|
24
|
+
|
|
25
|
+
# Wait a bit to see the result
|
|
26
|
+
await asyncio.sleep(2)
|
|
27
|
+
status = manager.get_task_status("task1")
|
|
28
|
+
print(f"Final status: {status.status}")
|
|
29
|
+
print(f"Result: {status.result}")
|
|
30
|
+
|
|
31
|
+
# Cleanup and shutdown - now properly awaited
|
|
32
|
+
manager.cleanup_old_tasks()
|
|
33
|
+
await manager.shutdown()
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class TaskQueueError(Exception):
|
|
2
|
+
"""Base exception for task queue errors"""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class TaskAlreadyExistsError(TaskQueueError):
|
|
6
|
+
"""Raised when attempting to add a task with an ID that already exists"""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class TaskNotFoundError(TaskQueueError):
|
|
10
|
+
"""Raised when attempting to access a non-existent task"""
|
|
11
|
+
pass
|
kew-0.1.0/kew/manager.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
2
|
+
from queue import Queue
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Optional, Dict, Any, Callable
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
import logging
|
|
7
|
+
import asyncio
|
|
8
|
+
from .models import TaskStatus, TaskInfo
|
|
9
|
+
from .exceptions import TaskAlreadyExistsError, TaskNotFoundError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class TaskQueueManager:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
max_workers: int = 2,
|
|
17
|
+
queue_size: int = 1000,
|
|
18
|
+
task_timeout: int = 3600
|
|
19
|
+
):
|
|
20
|
+
self.executor = ThreadPoolExecutor(max_workers=max_workers)
|
|
21
|
+
self.queue = Queue(maxsize=queue_size)
|
|
22
|
+
self.task_timeout = task_timeout
|
|
23
|
+
self.tasks: Dict[str, TaskInfo] = {}
|
|
24
|
+
self._lock = threading.Lock()
|
|
25
|
+
self._shutdown = False
|
|
26
|
+
self._setup_logging()
|
|
27
|
+
|
|
28
|
+
def _setup_logging(self):
|
|
29
|
+
handler = logging.StreamHandler()
|
|
30
|
+
formatter = logging.Formatter(
|
|
31
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
32
|
+
)
|
|
33
|
+
handler.setFormatter(formatter)
|
|
34
|
+
logger.addHandler(handler)
|
|
35
|
+
logger.setLevel(logging.INFO)
|
|
36
|
+
|
|
37
|
+
async def submit_task(
|
|
38
|
+
self,
|
|
39
|
+
task_id: str,
|
|
40
|
+
task_type: str,
|
|
41
|
+
task_func: Callable,
|
|
42
|
+
*args,
|
|
43
|
+
**kwargs
|
|
44
|
+
) -> TaskInfo:
|
|
45
|
+
with self._lock:
|
|
46
|
+
if task_id in self.tasks:
|
|
47
|
+
raise TaskAlreadyExistsError(
|
|
48
|
+
f"Task with ID {task_id} already exists"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
task_info = TaskInfo(task_id, task_type)
|
|
52
|
+
self.tasks[task_id] = task_info
|
|
53
|
+
logger.info(f"Task {task_id} ({task_type}) submitted")
|
|
54
|
+
|
|
55
|
+
# Create task coroutine
|
|
56
|
+
task = asyncio.create_task(self._execute_task(
|
|
57
|
+
task_id,
|
|
58
|
+
task_func,
|
|
59
|
+
*args,
|
|
60
|
+
**kwargs
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
# Store the task for later cleanup
|
|
64
|
+
task_info._task = task
|
|
65
|
+
|
|
66
|
+
return task_info
|
|
67
|
+
|
|
68
|
+
async def _execute_task(
|
|
69
|
+
self,
|
|
70
|
+
task_id: str,
|
|
71
|
+
task_func: Callable,
|
|
72
|
+
*args,
|
|
73
|
+
**kwargs
|
|
74
|
+
):
|
|
75
|
+
try:
|
|
76
|
+
with self._lock:
|
|
77
|
+
task_info = self.tasks[task_id]
|
|
78
|
+
task_info.status = TaskStatus.PROCESSING
|
|
79
|
+
task_info.started_time = datetime.now()
|
|
80
|
+
logger.info(f"Starting execution of task {task_id}")
|
|
81
|
+
|
|
82
|
+
# Execute the task function
|
|
83
|
+
result = await task_func(*args, **kwargs)
|
|
84
|
+
|
|
85
|
+
with self._lock:
|
|
86
|
+
task_info.result = result
|
|
87
|
+
task_info.status = TaskStatus.COMPLETED
|
|
88
|
+
task_info.completed_time = datetime.now()
|
|
89
|
+
logger.info(f"Task {task_id} completed successfully with result: {result}")
|
|
90
|
+
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.error(f"Error executing task {task_id}: {str(e)}")
|
|
95
|
+
with self._lock:
|
|
96
|
+
task_info = self.tasks[task_id]
|
|
97
|
+
task_info.status = TaskStatus.FAILED
|
|
98
|
+
task_info.error = str(e)
|
|
99
|
+
task_info.completed_time = datetime.now()
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
async def wait_for_task(self, task_id: str, timeout: Optional[float] = None) -> TaskInfo:
|
|
103
|
+
"""Wait for a specific task to complete"""
|
|
104
|
+
task_info = self.get_task_status(task_id)
|
|
105
|
+
if hasattr(task_info, '_task'):
|
|
106
|
+
try:
|
|
107
|
+
await asyncio.wait_for(task_info._task, timeout=timeout)
|
|
108
|
+
except asyncio.TimeoutError:
|
|
109
|
+
logger.warning(f"Task {task_id} timed out after {timeout} seconds")
|
|
110
|
+
raise
|
|
111
|
+
return task_info
|
|
112
|
+
|
|
113
|
+
async def wait_for_all_tasks(self, timeout: Optional[float] = None):
|
|
114
|
+
"""Wait for all tasks to complete"""
|
|
115
|
+
tasks = [
|
|
116
|
+
task_info._task for task_info in self.tasks.values()
|
|
117
|
+
if hasattr(task_info, '_task') and not task_info._task.done()
|
|
118
|
+
]
|
|
119
|
+
if tasks:
|
|
120
|
+
await asyncio.wait(tasks, timeout=timeout)
|
|
121
|
+
|
|
122
|
+
def get_task_status(self, task_id: str) -> TaskInfo:
|
|
123
|
+
with self._lock:
|
|
124
|
+
task_info = self.tasks.get(task_id)
|
|
125
|
+
if not task_info:
|
|
126
|
+
raise TaskNotFoundError(f"Task {task_id} not found")
|
|
127
|
+
return task_info
|
|
128
|
+
|
|
129
|
+
def get_queue_length(self) -> int:
|
|
130
|
+
return self.queue.qsize()
|
|
131
|
+
|
|
132
|
+
def cleanup_old_tasks(self, max_age_hours: int = 24):
|
|
133
|
+
current_time = datetime.now()
|
|
134
|
+
cleaned_count = 0
|
|
135
|
+
with self._lock:
|
|
136
|
+
for task_id, task_info in list(self.tasks.items()):
|
|
137
|
+
if task_info.completed_time:
|
|
138
|
+
age = current_time - task_info.completed_time
|
|
139
|
+
if age.total_seconds() > max_age_hours * 3600:
|
|
140
|
+
del self.tasks[task_id]
|
|
141
|
+
cleaned_count += 1
|
|
142
|
+
|
|
143
|
+
logger.info(f"Cleaned up {cleaned_count} old tasks")
|
|
144
|
+
|
|
145
|
+
async def shutdown(self):
|
|
146
|
+
logger.info("Shutting down TaskQueueManager")
|
|
147
|
+
self._shutdown = True
|
|
148
|
+
# Wait for pending tasks
|
|
149
|
+
await self.wait_for_all_tasks(timeout=5.0)
|
|
150
|
+
self.executor.shutdown(wait=True)
|
kew-0.1.0/kew/models.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Optional, TypeVar, Generic
|
|
4
|
+
|
|
5
|
+
T = TypeVar('T') # Generic type for task result
|
|
6
|
+
|
|
7
|
+
class TaskStatus(Enum):
|
|
8
|
+
QUEUED = "queued"
|
|
9
|
+
PROCESSING = "processing"
|
|
10
|
+
COMPLETED = "completed"
|
|
11
|
+
FAILED = "failed"
|
|
12
|
+
|
|
13
|
+
class TaskInfo(Generic[T]):
|
|
14
|
+
def __init__(self, task_id: str, task_type: str):
|
|
15
|
+
self.task_id = task_id
|
|
16
|
+
self.task_type = task_type
|
|
17
|
+
self.status = TaskStatus.QUEUED
|
|
18
|
+
self.queued_time = datetime.now()
|
|
19
|
+
self.started_time: Optional[datetime] = None
|
|
20
|
+
self.completed_time: Optional[datetime] = None
|
|
21
|
+
self.result: Optional[T] = None
|
|
22
|
+
self.error: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
def to_dict(self):
|
|
25
|
+
"""Convert TaskInfo to dictionary representation"""
|
|
26
|
+
return {
|
|
27
|
+
"task_id": self.task_id,
|
|
28
|
+
"task_type": self.task_type,
|
|
29
|
+
"status": self.status.value,
|
|
30
|
+
"queued_time": self.queued_time.isoformat(),
|
|
31
|
+
"started_time": self.started_time.isoformat() if self.started_time else None,
|
|
32
|
+
"completed_time": self.completed_time.isoformat() if self.completed_time else None,
|
|
33
|
+
"result": self.result,
|
|
34
|
+
"error": self.error
|
|
35
|
+
}
|
kew-0.1.0/pyproject.toml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# First, let's create pyproject.toml (save as kew/pyproject.toml)
|
|
2
|
+
|
|
3
|
+
[build-system]
|
|
4
|
+
requires = ["hatchling"]
|
|
5
|
+
build-backend = "hatchling.build"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "kew"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name="Rach Pradhan", email="rach@rachit.ai" },
|
|
12
|
+
]
|
|
13
|
+
description = "A flexible async task queue manager for Python applications"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
requires-python = ">=3.7"
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Framework :: AsyncIO",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
keywords = ["async", "queue", "task", "manager", "concurrent"]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"typing;python_version<'3.5'",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
"Homepage" = "https://github.com/justrach/kew"
|
|
32
|
+
"Bug Tracker" = "https://github.com/justrach/kew/issues"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["kew"]
|
kew-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="kew",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
install_requires=[
|
|
8
|
+
"typing;python_version<'3.5'",
|
|
9
|
+
],
|
|
10
|
+
author="Your Name",
|
|
11
|
+
author_email="your.email@example.com",
|
|
12
|
+
description="A flexible async task queue manager for Python applications",
|
|
13
|
+
long_description=open("README.md").read(),
|
|
14
|
+
long_description_content_type="text/markdown",
|
|
15
|
+
classifiers=[
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
],
|
|
20
|
+
python_requires=">=3.7",
|
|
21
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# test_multiple.py
|
|
2
|
+
import asyncio
|
|
3
|
+
from kew import TaskQueueManager, TaskStatus
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
async def long_task(task_num: int, sleep_time: int) -> dict:
|
|
7
|
+
"""Simulate a long-running task"""
|
|
8
|
+
print(f"Starting task {task_num} (will take {sleep_time} seconds)")
|
|
9
|
+
await asyncio.sleep(sleep_time)
|
|
10
|
+
result = sleep_time * 2
|
|
11
|
+
print(f"Task {task_num} completed with result: {result}")
|
|
12
|
+
return {"task_num": task_num, "result": result}
|
|
13
|
+
|
|
14
|
+
async def main():
|
|
15
|
+
# Initialize manager with 2 workers (so only 2 tasks can run simultaneously)
|
|
16
|
+
manager = TaskQueueManager(max_workers=2)
|
|
17
|
+
|
|
18
|
+
# Submit 5 tasks with different durations
|
|
19
|
+
tasks = []
|
|
20
|
+
for i in range(5):
|
|
21
|
+
sleep_time = random.randint(3, 7) # Random duration between 3-7 seconds
|
|
22
|
+
task_info = await manager.submit_task(
|
|
23
|
+
task_id=f"task{i+1}",
|
|
24
|
+
task_type="long_calculation",
|
|
25
|
+
task_func=long_task,
|
|
26
|
+
task_num=i+1,
|
|
27
|
+
sleep_time=sleep_time
|
|
28
|
+
)
|
|
29
|
+
tasks.append(task_info)
|
|
30
|
+
print(f"Submitted task {i+1}")
|
|
31
|
+
|
|
32
|
+
# Monitor task progress
|
|
33
|
+
while True:
|
|
34
|
+
all_completed = True
|
|
35
|
+
print("\nCurrent status:")
|
|
36
|
+
for task in tasks:
|
|
37
|
+
status = manager.get_task_status(task.task_id)
|
|
38
|
+
print(f"{task.task_id}: {status.status.value} - Result: {status.result}")
|
|
39
|
+
if status.status not in (TaskStatus.COMPLETED, TaskStatus.FAILED):
|
|
40
|
+
all_completed = False
|
|
41
|
+
|
|
42
|
+
if all_completed:
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
await asyncio.sleep(1) # Wait a second before checking again
|
|
46
|
+
|
|
47
|
+
# Final results
|
|
48
|
+
print("\nFinal results:")
|
|
49
|
+
for task in tasks:
|
|
50
|
+
status = manager.get_task_status(task.task_id)
|
|
51
|
+
print(f"{task.task_id}: {status.result}")
|
|
52
|
+
|
|
53
|
+
# Properly await shutdown
|
|
54
|
+
await manager.shutdown()
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
try:
|
|
58
|
+
asyncio.run(main())
|
|
59
|
+
except KeyboardInterrupt:
|
|
60
|
+
print("\nShutting down gracefully...")
|