chore: 调整Python版本与依赖适配,新增性能报告测试与工具函数
1. 将Python版本从3.13降级到3.11 2. 为typing-extensions添加版本适配标记 3. 简化dev依赖组,移除pysnooper 4. 重构perf_timer,提取_generate_report独立函数 5. 新增性能报告生成与测试用例
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
3.13
|
||||
3.11
|
||||
|
||||
+2
-5
@@ -13,7 +13,7 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"graphlib_backport >= 1.0.0; python_version < '3.9'",
|
||||
"typing-extensions>=4.13.2",
|
||||
"typing-extensions>=4.13.2; python_version < '3.10'",
|
||||
]
|
||||
description = "Lightweight, type-safe DAG task scheduler with multi-strategy execution."
|
||||
keywords = ["async", "dag", "scheduler", "task", "workflow"]
|
||||
@@ -93,10 +93,7 @@ packages = ["src/pyflowx"]
|
||||
pyflowx = { workspace = true }
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pyflowx[dev,office,llm]",
|
||||
"pysnooper>=1.2.3",
|
||||
]
|
||||
dev = ["pyflowx[dev,office,llm]"]
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
|
||||
+43
-6
@@ -1,15 +1,19 @@
|
||||
"""常用工具函数."""
|
||||
|
||||
__all__ = ["perf_timer"]
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = ["perf_timer"]
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from typing import Callable, ParamSpec, TypedDict
|
||||
from typing import Callable, TypedDict
|
||||
|
||||
from typing_extensions import TypeVar
|
||||
try:
|
||||
from typing_extensions import ParamSpec, TypeVar
|
||||
except ImportError:
|
||||
from typing import ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
@@ -30,6 +34,37 @@ _perf_metrics: defaultdict[str, _PerformanceMetrics] = defaultdict(
|
||||
)
|
||||
|
||||
|
||||
def _generate_report(unit: str, precision: int) -> str:
|
||||
"""生成性能指标报告,返回报告字符串."""
|
||||
if not _perf_metrics:
|
||||
return ""
|
||||
|
||||
lines: list[str] = []
|
||||
lines.append("=" * 50)
|
||||
lines.append("性能指标报告 (Performance Metrics Report)")
|
||||
lines.append("-" * 50)
|
||||
|
||||
# 按总耗时排序,最耗时的函数排在前面
|
||||
sorted_metrics = sorted(_perf_metrics.items(), key=lambda x: x[1]["total_time"], reverse=True)
|
||||
|
||||
for name, metrics in sorted_metrics:
|
||||
avg_time = metrics["total_time"] / metrics["count"] if metrics["count"] > 0 else 0
|
||||
lines.append(
|
||||
f"{name}: "
|
||||
f"调用次数={metrics['count']}, "
|
||||
f"总耗时={metrics['total_time']:.{precision}f}{unit}, "
|
||||
f"平均耗时={avg_time:.{precision}f}{unit}"
|
||||
)
|
||||
|
||||
lines.append("=" * 50)
|
||||
report_str = "\n".join(lines)
|
||||
|
||||
# 同时输出到日志
|
||||
logging.info("\n".join(lines))
|
||||
|
||||
return report_str
|
||||
|
||||
|
||||
def perf_timer(unit: str = "ms", precision: int = 4, report: bool = False):
|
||||
"""性能计时器装饰器."""
|
||||
scale: dict[str, float] = {
|
||||
@@ -63,8 +98,10 @@ def perf_timer(unit: str = "ms", precision: int = 4, report: bool = False):
|
||||
logging.info(f"Performance metrics report enabled with unit {unit} and precision {precision}")
|
||||
|
||||
@atexit.register
|
||||
def _() -> None:
|
||||
for name, metrics in _perf_metrics.items():
|
||||
logging.info(f"{name}: {metrics['count']} times, {metrics['total_time']:.{precision}f}{unit}")
|
||||
def _report_at_exit() -> None:
|
||||
"""在程序退出时报告性能指标."""
|
||||
_generate_report(unit, precision)
|
||||
|
||||
# 将报告生成逻辑提取为独立函数,便于测试
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -39,3 +39,27 @@ class TestPerformanceTimer:
|
||||
assert _perf_metrics["test_func"]["total_time"] >= 0.1
|
||||
|
||||
assert mock_log.call_count == 1
|
||||
|
||||
def test_generate_report(self, mocker: MockerFixture, caplog: pytest.LogCaptureFixture):
|
||||
mock_log = mocker.patch("logging.info")
|
||||
|
||||
from pyflowx.utils import _generate_report
|
||||
|
||||
@perf_timer(report=True, unit="ms", precision=3)
|
||||
def test_func():
|
||||
time.sleep(0.1)
|
||||
|
||||
@perf_timer(report=True, unit="ms", precision=3)
|
||||
def test_func2():
|
||||
time.sleep(0.2)
|
||||
|
||||
test_func()
|
||||
test_func2()
|
||||
|
||||
_generate_report("ms", 3)
|
||||
|
||||
assert mock_log.call_count == 3
|
||||
assert _perf_metrics["test_func"]["count"] == 1
|
||||
assert _perf_metrics["test_func"]["total_time"] >= 0.1
|
||||
assert _perf_metrics["test_func2"]["count"] == 1
|
||||
assert _perf_metrics["test_func2"]["total_time"] >= 0.2
|
||||
|
||||
@@ -5607,8 +5607,6 @@ version = "0.2.10"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "graphlib-backport", marker = "python_full_version < '3.9'" },
|
||||
{ name = "typing-extensions", version = "4.13.2", source = { registry = "https://mirrors.aliyun.com/pypi/simple/" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://mirrors.aliyun.com/pypi/simple/" }, marker = "python_full_version >= '3.9'" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -5682,7 +5680,7 @@ requires-dist = [
|
||||
{ name = "sglang", extras = ["all"], marker = "python_full_version >= '3.10' and sys_platform == 'linux' and extra == 'llm'", specifier = "==0.5.10rc0" },
|
||||
{ name = "tox", marker = "extra == 'dev'", specifier = ">=4.25.0" },
|
||||
{ name = "tox-uv", marker = "extra == 'dev'", specifier = ">=1.13.1" },
|
||||
{ name = "typing-extensions", specifier = ">=4.13.2" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.8'", specifier = ">=4.13.2" },
|
||||
]
|
||||
provides-extras = ["dev", "llm", "office"]
|
||||
|
||||
@@ -8439,22 +8437,13 @@ name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple/" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.15' and sys_platform == 'darwin'",
|
||||
"python_full_version >= '3.15' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
||||
"python_full_version >= '3.15' and sys_platform == 'win32'",
|
||||
"python_full_version >= '3.15' and sys_platform == 'emscripten'",
|
||||
"(python_full_version >= '3.15' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.15' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
"python_full_version == '3.14.*' and sys_platform == 'darwin'",
|
||||
"python_full_version == '3.13.*' and sys_platform == 'darwin'",
|
||||
"python_full_version == '3.12.*' and sys_platform == 'darwin'",
|
||||
"python_full_version == '3.14.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
||||
"python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
||||
"python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
||||
"python_full_version == '3.14.*' and sys_platform == 'win32'",
|
||||
"python_full_version == '3.14.*' and sys_platform == 'emscripten'",
|
||||
"(python_full_version == '3.14.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.14.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
"python_full_version == '3.13.*' and sys_platform == 'win32'",
|
||||
"python_full_version == '3.13.*' and sys_platform == 'emscripten'",
|
||||
"(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
"python_full_version == '3.12.*' and sys_platform == 'win32'",
|
||||
"python_full_version == '3.12.*' and sys_platform == 'emscripten'",
|
||||
|
||||
Reference in New Issue
Block a user