symphra-scheduler


Namesymphra-scheduler JSON
Version 1.0.2 PyPI version JSON
download
home_pageNone
SummaryA high-performance async task scheduler with cron support, auto-discovery, retry mechanism, and configuration management
upload_time2025-10-26 12:07:47
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseMIT
keywords async asyncio cron queue scheduler task timer
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # Symphra Scheduler

[![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

高性能异步定时任务调度库,专为 Python 3.11+ 设计。

## 特性

- **高性能异步** - 基于 asyncio,支持高并发任务执行
- **秒级精度** - 支持秒级定时任务,基于 Cron 表达式
- **多种后端** - 支持内存、Redis、RabbitMQ、SQLite 作为队列后端
- **重试机制** - 内置智能重试,支持指数退避策略
- **简洁 API** - 装饰器模式,一行代码即可定义任务
- **自动发现** - 自动扫描目录或包,发现并注册任务
- **类型安全** - 完整的类型提示,IDE 友好
- **配置灵活** - 支持代码配置、文件配置(JSON/TOML/YAML)
- **后台运行** - 支持守护进程模式,优雅关闭
- **低内存占用** - 精心优化,适合长时间运行
- **零依赖启动** - 默认内存后端,无需外部服务

## 安装

### 基础安装(仅内存/SQLite后端)

```bash
pip install symphra_scheduler
```

### 安装 Redis 支持

```bash
pip install symphra-scheduler[redis]
# 或
pip install symphra-scheduler redis
```

### 安装 RabbitMQ 支持

```bash
pip install symphra-scheduler[rabbitmq]
# 或
pip install symphra-scheduler aio-pika
```

### 完整安装

```bash
pip install symphra-scheduler[all]
```

## 快速开始

### 基础用法

```python
import asyncio
from symphra_scheduler import Scheduler, cron, interval

# 创建调度器
scheduler = Scheduler()

# 使用装饰器定义任务
@cron("*/5 * * * * *")  # 每5秒执行
async def task1():
    print("每5秒执行一次")

@interval(30)  # 每30秒执行
async def task2():
    print("每30秒执行一次")

# 运行调度器
async def main():
    await scheduler.start()

if __name__ == "__main__":
    asyncio.run(main())
```

### 使用 Redis 后端

```python
from symphra_scheduler import Scheduler, SchedulerConfig, cron
from symphra_scheduler.backends import RedisBackend

# 配置 Redis 后端
backend = RedisBackend(url="redis://localhost:6379/0")
config = SchedulerConfig(max_workers=20)

scheduler = Scheduler(config=config, backend=backend)

@cron("0 0 * * *")  # 每天零点执行
async def daily_cleanup():
    print("执行每日清理任务")
    await cleanup_old_data()

async def main():
    await scheduler.start(daemon=True)

asyncio.run(main())
```

### 使用 SQLite 本地持久化

```python
from symphra_scheduler import Scheduler, interval
from symphra_scheduler.backends import SQLiteBackend

# SQLite 后端,任务持久化到本地文件
backend = SQLiteBackend(db_path="scheduler.db")
scheduler = Scheduler(backend=backend)

@interval(60)
async def sync_task():
    print("同步数据...")
    await sync_data()

asyncio.run(scheduler.start())
```

### 任务自动发现

适合模块化项目,自动扫描并注册任务:

```python
from symphra_scheduler import Scheduler

scheduler = Scheduler()

# 自动发现并注册目录下所有任务
# 项目结构:
# app/
#   modules/
#     user/task.py
#     email/task.py
#     report/task.py

tasks = scheduler.discover_tasks_from_directory("app/modules")
print(f"自动发现并注册了 {len(tasks)} 个任务")

# 使用自定义文件名模式
tasks = scheduler.discover_tasks_from_directory(
    "app",
    pattern="*_tasks.py",
    exclude_patterns=["test_*.py"]
)

# 从包中发现
tasks = scheduler.discover_tasks_from_package("my_app.tasks")

asyncio.run(scheduler.start())
```

### 高级用法

```python
from datetime import datetime, timedelta
from symphra_scheduler import Scheduler, scheduled, RetryPolicy

scheduler = Scheduler()

# 自定义重试策略
@scheduled(
    interval=10,
    retry_policy=RetryPolicy(
        max_attempts=5,
        strategy="exponential",
        wait_min=1.0,
        wait_max=60.0,
    ),
    timeout=30.0,  # 30秒超时
    max_instances=3,  # 最多3个并发实例
    tags=["critical", "data-sync"],
)
async def important_task():
    """重要任务,需要重试和超时控制"""
    await process_critical_data()

# 一次性任务
from symphra_scheduler.decorators import once

@once(at=datetime(2025, 12, 31, 23, 59, 59))
async def new_year_celebration():
    print("新年快乐!")
```

### 配置文件方式

**config.toml:**

```toml
max_workers = 20
queue_size = 5000
shutdown_timeout = 60.0
enable_logging = true
log_level = "INFO"
timezone = "Asia/Shanghai"
persistence_enabled = true
persistence_path = "scheduler_state.json"
```

**Python 代码:**

```python
from symphra_scheduler import Scheduler, SchedulerConfig

# 从配置文件加载
config = SchedulerConfig.from_file("config.toml")
scheduler = Scheduler(config=config)
```

## 后端对比

| 后端 | 适用场景 | 持久化 | 分布式 | 性能 | 依赖 |
|------|---------|--------|--------|------|------|
| **Memory** | 开发、测试、单机 | ✗ | ✗ | ⭐⭐⭐⭐⭐ | 无 |
| **SQLite** | 单机生产、需要持久化 | ✓ | ✗ | ⭐⭐⭐⭐ | 无 |
| **Redis** | 分布式、高性能 | ✓ | ✓ | ⭐⭐⭐⭐⭐ | Redis |
| **RabbitMQ** | 高可靠性、消息队列 | ✓ | ✓ | ⭐⭐⭐⭐ | RabbitMQ |

## Cron 表达式

Symphra Scheduler 支持标准 Cron 表达式,并扩展支持秒级精度:

```
秒 分 时 日 月 周

示例:
*/5 * * * * *       - 每5秒
0 */10 * * * *      - 每10分钟
0 0 9 * * *         - 每天上午9点
0 0 0 1 * *         - 每月1号零点
0 0 0 * * 1         - 每周一零点
*/30 * 9-17 * * 1-5 - 工作日9-17点每30秒
```

## 监控和统计

```python
# 获取调度器统计信息
stats = await scheduler.get_stats()
print(stats)
# {
#     "running": true,
#     "total_tasks": 5,
#     "queue_size": 12,
#     "active_workers": 10,
#     "max_workers": 20,
#     "backend": "RedisBackend",
#     "tasks": [...]
# }

# 获取单个任务的指标
task = scheduler.get_task("task_name")
print(task.metrics)
# TaskMetrics(
#     total_runs=100,
#     successful_runs=95,
#     failed_runs=5,
#     average_execution_time=1.23
# )
```

## 架构设计

```
┌─────────────────────────────────────────┐
│           Scheduler (调度器)             │
│  - 任务注册和管理                         │
│  - 工作协程池                             │
│  - 优雅关闭                               │
└───────────┬─────────────────────────────┘
            │
            ├─── 装饰器 API (@cron, @interval)
            │
            ├─── 任务队列
            │    └─── 后端抽象层
            │         ├─── MemoryBackend
            │         ├─── SQLiteBackend
            │         ├─── RedisBackend
            │         └─── RabbitMQBackend
            │
            └─── 任务执行
                 ├─── 超时控制
                 ├─── 重试机制 (tenacity)
                 ├─── 并发控制
                 └─── 指标收集
```

## 为什么选择 Symphra Scheduler?

### vs Celery

- ✅ **更轻量** - 无需 Redis/RabbitMQ 即可运行
- ✅ **更简单** - 装饰器即用,无需额外配置
- ✅ **更快速** - 纯 asyncio,无进程开销
- ✅ **更现代** - Python 3.11+ 新特性,完整类型提示

### vs APScheduler

- ✅ **更高性能** - 原生异步,不是同步转异步
- ✅ **更好的可靠性** - 经过优化的内存管理
- ✅ **更灵活** - 可插拔后端,支持多种存储
- ✅ **更好的可观测性** - 内置指标和监控

## 开发

```bash
# 克隆仓库
git clone https://github.com/getaix/symphra-scheduler.git
cd symphra-scheduler

# 安装开发依赖
pip install -e ".[dev]"

# 运行测试
pytest tests/ -v --cov=symphra_scheduler --cov-report=html

# 代码检查
ruff check symphra_scheduler/
mypy symphra_scheduler/

# 格式化代码
black symphra_scheduler/
```

## 许可证

MIT License - 详见 [LICENSE](LICENSE) 文件

## 贡献

欢迎提交 Issue 和 Pull Request!

## 链接

- 📖 [完整文档](https://getaix.github.io/symphra-scheduler)
- 🐛 [问题反馈](https://github.com/getaix/symphra-scheduler/issues)
- 📦 [PyPI](https://pypi.org/project/symphra-scheduler/)
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "symphra-scheduler",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "async, asyncio, cron, queue, scheduler, task, timer",
    "author": null,
    "author_email": "getaix <develop@getaix.tech>",
    "download_url": "https://files.pythonhosted.org/packages/79/ba/b5c8a50655fe0c6c38419d62237e1eec57df5528ae082e44e568bc0a6283/symphra_scheduler-1.0.2.tar.gz",
    "platform": null,
    "description": "# Symphra Scheduler\n\n[![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n\u9ad8\u6027\u80fd\u5f02\u6b65\u5b9a\u65f6\u4efb\u52a1\u8c03\u5ea6\u5e93,\u4e13\u4e3a Python 3.11+ \u8bbe\u8ba1\u3002\n\n## \u7279\u6027\n\n- **\u9ad8\u6027\u80fd\u5f02\u6b65** - \u57fa\u4e8e asyncio,\u652f\u6301\u9ad8\u5e76\u53d1\u4efb\u52a1\u6267\u884c\n- **\u79d2\u7ea7\u7cbe\u5ea6** - \u652f\u6301\u79d2\u7ea7\u5b9a\u65f6\u4efb\u52a1,\u57fa\u4e8e Cron \u8868\u8fbe\u5f0f\n- **\u591a\u79cd\u540e\u7aef** - \u652f\u6301\u5185\u5b58\u3001Redis\u3001RabbitMQ\u3001SQLite \u4f5c\u4e3a\u961f\u5217\u540e\u7aef\n- **\u91cd\u8bd5\u673a\u5236** - \u5185\u7f6e\u667a\u80fd\u91cd\u8bd5,\u652f\u6301\u6307\u6570\u9000\u907f\u7b56\u7565\n- **\u7b80\u6d01 API** - \u88c5\u9970\u5668\u6a21\u5f0f,\u4e00\u884c\u4ee3\u7801\u5373\u53ef\u5b9a\u4e49\u4efb\u52a1\n- **\u81ea\u52a8\u53d1\u73b0** - \u81ea\u52a8\u626b\u63cf\u76ee\u5f55\u6216\u5305,\u53d1\u73b0\u5e76\u6ce8\u518c\u4efb\u52a1\n- **\u7c7b\u578b\u5b89\u5168** - \u5b8c\u6574\u7684\u7c7b\u578b\u63d0\u793a,IDE \u53cb\u597d\n- **\u914d\u7f6e\u7075\u6d3b** - \u652f\u6301\u4ee3\u7801\u914d\u7f6e\u3001\u6587\u4ef6\u914d\u7f6e(JSON/TOML/YAML)\n- **\u540e\u53f0\u8fd0\u884c** - \u652f\u6301\u5b88\u62a4\u8fdb\u7a0b\u6a21\u5f0f,\u4f18\u96c5\u5173\u95ed\n- **\u4f4e\u5185\u5b58\u5360\u7528** - \u7cbe\u5fc3\u4f18\u5316,\u9002\u5408\u957f\u65f6\u95f4\u8fd0\u884c\n- **\u96f6\u4f9d\u8d56\u542f\u52a8** - \u9ed8\u8ba4\u5185\u5b58\u540e\u7aef,\u65e0\u9700\u5916\u90e8\u670d\u52a1\n\n## \u5b89\u88c5\n\n### \u57fa\u7840\u5b89\u88c5(\u4ec5\u5185\u5b58/SQLite\u540e\u7aef)\n\n```bash\npip install symphra_scheduler\n```\n\n### \u5b89\u88c5 Redis \u652f\u6301\n\n```bash\npip install symphra-scheduler[redis]\n# \u6216\npip install symphra-scheduler redis\n```\n\n### \u5b89\u88c5 RabbitMQ \u652f\u6301\n\n```bash\npip install symphra-scheduler[rabbitmq]\n# \u6216\npip install symphra-scheduler aio-pika\n```\n\n### \u5b8c\u6574\u5b89\u88c5\n\n```bash\npip install symphra-scheduler[all]\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n### \u57fa\u7840\u7528\u6cd5\n\n```python\nimport asyncio\nfrom symphra_scheduler import Scheduler, cron, interval\n\n# \u521b\u5efa\u8c03\u5ea6\u5668\nscheduler = Scheduler()\n\n# \u4f7f\u7528\u88c5\u9970\u5668\u5b9a\u4e49\u4efb\u52a1\n@cron(\"*/5 * * * * *\")  # \u6bcf5\u79d2\u6267\u884c\nasync def task1():\n    print(\"\u6bcf5\u79d2\u6267\u884c\u4e00\u6b21\")\n\n@interval(30)  # \u6bcf30\u79d2\u6267\u884c\nasync def task2():\n    print(\"\u6bcf30\u79d2\u6267\u884c\u4e00\u6b21\")\n\n# \u8fd0\u884c\u8c03\u5ea6\u5668\nasync def main():\n    await scheduler.start()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n### \u4f7f\u7528 Redis \u540e\u7aef\n\n```python\nfrom symphra_scheduler import Scheduler, SchedulerConfig, cron\nfrom symphra_scheduler.backends import RedisBackend\n\n# \u914d\u7f6e Redis \u540e\u7aef\nbackend = RedisBackend(url=\"redis://localhost:6379/0\")\nconfig = SchedulerConfig(max_workers=20)\n\nscheduler = Scheduler(config=config, backend=backend)\n\n@cron(\"0 0 * * *\")  # \u6bcf\u5929\u96f6\u70b9\u6267\u884c\nasync def daily_cleanup():\n    print(\"\u6267\u884c\u6bcf\u65e5\u6e05\u7406\u4efb\u52a1\")\n    await cleanup_old_data()\n\nasync def main():\n    await scheduler.start(daemon=True)\n\nasyncio.run(main())\n```\n\n### \u4f7f\u7528 SQLite \u672c\u5730\u6301\u4e45\u5316\n\n```python\nfrom symphra_scheduler import Scheduler, interval\nfrom symphra_scheduler.backends import SQLiteBackend\n\n# SQLite \u540e\u7aef,\u4efb\u52a1\u6301\u4e45\u5316\u5230\u672c\u5730\u6587\u4ef6\nbackend = SQLiteBackend(db_path=\"scheduler.db\")\nscheduler = Scheduler(backend=backend)\n\n@interval(60)\nasync def sync_task():\n    print(\"\u540c\u6b65\u6570\u636e...\")\n    await sync_data()\n\nasyncio.run(scheduler.start())\n```\n\n### \u4efb\u52a1\u81ea\u52a8\u53d1\u73b0\n\n\u9002\u5408\u6a21\u5757\u5316\u9879\u76ee,\u81ea\u52a8\u626b\u63cf\u5e76\u6ce8\u518c\u4efb\u52a1:\n\n```python\nfrom symphra_scheduler import Scheduler\n\nscheduler = Scheduler()\n\n# \u81ea\u52a8\u53d1\u73b0\u5e76\u6ce8\u518c\u76ee\u5f55\u4e0b\u6240\u6709\u4efb\u52a1\n# \u9879\u76ee\u7ed3\u6784:\n# app/\n#   modules/\n#     user/task.py\n#     email/task.py\n#     report/task.py\n\ntasks = scheduler.discover_tasks_from_directory(\"app/modules\")\nprint(f\"\u81ea\u52a8\u53d1\u73b0\u5e76\u6ce8\u518c\u4e86 {len(tasks)} \u4e2a\u4efb\u52a1\")\n\n# \u4f7f\u7528\u81ea\u5b9a\u4e49\u6587\u4ef6\u540d\u6a21\u5f0f\ntasks = scheduler.discover_tasks_from_directory(\n    \"app\",\n    pattern=\"*_tasks.py\",\n    exclude_patterns=[\"test_*.py\"]\n)\n\n# \u4ece\u5305\u4e2d\u53d1\u73b0\ntasks = scheduler.discover_tasks_from_package(\"my_app.tasks\")\n\nasyncio.run(scheduler.start())\n```\n\n### \u9ad8\u7ea7\u7528\u6cd5\n\n```python\nfrom datetime import datetime, timedelta\nfrom symphra_scheduler import Scheduler, scheduled, RetryPolicy\n\nscheduler = Scheduler()\n\n# \u81ea\u5b9a\u4e49\u91cd\u8bd5\u7b56\u7565\n@scheduled(\n    interval=10,\n    retry_policy=RetryPolicy(\n        max_attempts=5,\n        strategy=\"exponential\",\n        wait_min=1.0,\n        wait_max=60.0,\n    ),\n    timeout=30.0,  # 30\u79d2\u8d85\u65f6\n    max_instances=3,  # \u6700\u591a3\u4e2a\u5e76\u53d1\u5b9e\u4f8b\n    tags=[\"critical\", \"data-sync\"],\n)\nasync def important_task():\n    \"\"\"\u91cd\u8981\u4efb\u52a1,\u9700\u8981\u91cd\u8bd5\u548c\u8d85\u65f6\u63a7\u5236\"\"\"\n    await process_critical_data()\n\n# \u4e00\u6b21\u6027\u4efb\u52a1\nfrom symphra_scheduler.decorators import once\n\n@once(at=datetime(2025, 12, 31, 23, 59, 59))\nasync def new_year_celebration():\n    print(\"\u65b0\u5e74\u5feb\u4e50!\")\n```\n\n### \u914d\u7f6e\u6587\u4ef6\u65b9\u5f0f\n\n**config.toml:**\n\n```toml\nmax_workers = 20\nqueue_size = 5000\nshutdown_timeout = 60.0\nenable_logging = true\nlog_level = \"INFO\"\ntimezone = \"Asia/Shanghai\"\npersistence_enabled = true\npersistence_path = \"scheduler_state.json\"\n```\n\n**Python \u4ee3\u7801:**\n\n```python\nfrom symphra_scheduler import Scheduler, SchedulerConfig\n\n# \u4ece\u914d\u7f6e\u6587\u4ef6\u52a0\u8f7d\nconfig = SchedulerConfig.from_file(\"config.toml\")\nscheduler = Scheduler(config=config)\n```\n\n## \u540e\u7aef\u5bf9\u6bd4\n\n| \u540e\u7aef | \u9002\u7528\u573a\u666f | \u6301\u4e45\u5316 | \u5206\u5e03\u5f0f | \u6027\u80fd | \u4f9d\u8d56 |\n|------|---------|--------|--------|------|------|\n| **Memory** | \u5f00\u53d1\u3001\u6d4b\u8bd5\u3001\u5355\u673a | \u2717 | \u2717 | \u2b50\u2b50\u2b50\u2b50\u2b50 | \u65e0 |\n| **SQLite** | \u5355\u673a\u751f\u4ea7\u3001\u9700\u8981\u6301\u4e45\u5316 | \u2713 | \u2717 | \u2b50\u2b50\u2b50\u2b50 | \u65e0 |\n| **Redis** | \u5206\u5e03\u5f0f\u3001\u9ad8\u6027\u80fd | \u2713 | \u2713 | \u2b50\u2b50\u2b50\u2b50\u2b50 | Redis |\n| **RabbitMQ** | \u9ad8\u53ef\u9760\u6027\u3001\u6d88\u606f\u961f\u5217 | \u2713 | \u2713 | \u2b50\u2b50\u2b50\u2b50 | RabbitMQ |\n\n## Cron \u8868\u8fbe\u5f0f\n\nSymphra Scheduler \u652f\u6301\u6807\u51c6 Cron \u8868\u8fbe\u5f0f,\u5e76\u6269\u5c55\u652f\u6301\u79d2\u7ea7\u7cbe\u5ea6:\n\n```\n\u79d2 \u5206 \u65f6 \u65e5 \u6708 \u5468\n\n\u793a\u4f8b:\n*/5 * * * * *       - \u6bcf5\u79d2\n0 */10 * * * *      - \u6bcf10\u5206\u949f\n0 0 9 * * *         - \u6bcf\u5929\u4e0a\u53489\u70b9\n0 0 0 1 * *         - \u6bcf\u67081\u53f7\u96f6\u70b9\n0 0 0 * * 1         - \u6bcf\u5468\u4e00\u96f6\u70b9\n*/30 * 9-17 * * 1-5 - \u5de5\u4f5c\u65e59-17\u70b9\u6bcf30\u79d2\n```\n\n## \u76d1\u63a7\u548c\u7edf\u8ba1\n\n```python\n# \u83b7\u53d6\u8c03\u5ea6\u5668\u7edf\u8ba1\u4fe1\u606f\nstats = await scheduler.get_stats()\nprint(stats)\n# {\n#     \"running\": true,\n#     \"total_tasks\": 5,\n#     \"queue_size\": 12,\n#     \"active_workers\": 10,\n#     \"max_workers\": 20,\n#     \"backend\": \"RedisBackend\",\n#     \"tasks\": [...]\n# }\n\n# \u83b7\u53d6\u5355\u4e2a\u4efb\u52a1\u7684\u6307\u6807\ntask = scheduler.get_task(\"task_name\")\nprint(task.metrics)\n# TaskMetrics(\n#     total_runs=100,\n#     successful_runs=95,\n#     failed_runs=5,\n#     average_execution_time=1.23\n# )\n```\n\n## \u67b6\u6784\u8bbe\u8ba1\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502           Scheduler (\u8c03\u5ea6\u5668)             \u2502\n\u2502  - \u4efb\u52a1\u6ce8\u518c\u548c\u7ba1\u7406                         \u2502\n\u2502  - \u5de5\u4f5c\u534f\u7a0b\u6c60                             \u2502\n\u2502  - \u4f18\u96c5\u5173\u95ed                               \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n            \u2502\n            \u251c\u2500\u2500\u2500 \u88c5\u9970\u5668 API (@cron, @interval)\n            \u2502\n            \u251c\u2500\u2500\u2500 \u4efb\u52a1\u961f\u5217\n            \u2502    \u2514\u2500\u2500\u2500 \u540e\u7aef\u62bd\u8c61\u5c42\n            \u2502         \u251c\u2500\u2500\u2500 MemoryBackend\n            \u2502         \u251c\u2500\u2500\u2500 SQLiteBackend\n            \u2502         \u251c\u2500\u2500\u2500 RedisBackend\n            \u2502         \u2514\u2500\u2500\u2500 RabbitMQBackend\n            \u2502\n            \u2514\u2500\u2500\u2500 \u4efb\u52a1\u6267\u884c\n                 \u251c\u2500\u2500\u2500 \u8d85\u65f6\u63a7\u5236\n                 \u251c\u2500\u2500\u2500 \u91cd\u8bd5\u673a\u5236 (tenacity)\n                 \u251c\u2500\u2500\u2500 \u5e76\u53d1\u63a7\u5236\n                 \u2514\u2500\u2500\u2500 \u6307\u6807\u6536\u96c6\n```\n\n## \u4e3a\u4ec0\u4e48\u9009\u62e9 Symphra Scheduler?\n\n### vs Celery\n\n- \u2705 **\u66f4\u8f7b\u91cf** - \u65e0\u9700 Redis/RabbitMQ \u5373\u53ef\u8fd0\u884c\n- \u2705 **\u66f4\u7b80\u5355** - \u88c5\u9970\u5668\u5373\u7528,\u65e0\u9700\u989d\u5916\u914d\u7f6e\n- \u2705 **\u66f4\u5feb\u901f** - \u7eaf asyncio,\u65e0\u8fdb\u7a0b\u5f00\u9500\n- \u2705 **\u66f4\u73b0\u4ee3** - Python 3.11+ \u65b0\u7279\u6027,\u5b8c\u6574\u7c7b\u578b\u63d0\u793a\n\n### vs APScheduler\n\n- \u2705 **\u66f4\u9ad8\u6027\u80fd** - \u539f\u751f\u5f02\u6b65,\u4e0d\u662f\u540c\u6b65\u8f6c\u5f02\u6b65\n- \u2705 **\u66f4\u597d\u7684\u53ef\u9760\u6027** - \u7ecf\u8fc7\u4f18\u5316\u7684\u5185\u5b58\u7ba1\u7406\n- \u2705 **\u66f4\u7075\u6d3b** - \u53ef\u63d2\u62d4\u540e\u7aef,\u652f\u6301\u591a\u79cd\u5b58\u50a8\n- \u2705 **\u66f4\u597d\u7684\u53ef\u89c2\u6d4b\u6027** - \u5185\u7f6e\u6307\u6807\u548c\u76d1\u63a7\n\n## \u5f00\u53d1\n\n```bash\n# \u514b\u9686\u4ed3\u5e93\ngit clone https://github.com/getaix/symphra-scheduler.git\ncd symphra-scheduler\n\n# \u5b89\u88c5\u5f00\u53d1\u4f9d\u8d56\npip install -e \".[dev]\"\n\n# \u8fd0\u884c\u6d4b\u8bd5\npytest tests/ -v --cov=symphra_scheduler --cov-report=html\n\n# \u4ee3\u7801\u68c0\u67e5\nruff check symphra_scheduler/\nmypy symphra_scheduler/\n\n# \u683c\u5f0f\u5316\u4ee3\u7801\nblack symphra_scheduler/\n```\n\n## \u8bb8\u53ef\u8bc1\n\nMIT License - \u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6\n\n## \u8d21\u732e\n\n\u6b22\u8fce\u63d0\u4ea4 Issue \u548c Pull Request!\n\n## \u94fe\u63a5\n\n- \ud83d\udcd6 [\u5b8c\u6574\u6587\u6863](https://getaix.github.io/symphra-scheduler)\n- \ud83d\udc1b [\u95ee\u9898\u53cd\u9988](https://github.com/getaix/symphra-scheduler/issues)\n- \ud83d\udce6 [PyPI](https://pypi.org/project/symphra-scheduler/)",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A high-performance async task scheduler with cron support, auto-discovery, retry mechanism, and configuration management",
    "version": "1.0.2",
    "project_urls": {
        "Changelog": "https://getaix.github.io/symphra-scheduler/changelog/",
        "Documentation": "https://getaix.github.io/symphra-scheduler",
        "Homepage": "https://github.com/getaix/symphra-scheduler",
        "Issues": "https://github.com/getaix/symphra-scheduler/issues",
        "Repository": "https://github.com/getaix/symphra-scheduler"
    },
    "split_keywords": [
        "async",
        " asyncio",
        " cron",
        " queue",
        " scheduler",
        " task",
        " timer"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "49b8fd0678d7a053bd74540fdd9dab80c2075e587ffafd7a7e0fef0cf6bbf341",
                "md5": "09de871403634bb4f411cc30ecc7b6fd",
                "sha256": "d99b78ea0f259f9cd192ef9ae5b02f1b96d561d0650eb77e0bb0d56a695449f5"
            },
            "downloads": -1,
            "filename": "symphra_scheduler-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "09de871403634bb4f411cc30ecc7b6fd",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 52309,
            "upload_time": "2025-10-26T12:07:45",
            "upload_time_iso_8601": "2025-10-26T12:07:45.581616Z",
            "url": "https://files.pythonhosted.org/packages/49/b8/fd0678d7a053bd74540fdd9dab80c2075e587ffafd7a7e0fef0cf6bbf341/symphra_scheduler-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "79bab5c8a50655fe0c6c38419d62237e1eec57df5528ae082e44e568bc0a6283",
                "md5": "ec72bc2416ed14fd368197e39ffc944d",
                "sha256": "60eefccd9acb55e2837d5322d11e39a3df5a17ccbe42d01927de088b4bb7d4ff"
            },
            "downloads": -1,
            "filename": "symphra_scheduler-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "ec72bc2416ed14fd368197e39ffc944d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 120669,
            "upload_time": "2025-10-26T12:07:47",
            "upload_time_iso_8601": "2025-10-26T12:07:47.127884Z",
            "url": "https://files.pythonhosted.org/packages/79/ba/b5c8a50655fe0c6c38419d62237e1eec57df5528ae082e44e568bc0a6283/symphra_scheduler-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-26 12:07:47",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "getaix",
    "github_project": "symphra-scheduler",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "symphra-scheduler"
}
        
Elapsed time: 1.05717s