faster-cron 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.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: faster-cron
3
+ Version: 0.1.0
4
+ Summary: 一个轻量、直观、支持异步与同步双模式的定时任务调度器
5
+ Author-email: Bernard Simon <bernardziyi@gmail.com>
6
+ Project-URL: Homepage, https://github.com/BernardSimon/faster-cron
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+
13
+ # FasterCron
14
+
15
+ **FasterCron** 是一个轻量级、直观且功能强大的 Python 定时任务调度工具库。它完美支持 **Asyncio (异步)** 和 **Threading (多线程)** 双模式,专为需要高可靠性、简单配置和任务并发控制的场景设计。
16
+
17
+ ---
18
+
19
+ ## 🌟 核心特性
20
+
21
+ * **双模式支持**:一套逻辑同时提供 `AsyncFasterCron`(异步)和 `FasterCron`(同步多线程)两种实现。
22
+ * **任务级并发控制**:通过 `allow_overlap` 参数精准控制同一个任务是否允许重叠执行(单例模式 vs 并发模式)。
23
+ * **智能参数注入**:自动检测任务函数签名,按需注入包含调度时间、任务名称的 `context` 上下文。
24
+ * **标准 Cron 支持**:兼容 5 位(分时日月周)和 6 位(秒分时日月周)Cron 表达式。
25
+ * **健壮性**:内置异常捕获机制,单个任务崩溃不影响调度器运行。
26
+ * **无外部依赖**:仅使用 Python 标准库实现,轻量无负担。
27
+
28
+ ---
29
+
30
+ ## 📦 安装
31
+
32
+ 您可以直接通过 pip 安装:
33
+
34
+ ```bash
35
+ pip install faster-cron
36
+
37
+ ```
38
+
39
+ 或者直接将源码放入您的项目中。
40
+
41
+ ---
42
+
43
+ ## 🚀 快速上手
44
+
45
+ ### 1. 异步模式 (Async Mode)
46
+
47
+ 适用于使用了 `aiohttp`, `httpx` 或 `tortoise-orm` 等异步库的项目。
48
+
49
+ ```python
50
+ import asyncio
51
+ from faster_cron import AsyncFasterCron
52
+
53
+ cron = AsyncFasterCron()
54
+
55
+
56
+ # 示例:每 5 秒执行一次,禁止重叠(若上一个任务没跑完,则跳过本次)
57
+ @cron.schedule("*/5 * * * * *", allow_overlap=False)
58
+ async def my_async_job(context):
59
+ print(f"正在执行任务: {context['task_name']}, 计划时间: {context['scheduled_at']}")
60
+ await asyncio.sleep(6) # 模拟长耗时任务
61
+
62
+
63
+ async def main():
64
+ await cron.start()
65
+
66
+
67
+ if __name__ == "__main__":
68
+ asyncio.run(main())
69
+
70
+ ```
71
+
72
+ ### 2. 同步模式 (Sync Mode)
73
+
74
+ 适用于传统的阻塞式脚本或爬虫。
75
+
76
+ ```python
77
+ from faster_cron import FasterCron
78
+ import time
79
+
80
+ cron = FasterCron()
81
+
82
+
83
+ # 示例:每秒执行一次,允许并发执行
84
+ @cron.schedule("* * * * * *", allow_overlap=True)
85
+ def my_sync_job():
86
+ print("滴答,同步任务正在运行...")
87
+ time.sleep(2)
88
+
89
+
90
+ if __name__ == "__main__":
91
+ cron.run()
92
+
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 🛠 核心 API 说明
98
+
99
+ ### 调度装饰器 `schedule`
100
+
101
+ | 参数 | 类型 | 默认值 | 说明 |
102
+ | --- | --- | --- | --- |
103
+ | `expression` | `str` | - | Cron 表达式。支持 `*`, `,`, `-`, `/`。 |
104
+ | `allow_overlap` | `bool` | `True` | **关键参数**。`True`: 时间点到达即执行;`False`: 若该任务的上一个实例未结束,则跳过本次执行循环。 |
105
+
106
+ ### 上下文参数 `context`
107
+
108
+ 如果您的任务函数接收名为 `context` 的参数,FasterCron 会自动注入以下字典:
109
+
110
+ * `task_name`: 函数名称。
111
+ * `scheduled_at`: 任务触发的精确 `datetime` 对象。
112
+ * `expression`: 该任务使用的 Cron 表达式。
113
+
114
+ ---
115
+
116
+ ## 📅 Cron 表达式参考
117
+
118
+ FasterCron 支持灵活的表达式定义:
119
+
120
+ * `* * * * * *` : 每秒执行。
121
+ * `*/5 * * * * *` : 每 5 秒执行。
122
+ * `0 0 * * * *` : 每整小时执行。
123
+ * `0 30 9-17 * * *` : 每天 9:00 到 17:00 之间的每半小时执行。
124
+ * `0 0 0 * * 0` : 每周日凌晨执行。
125
+
126
+ ---
127
+
128
+ ## 🧪 运行测试
129
+
130
+ 本项目包含完善的单元测试。您可以使用 `pytest` 来验证功能:
131
+
132
+ ```bash
133
+ # 安装测试依赖
134
+ pip install pytest pytest-asyncio
135
+
136
+ # 运行所有测试
137
+ pytest
138
+
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 📄 开源协议
144
+
145
+ MIT License.
146
+
147
+ ---
148
+
149
+ **如果您觉得好用,欢迎点一个 Star!🌟**
@@ -0,0 +1,137 @@
1
+ # FasterCron
2
+
3
+ **FasterCron** 是一个轻量级、直观且功能强大的 Python 定时任务调度工具库。它完美支持 **Asyncio (异步)** 和 **Threading (多线程)** 双模式,专为需要高可靠性、简单配置和任务并发控制的场景设计。
4
+
5
+ ---
6
+
7
+ ## 🌟 核心特性
8
+
9
+ * **双模式支持**:一套逻辑同时提供 `AsyncFasterCron`(异步)和 `FasterCron`(同步多线程)两种实现。
10
+ * **任务级并发控制**:通过 `allow_overlap` 参数精准控制同一个任务是否允许重叠执行(单例模式 vs 并发模式)。
11
+ * **智能参数注入**:自动检测任务函数签名,按需注入包含调度时间、任务名称的 `context` 上下文。
12
+ * **标准 Cron 支持**:兼容 5 位(分时日月周)和 6 位(秒分时日月周)Cron 表达式。
13
+ * **健壮性**:内置异常捕获机制,单个任务崩溃不影响调度器运行。
14
+ * **无外部依赖**:仅使用 Python 标准库实现,轻量无负担。
15
+
16
+ ---
17
+
18
+ ## 📦 安装
19
+
20
+ 您可以直接通过 pip 安装:
21
+
22
+ ```bash
23
+ pip install faster-cron
24
+
25
+ ```
26
+
27
+ 或者直接将源码放入您的项目中。
28
+
29
+ ---
30
+
31
+ ## 🚀 快速上手
32
+
33
+ ### 1. 异步模式 (Async Mode)
34
+
35
+ 适用于使用了 `aiohttp`, `httpx` 或 `tortoise-orm` 等异步库的项目。
36
+
37
+ ```python
38
+ import asyncio
39
+ from faster_cron import AsyncFasterCron
40
+
41
+ cron = AsyncFasterCron()
42
+
43
+
44
+ # 示例:每 5 秒执行一次,禁止重叠(若上一个任务没跑完,则跳过本次)
45
+ @cron.schedule("*/5 * * * * *", allow_overlap=False)
46
+ async def my_async_job(context):
47
+ print(f"正在执行任务: {context['task_name']}, 计划时间: {context['scheduled_at']}")
48
+ await asyncio.sleep(6) # 模拟长耗时任务
49
+
50
+
51
+ async def main():
52
+ await cron.start()
53
+
54
+
55
+ if __name__ == "__main__":
56
+ asyncio.run(main())
57
+
58
+ ```
59
+
60
+ ### 2. 同步模式 (Sync Mode)
61
+
62
+ 适用于传统的阻塞式脚本或爬虫。
63
+
64
+ ```python
65
+ from faster_cron import FasterCron
66
+ import time
67
+
68
+ cron = FasterCron()
69
+
70
+
71
+ # 示例:每秒执行一次,允许并发执行
72
+ @cron.schedule("* * * * * *", allow_overlap=True)
73
+ def my_sync_job():
74
+ print("滴答,同步任务正在运行...")
75
+ time.sleep(2)
76
+
77
+
78
+ if __name__ == "__main__":
79
+ cron.run()
80
+
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 🛠 核心 API 说明
86
+
87
+ ### 调度装饰器 `schedule`
88
+
89
+ | 参数 | 类型 | 默认值 | 说明 |
90
+ | --- | --- | --- | --- |
91
+ | `expression` | `str` | - | Cron 表达式。支持 `*`, `,`, `-`, `/`。 |
92
+ | `allow_overlap` | `bool` | `True` | **关键参数**。`True`: 时间点到达即执行;`False`: 若该任务的上一个实例未结束,则跳过本次执行循环。 |
93
+
94
+ ### 上下文参数 `context`
95
+
96
+ 如果您的任务函数接收名为 `context` 的参数,FasterCron 会自动注入以下字典:
97
+
98
+ * `task_name`: 函数名称。
99
+ * `scheduled_at`: 任务触发的精确 `datetime` 对象。
100
+ * `expression`: 该任务使用的 Cron 表达式。
101
+
102
+ ---
103
+
104
+ ## 📅 Cron 表达式参考
105
+
106
+ FasterCron 支持灵活的表达式定义:
107
+
108
+ * `* * * * * *` : 每秒执行。
109
+ * `*/5 * * * * *` : 每 5 秒执行。
110
+ * `0 0 * * * *` : 每整小时执行。
111
+ * `0 30 9-17 * * *` : 每天 9:00 到 17:00 之间的每半小时执行。
112
+ * `0 0 0 * * 0` : 每周日凌晨执行。
113
+
114
+ ---
115
+
116
+ ## 🧪 运行测试
117
+
118
+ 本项目包含完善的单元测试。您可以使用 `pytest` 来验证功能:
119
+
120
+ ```bash
121
+ # 安装测试依赖
122
+ pip install pytest pytest-asyncio
123
+
124
+ # 运行所有测试
125
+ pytest
126
+
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 📄 开源协议
132
+
133
+ MIT License.
134
+
135
+ ---
136
+
137
+ **如果您觉得好用,欢迎点一个 Star!🌟**
@@ -0,0 +1,9 @@
1
+ """
2
+ FasterCron: 一个轻量、直观、支持异步与同步双模式的定时任务调度器。
3
+ """
4
+
5
+ from .async_cron import AsyncFasterCron
6
+ from .sync_cron import FasterCron
7
+
8
+ __version__ = "0.1.0"
9
+ __all__ = ["AsyncFasterCron", "FasterCron"]
@@ -0,0 +1,59 @@
1
+ import asyncio
2
+ import inspect
3
+ import datetime
4
+ import logging
5
+ from typing import Callable, Optional
6
+ from .base import CronBase
7
+
8
+
9
+ class AsyncFasterCron:
10
+ def __init__(self, log_level=logging.INFO):
11
+ self.tasks = []
12
+ self.logger = logging.getLogger("FasterCron.Async")
13
+ self.logger.setLevel(log_level)
14
+ self._running = False
15
+
16
+ def schedule(self, expression: str, allow_overlap: bool = True):
17
+ def decorator(func: Callable):
18
+ self.tasks.append({
19
+ "expression": expression,
20
+ "func": func,
21
+ "allow_overlap": allow_overlap,
22
+ "name": func.__name__
23
+ })
24
+ return func
25
+
26
+ return decorator
27
+
28
+ async def start(self):
29
+ self._running = True
30
+ listeners = [self._monitor(task) for task in self.tasks]
31
+ await asyncio.gather(*listeners)
32
+
33
+ async def _monitor(self, task):
34
+ last_ts = 0
35
+ current_task: Optional[asyncio.Task] = None
36
+
37
+ while self._running:
38
+ now = datetime.datetime.now()
39
+ ts = int(now.timestamp())
40
+
41
+ if ts != last_ts and CronBase.is_time_match(task["expression"], now):
42
+ last_ts = ts
43
+ if not task["allow_overlap"] and current_task and not current_task.done():
44
+ self.logger.warning(f"Skip {task['name']}: overlapping blocked.")
45
+ continue
46
+
47
+ context = {"scheduled_at": now, "task_name": task["name"]}
48
+ current_task = asyncio.create_task(self._wrapper(task["func"], context))
49
+
50
+ await asyncio.sleep(1.0 - (now.microsecond / 1_000_000) + 0.01)
51
+
52
+ async def _wrapper(self, func, context):
53
+ try:
54
+ sig = inspect.signature(func)
55
+ kwargs = {"context": context} if "context" in sig.parameters or any(
56
+ p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()) else {}
57
+ await func(**kwargs)
58
+ except Exception as e:
59
+ self.logger.error(f"Task {func.__name__} failed: {e}", exc_info=True)
@@ -0,0 +1,42 @@
1
+ import datetime
2
+
3
+
4
+ class CronBase:
5
+ """提供 Cron 表达式解析的底层逻辑"""
6
+
7
+ @staticmethod
8
+ def is_time_match(expression: str, now: datetime.datetime) -> bool:
9
+ parts = expression.split()
10
+ if len(parts) == 5:
11
+ # 分 时 日 月 周 -> 补齐秒为 0
12
+ sec_part, min_part, hour_part, day_part, month_part, weekday_part = "0", *parts
13
+ elif len(parts) == 6:
14
+ sec_part, min_part, hour_part, day_part, month_part, weekday_part = parts
15
+ else:
16
+ return False
17
+
18
+ return (
19
+ CronBase._match_field(sec_part, now.second) and
20
+ CronBase._match_field(min_part, now.minute) and
21
+ CronBase._match_field(hour_part, now.hour) and
22
+ CronBase._match_field(day_part, now.day) and
23
+ CronBase._match_field(month_part, now.month) and
24
+ CronBase._match_field(weekday_part, now.weekday())
25
+ )
26
+
27
+ @staticmethod
28
+ def _match_field(pattern: str, value: int) -> bool:
29
+ if pattern == "*": return True
30
+ if "," in pattern: return any(CronBase._match_field(p, value) for p in pattern.split(","))
31
+ if "/" in pattern:
32
+ r, s = pattern.split("/")
33
+ step = int(s)
34
+ if r in ["*", ""]: return value % step == 0
35
+ if "-" in r:
36
+ start, end = map(int, r.split("-"))
37
+ return start <= value <= end and (value - start) % step == 0
38
+ return value >= int(r) and (value - int(r)) % step == 0
39
+ if "-" in pattern:
40
+ start, end = map(int, pattern.split("-"))
41
+ return start <= value <= end
42
+ return int(pattern) == value
@@ -0,0 +1,104 @@
1
+ import threading
2
+ import time
3
+ import datetime
4
+ import logging
5
+ import inspect
6
+ from typing import List, Dict, Any, Callable
7
+ from .base import CronBase
8
+
9
+
10
+ class FasterCron:
11
+ def __init__(self, log_level=logging.INFO):
12
+ self.tasks: List[Dict[str, Any]] = []
13
+ self.logger = logging.getLogger("FasterCron.Sync")
14
+ self.logger.setLevel(log_level)
15
+ self._running = False
16
+ self._monitors: List[threading.Thread] = []
17
+
18
+ def schedule(self, expression: str, allow_overlap: bool = True):
19
+ """
20
+ 注册同步任务。
21
+ allow_overlap 为 True 时,会通过开启新线程来实现并发执行。
22
+ """
23
+
24
+ def decorator(func: Callable):
25
+ self.tasks.append({
26
+ "expression": expression,
27
+ "func": func,
28
+ "allow_overlap": allow_overlap,
29
+ "name": func.__name__,
30
+ "last_worker": None # 用于追踪此任务的上一个执行线程
31
+ })
32
+ return func
33
+
34
+ return decorator
35
+
36
+ def run(self):
37
+ """阻塞启动所有任务监控器"""
38
+ self._running = True
39
+ self.logger.info(f"FasterCron (Sync Mode) started with {len(self.tasks)} tasks.")
40
+
41
+ for task in self.tasks:
42
+ t = threading.Thread(
43
+ target=self._monitor_loop,
44
+ args=(task,),
45
+ name=f"Monitor-{task['name']}",
46
+ daemon=True
47
+ )
48
+ t.start()
49
+ self._monitors.append(t)
50
+
51
+ try:
52
+ while self._running:
53
+ time.sleep(1)
54
+ except KeyboardInterrupt:
55
+ self.logger.info("FasterCron stopping...")
56
+ self._running = False
57
+
58
+ def _monitor_loop(self, task: Dict[str, Any]):
59
+ """每个任务独立的监听循环"""
60
+ last_trigger_ts = 0
61
+
62
+ while self._running:
63
+ now = datetime.datetime.now()
64
+ current_ts = int(now.timestamp())
65
+
66
+ # 1. 时间匹配检查 (确保每秒只触发一次)
67
+ if current_ts != last_trigger_ts and CronBase.is_time_match(task["expression"], now):
68
+ last_trigger_ts = current_ts
69
+
70
+ # 2. 并发控制
71
+ if not task["allow_overlap"]:
72
+ # 单例模式:检查上一个工作线程是否还在跑
73
+ prev_worker = task.get("last_worker")
74
+ if prev_worker and prev_worker.is_alive():
75
+ self.logger.warning(f"Task '{task['name']}' is still running. Skipping this cycle.")
76
+ continue
77
+
78
+ # 3. 执行任务
79
+ # 无论是并发还是单例,都开启新线程执行工作函数,避免阻塞监控循环
80
+ context = {"scheduled_at": now, "task_name": task["name"]}
81
+ worker_thread = threading.Thread(
82
+ target=self._execute_task,
83
+ args=(task["func"], context),
84
+ name=f"Worker-{task['name']}-{current_ts}",
85
+ daemon=True
86
+ )
87
+ task["last_worker"] = worker_thread # 记录引用以便下次检查
88
+ worker_thread.start()
89
+
90
+ # 4. 精确对齐到下一秒
91
+ sleep_time = 1.0 - (now.microsecond / 1_000_000) + 0.01
92
+ time.sleep(sleep_time)
93
+
94
+ def _execute_task(self, func: Callable, context: Dict):
95
+ """具体的任务执行包装器"""
96
+ try:
97
+ # 智能参数注入
98
+ sig = inspect.signature(func)
99
+ if 'context' in sig.parameters:
100
+ func(context=context)
101
+ else:
102
+ func()
103
+ except Exception as e:
104
+ self.logger.error(f"Error in task '{func.__name__}': {e}", exc_info=True)
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: faster-cron
3
+ Version: 0.1.0
4
+ Summary: 一个轻量、直观、支持异步与同步双模式的定时任务调度器
5
+ Author-email: Bernard Simon <bernardziyi@gmail.com>
6
+ Project-URL: Homepage, https://github.com/BernardSimon/faster-cron
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+
13
+ # FasterCron
14
+
15
+ **FasterCron** 是一个轻量级、直观且功能强大的 Python 定时任务调度工具库。它完美支持 **Asyncio (异步)** 和 **Threading (多线程)** 双模式,专为需要高可靠性、简单配置和任务并发控制的场景设计。
16
+
17
+ ---
18
+
19
+ ## 🌟 核心特性
20
+
21
+ * **双模式支持**:一套逻辑同时提供 `AsyncFasterCron`(异步)和 `FasterCron`(同步多线程)两种实现。
22
+ * **任务级并发控制**:通过 `allow_overlap` 参数精准控制同一个任务是否允许重叠执行(单例模式 vs 并发模式)。
23
+ * **智能参数注入**:自动检测任务函数签名,按需注入包含调度时间、任务名称的 `context` 上下文。
24
+ * **标准 Cron 支持**:兼容 5 位(分时日月周)和 6 位(秒分时日月周)Cron 表达式。
25
+ * **健壮性**:内置异常捕获机制,单个任务崩溃不影响调度器运行。
26
+ * **无外部依赖**:仅使用 Python 标准库实现,轻量无负担。
27
+
28
+ ---
29
+
30
+ ## 📦 安装
31
+
32
+ 您可以直接通过 pip 安装:
33
+
34
+ ```bash
35
+ pip install faster-cron
36
+
37
+ ```
38
+
39
+ 或者直接将源码放入您的项目中。
40
+
41
+ ---
42
+
43
+ ## 🚀 快速上手
44
+
45
+ ### 1. 异步模式 (Async Mode)
46
+
47
+ 适用于使用了 `aiohttp`, `httpx` 或 `tortoise-orm` 等异步库的项目。
48
+
49
+ ```python
50
+ import asyncio
51
+ from faster_cron import AsyncFasterCron
52
+
53
+ cron = AsyncFasterCron()
54
+
55
+
56
+ # 示例:每 5 秒执行一次,禁止重叠(若上一个任务没跑完,则跳过本次)
57
+ @cron.schedule("*/5 * * * * *", allow_overlap=False)
58
+ async def my_async_job(context):
59
+ print(f"正在执行任务: {context['task_name']}, 计划时间: {context['scheduled_at']}")
60
+ await asyncio.sleep(6) # 模拟长耗时任务
61
+
62
+
63
+ async def main():
64
+ await cron.start()
65
+
66
+
67
+ if __name__ == "__main__":
68
+ asyncio.run(main())
69
+
70
+ ```
71
+
72
+ ### 2. 同步模式 (Sync Mode)
73
+
74
+ 适用于传统的阻塞式脚本或爬虫。
75
+
76
+ ```python
77
+ from faster_cron import FasterCron
78
+ import time
79
+
80
+ cron = FasterCron()
81
+
82
+
83
+ # 示例:每秒执行一次,允许并发执行
84
+ @cron.schedule("* * * * * *", allow_overlap=True)
85
+ def my_sync_job():
86
+ print("滴答,同步任务正在运行...")
87
+ time.sleep(2)
88
+
89
+
90
+ if __name__ == "__main__":
91
+ cron.run()
92
+
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 🛠 核心 API 说明
98
+
99
+ ### 调度装饰器 `schedule`
100
+
101
+ | 参数 | 类型 | 默认值 | 说明 |
102
+ | --- | --- | --- | --- |
103
+ | `expression` | `str` | - | Cron 表达式。支持 `*`, `,`, `-`, `/`。 |
104
+ | `allow_overlap` | `bool` | `True` | **关键参数**。`True`: 时间点到达即执行;`False`: 若该任务的上一个实例未结束,则跳过本次执行循环。 |
105
+
106
+ ### 上下文参数 `context`
107
+
108
+ 如果您的任务函数接收名为 `context` 的参数,FasterCron 会自动注入以下字典:
109
+
110
+ * `task_name`: 函数名称。
111
+ * `scheduled_at`: 任务触发的精确 `datetime` 对象。
112
+ * `expression`: 该任务使用的 Cron 表达式。
113
+
114
+ ---
115
+
116
+ ## 📅 Cron 表达式参考
117
+
118
+ FasterCron 支持灵活的表达式定义:
119
+
120
+ * `* * * * * *` : 每秒执行。
121
+ * `*/5 * * * * *` : 每 5 秒执行。
122
+ * `0 0 * * * *` : 每整小时执行。
123
+ * `0 30 9-17 * * *` : 每天 9:00 到 17:00 之间的每半小时执行。
124
+ * `0 0 0 * * 0` : 每周日凌晨执行。
125
+
126
+ ---
127
+
128
+ ## 🧪 运行测试
129
+
130
+ 本项目包含完善的单元测试。您可以使用 `pytest` 来验证功能:
131
+
132
+ ```bash
133
+ # 安装测试依赖
134
+ pip install pytest pytest-asyncio
135
+
136
+ # 运行所有测试
137
+ pytest
138
+
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 📄 开源协议
144
+
145
+ MIT License.
146
+
147
+ ---
148
+
149
+ **如果您觉得好用,欢迎点一个 Star!🌟**
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ faster_cron/__init__.py
4
+ faster_cron/async_cron.py
5
+ faster_cron/base.py
6
+ faster_cron/sync_cron.py
7
+ faster_cron.egg-info/PKG-INFO
8
+ faster_cron.egg-info/SOURCES.txt
9
+ faster_cron.egg-info/dependency_links.txt
10
+ faster_cron.egg-info/top_level.txt
11
+ test/test_async.py
12
+ test/test_sync.py
@@ -0,0 +1 @@
1
+ faster_cron
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "faster-cron"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="Bernard Simon", email="bernardziyi@gmail.com" },
10
+ ]
11
+ description = "一个轻量、直观、支持异步与同步双模式的定时任务调度器"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ dependencies = [
20
+ ]
21
+
22
+ [project.urls]
23
+ "Homepage" = "https://github.com/BernardSimon/faster-cron"
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["."]
27
+ include = ["faster_cron*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,50 @@
1
+ import asyncio
2
+ import pytest
3
+ from datetime import datetime
4
+ from faster_cron.async_cron import AsyncFastCron
5
+
6
+ @pytest.mark.asyncio
7
+ async def test_async_overlap_prevention():
8
+ """
9
+ 测试异步模式下的重叠控制:
10
+ 注册一个每秒触发一次的任务,但任务耗时 1.5 秒且 allow_overlap=False。
11
+ 在运行 3 秒的时间内,该任务应该只执行 2 次(第 1 秒触发,第 2 秒因为第 1 秒的没跑完被跳过)。
12
+ """
13
+ cron = AsyncFastCron()
14
+ execution_counter = 0
15
+
16
+ @cron.schedule("* * * * * *", allow_overlap=False)
17
+ async def slow_task():
18
+ nonlocal execution_counter
19
+ execution_counter += 1
20
+ await asyncio.sleep(1.5)
21
+
22
+ # 启动调度器并运行 3.2 秒后停止
23
+ cron_task = asyncio.create_task(cron.start())
24
+ await asyncio.sleep(3.2)
25
+ cron._running = False
26
+ cron_task.cancel()
27
+
28
+ # 预期执行次数应为 2 (触发点: T+1, T+3)
29
+ # 如果允许重叠,3.2秒内会触发 3 次
30
+ assert execution_counter == 2, f"预期执行 2 次,实际执行了 {execution_counter} 次"
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_async_context_injection():
34
+ """测试上下文参数是否能正确注入"""
35
+ cron = AsyncFastCron()
36
+ received_context = None
37
+
38
+ @cron.schedule("* * * * * *")
39
+ async def task_with_ctx(context):
40
+ nonlocal received_context
41
+ received_context = context
42
+
43
+ cron_task = asyncio.create_task(cron.start())
44
+ await asyncio.sleep(1.1)
45
+ cron._running = False
46
+ cron_task.cancel()
47
+
48
+ assert received_context is not None
49
+ assert "task_name" in received_context
50
+ assert received_context["task_name"] == "task_with_ctx"
@@ -0,0 +1,60 @@
1
+ import time
2
+ import threading
3
+ import pytest
4
+ from faster_cron.sync_cron import FastCron
5
+
6
+
7
+ def test_sync_overlap_prevention():
8
+ """
9
+ 测试同步模式下的重叠控制:
10
+ 任务耗时 1.2 秒,触发间隔 1 秒,禁止重叠。
11
+ """
12
+ cron = FastCron()
13
+ execution_counter = 0
14
+ lock = threading.Lock()
15
+
16
+ @cron.schedule("* * * * * *", allow_overlap=False)
17
+ def slow_sync_task():
18
+ nonlocal execution_counter
19
+ with lock:
20
+ execution_counter += 1
21
+ time.sleep(1.2)
22
+
23
+ # 在后台线程运行调度器
24
+ cron_thread = threading.Thread(target=cron.run, daemon=True)
25
+ cron_thread.start()
26
+
27
+ # 观察 2.5 秒
28
+ time.sleep(2.5)
29
+ cron._running = False
30
+
31
+ # 预期:第 1 秒启动,第 2 秒检测到线程 alive 于是跳过
32
+ with lock:
33
+ assert execution_counter < 3, "同步模式下禁止重叠失败,执行次数过多"
34
+
35
+
36
+ def test_sync_no_overlap_allowed():
37
+ """
38
+ 测试同步模式下的并发:
39
+ 允许重叠时,即使任务慢,触发点到了也应该立即启动新线程。
40
+ """
41
+ cron = FastCron()
42
+ execution_counter = 0
43
+ lock = threading.Lock()
44
+
45
+ @cron.schedule("* * * * * *", allow_overlap=True)
46
+ def slow_concurrent_task():
47
+ nonlocal execution_counter
48
+ with lock:
49
+ execution_counter += 1
50
+ time.sleep(2)
51
+
52
+ cron_thread = threading.Thread(target=cron.run, daemon=True)
53
+ cron_thread.start()
54
+
55
+ time.sleep(2.5)
56
+ cron._running = False
57
+
58
+ # 允许重叠,2.5秒内应该触发 2-3 次
59
+ with lock:
60
+ assert execution_counter >= 2