queuerPy 0.6.0__tar.gz → 0.8.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.
- {queuerpy-0.6.0/queuerPy.egg-info → queuerpy-0.8.0}/PKG-INFO +22 -9
- {queuerpy-0.6.0 → queuerpy-0.8.0}/README.md +21 -8
- {queuerpy-0.6.0 → queuerpy-0.8.0}/_version.py +1 -1
- {queuerpy-0.6.0 → queuerpy-0.8.0}/database/db_job.py +35 -11
- {queuerpy-0.6.0 → queuerpy-0.8.0}/pyproject.toml +1 -1
- {queuerpy-0.6.0 → queuerpy-0.8.0/queuerPy.egg-info}/PKG-INFO +22 -9
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_job.py +3 -5
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_task.py +37 -1
- {queuerpy-0.6.0 → queuerpy-0.8.0}/LICENSE +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/MANIFEST.in +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/__init__.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/__init__.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/broadcaster.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/listener.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/retryer.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/runner.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/scheduler.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/core/ticker.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/database/__init__.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/database/db_listener.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/database/db_master.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/database/db_worker.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/__init__.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/database.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/error.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/logging.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/sql.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/helper/task.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/__init__.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/batch_job.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/connection.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/job.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/master.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/options.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/options_on_error.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/task.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/model/worker.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuerPy.egg-info/SOURCES.txt +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuerPy.egg-info/dependency_links.txt +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuerPy.egg-info/requires.txt +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuerPy.egg-info/top_level.txt +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_global.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_listener.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_master.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/queuer_next_interval.py +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/setup.cfg +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/sql/job.sql +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/sql/master.sql +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/sql/notify.sql +0 -0
- {queuerpy-0.6.0 → queuerpy-0.8.0}/sql/worker.sql +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queuerPy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A Python implementation of the queuer system - a job queuing and processing system with PostgreSQL backend
|
|
5
5
|
Author-email: Simon Herrmann <siherrmann@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: Simon Herrmann <siherrmann@users.noreply.github.com>
|
|
@@ -86,6 +86,14 @@ q.add_task(example_task)
|
|
|
86
86
|
q.start()
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
You can also add a task with a decorator so the tasks don't have to be added to some central function:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
@queuer.task(name="optionalName")
|
|
93
|
+
def example_task():
|
|
94
|
+
...
|
|
95
|
+
```
|
|
96
|
+
|
|
89
97
|
That's easy, right? Adding a job is just as easy:
|
|
90
98
|
|
|
91
99
|
```python
|
|
@@ -329,19 +337,24 @@ worker_jobs = queuer.get_jobs_by_worker_rid(worker.rid)
|
|
|
329
337
|
|
|
330
338
|
- **Async/Await Support**: Full asyncio integration with threading fallbacks.
|
|
331
339
|
- **PostgreSQL NOTIFY/LISTEN**: Real-time job notifications without polling overhead.
|
|
332
|
-
- **Batch Job Processing**: Insert job batches efficiently.
|
|
333
|
-
- **
|
|
334
|
-
- **Error
|
|
335
|
-
- **Multiple Workers**: Multiple queuer instances can run across different
|
|
340
|
+
- **Batch Job Processing**: Insert job batches efficiently using PostgreSQL's `COPY FROM` feature.
|
|
341
|
+
- **Panic Recovery**: Automatic recovery for all running jobs in case of unexpected failures.
|
|
342
|
+
- **Error Handling**: Comprehensive error handling by checking last output parameter for errors.
|
|
343
|
+
- **Multiple Workers**: Multiple queuer instances can run across different microservices while maintaining job start order and isolation.
|
|
336
344
|
- **Scheduled Jobs**: Support for scheduled and periodic jobs with custom intervals.
|
|
337
345
|
- **Job Lifecycle Management**: Easy functions to get jobs and workers, track job status.
|
|
338
|
-
- **Event Listeners**: Listen for job updates, completion, and deletion events.
|
|
339
|
-
- **
|
|
340
|
-
- **
|
|
341
|
-
- **
|
|
346
|
+
- **Event Listeners**: Listen for job updates, completion, and deletion events (ended jobs).
|
|
347
|
+
- **Job Completion Helpers**: Helper functions to listen for specific finished jobs.
|
|
348
|
+
- **Retry Mechanisms**: Retry mechanism for ended jobs which creates a new job with the same parameters, with configurable retry logic and different backoff strategies.
|
|
349
|
+
- **Custom Scheduling**: Custom NextInterval functions to address custom needs for scheduling (e.g., scheduling with timezone offset).
|
|
350
|
+
- **Master Worker Management**: Automatic master worker setting retention and other central settings. Automatic switch to new master if old worker stops.
|
|
351
|
+
- **Heartbeat System**: Worker heartbeat monitoring and automatic stale worker detection and cancellation by the master.
|
|
352
|
+
- **Encryption Support**: Encryption support for sensitive job data stored in the database.
|
|
342
353
|
- **Database Integration**: Seamless PostgreSQL integration with automatic schema management.
|
|
343
354
|
- **Type Safety**: Full type hints and dataclass-based models for better development experience.
|
|
344
355
|
|
|
356
|
+
> **Note**: Transactional job insert is the only feature that is not yet implemented in the Python implementation of the queuer.
|
|
357
|
+
|
|
345
358
|
---
|
|
346
359
|
|
|
347
360
|
# 🧪 Testing
|
|
@@ -43,6 +43,14 @@ q.add_task(example_task)
|
|
|
43
43
|
q.start()
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
You can also add a task with a decorator so the tasks don't have to be added to some central function:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
@queuer.task(name="optionalName")
|
|
50
|
+
def example_task():
|
|
51
|
+
...
|
|
52
|
+
```
|
|
53
|
+
|
|
46
54
|
That's easy, right? Adding a job is just as easy:
|
|
47
55
|
|
|
48
56
|
```python
|
|
@@ -286,19 +294,24 @@ worker_jobs = queuer.get_jobs_by_worker_rid(worker.rid)
|
|
|
286
294
|
|
|
287
295
|
- **Async/Await Support**: Full asyncio integration with threading fallbacks.
|
|
288
296
|
- **PostgreSQL NOTIFY/LISTEN**: Real-time job notifications without polling overhead.
|
|
289
|
-
- **Batch Job Processing**: Insert job batches efficiently.
|
|
290
|
-
- **
|
|
291
|
-
- **Error
|
|
292
|
-
- **Multiple Workers**: Multiple queuer instances can run across different
|
|
297
|
+
- **Batch Job Processing**: Insert job batches efficiently using PostgreSQL's `COPY FROM` feature.
|
|
298
|
+
- **Panic Recovery**: Automatic recovery for all running jobs in case of unexpected failures.
|
|
299
|
+
- **Error Handling**: Comprehensive error handling by checking last output parameter for errors.
|
|
300
|
+
- **Multiple Workers**: Multiple queuer instances can run across different microservices while maintaining job start order and isolation.
|
|
293
301
|
- **Scheduled Jobs**: Support for scheduled and periodic jobs with custom intervals.
|
|
294
302
|
- **Job Lifecycle Management**: Easy functions to get jobs and workers, track job status.
|
|
295
|
-
- **Event Listeners**: Listen for job updates, completion, and deletion events.
|
|
296
|
-
- **
|
|
297
|
-
- **
|
|
298
|
-
- **
|
|
303
|
+
- **Event Listeners**: Listen for job updates, completion, and deletion events (ended jobs).
|
|
304
|
+
- **Job Completion Helpers**: Helper functions to listen for specific finished jobs.
|
|
305
|
+
- **Retry Mechanisms**: Retry mechanism for ended jobs which creates a new job with the same parameters, with configurable retry logic and different backoff strategies.
|
|
306
|
+
- **Custom Scheduling**: Custom NextInterval functions to address custom needs for scheduling (e.g., scheduling with timezone offset).
|
|
307
|
+
- **Master Worker Management**: Automatic master worker setting retention and other central settings. Automatic switch to new master if old worker stops.
|
|
308
|
+
- **Heartbeat System**: Worker heartbeat monitoring and automatic stale worker detection and cancellation by the master.
|
|
309
|
+
- **Encryption Support**: Encryption support for sensitive job data stored in the database.
|
|
299
310
|
- **Database Integration**: Seamless PostgreSQL integration with automatic schema management.
|
|
300
311
|
- **Type Safety**: Full type hints and dataclass-based models for better development experience.
|
|
301
312
|
|
|
313
|
+
> **Note**: Transactional job insert is the only feature that is not yet implemented in the Python implementation of the queuer.
|
|
314
|
+
|
|
302
315
|
---
|
|
303
316
|
|
|
304
317
|
# 🧪 Testing
|
|
@@ -3,8 +3,9 @@ Job database handler for Python queuer implementation.
|
|
|
3
3
|
Mirrors Go's database/dbJob.go with psycopg3 and references same SQL functions.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from datetime import datetime
|
|
6
7
|
import json
|
|
7
|
-
from typing import List, Optional
|
|
8
|
+
from typing import List, Optional, Tuple
|
|
8
9
|
from uuid import UUID
|
|
9
10
|
from psycopg.rows import dict_row
|
|
10
11
|
|
|
@@ -127,21 +128,44 @@ class JobDBHandler:
|
|
|
127
128
|
# In full implementation, this would handle explicit transaction context
|
|
128
129
|
return self.insert_job(job)
|
|
129
130
|
|
|
130
|
-
def batch_insert_jobs(self, jobs: List[Job])
|
|
131
|
+
def batch_insert_jobs(self, jobs: List[Job]):
|
|
131
132
|
"""
|
|
132
|
-
Insert multiple jobs in a batch operation.
|
|
133
|
-
|
|
134
|
-
TODO: Implement efficient batch insert logic.
|
|
133
|
+
Insert multiple jobs in a batch operation using executemany.
|
|
134
|
+
Mirrors Go's BatchInsertJobs method with direct INSERT statements.
|
|
135
135
|
|
|
136
136
|
:param jobs: List of Job instances to insert.
|
|
137
137
|
:return: List of inserted Job instances.
|
|
138
|
+
:raises QueuerError: If batch insertion fails.
|
|
138
139
|
"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
if not jobs:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
if self.db.instance is None:
|
|
144
|
+
raise ValueError("Database connection is not established")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
batch_data: List[Tuple[str, str, str, Optional[datetime]]] = []
|
|
148
|
+
for job in jobs:
|
|
149
|
+
options_json = (
|
|
150
|
+
json.dumps(job.options.to_dict()) if job.options else "{}"
|
|
151
|
+
)
|
|
152
|
+
parameters_json = json.dumps(job.parameters)
|
|
153
|
+
batch_data.append(
|
|
154
|
+
(options_json, job.task_name, parameters_json, job.scheduled_at)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
with self.db.instance.cursor(row_factory=dict_row) as cur:
|
|
158
|
+
cur.executemany(
|
|
159
|
+
"""
|
|
160
|
+
INSERT INTO job (options, task_name, parameters, scheduled_at)
|
|
161
|
+
VALUES (%s, %s, %s, %s)
|
|
162
|
+
""",
|
|
163
|
+
batch_data,
|
|
164
|
+
)
|
|
165
|
+
self.db.instance.commit()
|
|
166
|
+
except Exception as e:
|
|
167
|
+
self.db.instance.rollback()
|
|
168
|
+
raise QueuerError("batch insert jobs", e)
|
|
145
169
|
|
|
146
170
|
def update_jobs_initial(self, worker: Worker) -> List[Job]:
|
|
147
171
|
"""
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "queuerPy"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "A Python implementation of the queuer system - a job queuing and processing system with PostgreSQL backend"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queuerPy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A Python implementation of the queuer system - a job queuing and processing system with PostgreSQL backend
|
|
5
5
|
Author-email: Simon Herrmann <siherrmann@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: Simon Herrmann <siherrmann@users.noreply.github.com>
|
|
@@ -86,6 +86,14 @@ q.add_task(example_task)
|
|
|
86
86
|
q.start()
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
You can also add a task with a decorator so the tasks don't have to be added to some central function:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
@queuer.task(name="optionalName")
|
|
93
|
+
def example_task():
|
|
94
|
+
...
|
|
95
|
+
```
|
|
96
|
+
|
|
89
97
|
That's easy, right? Adding a job is just as easy:
|
|
90
98
|
|
|
91
99
|
```python
|
|
@@ -329,19 +337,24 @@ worker_jobs = queuer.get_jobs_by_worker_rid(worker.rid)
|
|
|
329
337
|
|
|
330
338
|
- **Async/Await Support**: Full asyncio integration with threading fallbacks.
|
|
331
339
|
- **PostgreSQL NOTIFY/LISTEN**: Real-time job notifications without polling overhead.
|
|
332
|
-
- **Batch Job Processing**: Insert job batches efficiently.
|
|
333
|
-
- **
|
|
334
|
-
- **Error
|
|
335
|
-
- **Multiple Workers**: Multiple queuer instances can run across different
|
|
340
|
+
- **Batch Job Processing**: Insert job batches efficiently using PostgreSQL's `COPY FROM` feature.
|
|
341
|
+
- **Panic Recovery**: Automatic recovery for all running jobs in case of unexpected failures.
|
|
342
|
+
- **Error Handling**: Comprehensive error handling by checking last output parameter for errors.
|
|
343
|
+
- **Multiple Workers**: Multiple queuer instances can run across different microservices while maintaining job start order and isolation.
|
|
336
344
|
- **Scheduled Jobs**: Support for scheduled and periodic jobs with custom intervals.
|
|
337
345
|
- **Job Lifecycle Management**: Easy functions to get jobs and workers, track job status.
|
|
338
|
-
- **Event Listeners**: Listen for job updates, completion, and deletion events.
|
|
339
|
-
- **
|
|
340
|
-
- **
|
|
341
|
-
- **
|
|
346
|
+
- **Event Listeners**: Listen for job updates, completion, and deletion events (ended jobs).
|
|
347
|
+
- **Job Completion Helpers**: Helper functions to listen for specific finished jobs.
|
|
348
|
+
- **Retry Mechanisms**: Retry mechanism for ended jobs which creates a new job with the same parameters, with configurable retry logic and different backoff strategies.
|
|
349
|
+
- **Custom Scheduling**: Custom NextInterval functions to address custom needs for scheduling (e.g., scheduling with timezone offset).
|
|
350
|
+
- **Master Worker Management**: Automatic master worker setting retention and other central settings. Automatic switch to new master if old worker stops.
|
|
351
|
+
- **Heartbeat System**: Worker heartbeat monitoring and automatic stale worker detection and cancellation by the master.
|
|
352
|
+
- **Encryption Support**: Encryption support for sensitive job data stored in the database.
|
|
342
353
|
- **Database Integration**: Seamless PostgreSQL integration with automatic schema management.
|
|
343
354
|
- **Type Safety**: Full type hints and dataclass-based models for better development experience.
|
|
344
355
|
|
|
356
|
+
> **Note**: Transactional job insert is the only feature that is not yet implemented in the Python implementation of the queuer.
|
|
357
|
+
|
|
345
358
|
---
|
|
346
359
|
|
|
347
360
|
# 🧪 Testing
|
|
@@ -6,7 +6,7 @@ Mirrors Go's queuerJob.go functionality.
|
|
|
6
6
|
import asyncio
|
|
7
7
|
import logging
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from typing import Dict, List, Optional, Any, Tuple, Union, Callable
|
|
9
|
+
from typing import Dict, List, Optional, Any, Tuple, Union, Callable
|
|
10
10
|
from uuid import UUID
|
|
11
11
|
|
|
12
12
|
from core.broadcaster import BroadcasterQueue
|
|
@@ -15,14 +15,12 @@ from core.retryer import Retryer
|
|
|
15
15
|
from core.scheduler import Scheduler
|
|
16
16
|
from helper.error import QueuerError
|
|
17
17
|
from helper.task import get_task_name_from_interface
|
|
18
|
+
from model.batch_job import BatchJob
|
|
18
19
|
from model.job import Job, JobStatus, new_job as create_job
|
|
19
20
|
from model.options import Options
|
|
20
21
|
from model.worker import Worker
|
|
21
22
|
from queuer_global import QueuerGlobalMixin
|
|
22
23
|
|
|
23
|
-
if TYPE_CHECKING:
|
|
24
|
-
from model.batch_job import BatchJob
|
|
25
|
-
|
|
26
24
|
# Set up logger
|
|
27
25
|
logger = logging.getLogger(__name__)
|
|
28
26
|
|
|
@@ -73,7 +71,7 @@ class QueuerJobMixin(QueuerGlobalMixin):
|
|
|
73
71
|
logger.error(f"Error adding job with options: {str(e)}")
|
|
74
72
|
raise Exception(f"Adding job: {str(e)}")
|
|
75
73
|
|
|
76
|
-
def add_jobs(self, batch_jobs: List[
|
|
74
|
+
def add_jobs(self, batch_jobs: List[BatchJob]):
|
|
77
75
|
"""
|
|
78
76
|
Add a batch of jobs to the queue.
|
|
79
77
|
|
|
@@ -4,7 +4,7 @@ Mirrors Go's queuerTask.go functionality.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Any, Callable, Optional
|
|
7
|
+
from typing import Any, Callable, Optional, TypeVar, overload
|
|
8
8
|
|
|
9
9
|
from model.task import new_task, new_task_with_name
|
|
10
10
|
from model.task import Task
|
|
@@ -12,6 +12,9 @@ from queuer_global import QueuerGlobalMixin
|
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
|
+
# TypeVar for maintaining function type through decorator
|
|
16
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
17
|
+
|
|
15
18
|
|
|
16
19
|
class QueuerTaskMixin(QueuerGlobalMixin):
|
|
17
20
|
"""
|
|
@@ -87,3 +90,36 @@ class QueuerTaskMixin(QueuerGlobalMixin):
|
|
|
87
90
|
|
|
88
91
|
logger.info(f"Task added: {new_task_obj.name}")
|
|
89
92
|
return new_task_obj
|
|
93
|
+
|
|
94
|
+
@overload
|
|
95
|
+
def task(self, name: None = None) -> Callable[[F], F]: ...
|
|
96
|
+
|
|
97
|
+
@overload
|
|
98
|
+
def task(self, name: str) -> Callable[[F], F]: ...
|
|
99
|
+
|
|
100
|
+
def task(self, name: Optional[str] = None) -> Callable[[F], F]:
|
|
101
|
+
"""
|
|
102
|
+
Decorator to register a task function with the queuer.
|
|
103
|
+
This is equivalent to calling add_task() or add_task_with_name().
|
|
104
|
+
|
|
105
|
+
Usage:
|
|
106
|
+
@queuer.task()
|
|
107
|
+
def my_task(param1, param2):
|
|
108
|
+
return param1 + param2
|
|
109
|
+
|
|
110
|
+
@queuer.task(name="custom_task_name")
|
|
111
|
+
def my_other_task(param):
|
|
112
|
+
return param * 2
|
|
113
|
+
|
|
114
|
+
:param name: Optional custom name for the task. If not provided, uses function name.
|
|
115
|
+
:return: The decorator function that preserves the original function's type
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def decorator(func: F) -> F:
|
|
119
|
+
if name is not None:
|
|
120
|
+
self.add_task_with_name(func, name)
|
|
121
|
+
else:
|
|
122
|
+
self.add_task(func)
|
|
123
|
+
return func
|
|
124
|
+
|
|
125
|
+
return decorator
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|