fix: 修复 py310 下 TaskSpec[Any]() 触发的 super() TypeError,CI 测试环境改为 py310,py314
CI / Lint, Typecheck & Test (push) Has been cancelled
CI / Lint, Typecheck & Test (push) Has been cancelled
This commit is contained in:
@@ -30,5 +30,5 @@ jobs:
|
||||
- name: Ruff check
|
||||
run: ruff check src tests
|
||||
|
||||
- name: Tox test (py38, py313)
|
||||
run: uvx tox run -e py38,py313
|
||||
- name: Tox test (py310, py314)
|
||||
run: uvx tox run -e py310,py314
|
||||
|
||||
@@ -27,7 +27,7 @@ def _fn() -> int:
|
||||
|
||||
|
||||
def _spec(name: str, deps: tuple[str, ...] = ()) -> TaskSpec[Any]:
|
||||
return TaskSpec[Any](name, _fn, depends_on=deps)
|
||||
return TaskSpec(name, _fn, depends_on=deps)
|
||||
|
||||
|
||||
def _result(
|
||||
@@ -40,7 +40,7 @@ def _result(
|
||||
) -> TaskResult[Any]:
|
||||
"""构造带时间戳的 TaskResult."""
|
||||
end = start + timedelta(seconds=duration) if duration > 0 else start
|
||||
return TaskResult[Any](
|
||||
return TaskResult(
|
||||
spec=_spec(name),
|
||||
status=status,
|
||||
value=None,
|
||||
@@ -139,7 +139,7 @@ class TestToHtml:
|
||||
start = datetime(2024, 1, 1, 0, 0, 0)
|
||||
report = px.RunReport()
|
||||
report.results["a"] = _result("a", start, 1.0)
|
||||
report.results["b"] = TaskResult[Any](
|
||||
report.results["b"] = TaskResult(
|
||||
spec=_spec("b"),
|
||||
status=TaskStatus.SKIPPED,
|
||||
reason="skip",
|
||||
|
||||
@@ -27,10 +27,10 @@ def _make_result( # noqa: PLR0913
|
||||
attempts: int = 1,
|
||||
) -> TaskResult[Any]:
|
||||
"""构造测试用 TaskResult。"""
|
||||
spec: TaskSpec[Any] = TaskSpec[Any](name, _fn, depends_on=depends_on, soft_depends_on=soft_depends_on)
|
||||
spec: TaskSpec[Any] = TaskSpec(name, _fn, depends_on=depends_on, soft_depends_on=soft_depends_on)
|
||||
start = datetime(2024, 1, 1, 0, 0, 0)
|
||||
end = datetime(2024, 1, 1, 0, 0, 1) if status != TaskStatus.PENDING else None
|
||||
return TaskResult[Any](
|
||||
return TaskResult(
|
||||
spec=spec,
|
||||
status=status,
|
||||
value=value,
|
||||
@@ -507,12 +507,10 @@ class TestRunReportIntegration:
|
||||
return 1
|
||||
|
||||
# continue_on_error=True 使失败不抛异常而写入报告(success 保持 True)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
px.TaskSpec("boom", boom, continue_on_error=True),
|
||||
px.TaskSpec("downstream", downstream, depends_on=("boom",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
px.TaskSpec("boom", boom, continue_on_error=True),
|
||||
px.TaskSpec("downstream", downstream, depends_on=("boom",)),
|
||||
])
|
||||
report = px.run(graph, strategy="sequential")
|
||||
# continue_on_error 不影响 success 标志,手动标记以触发诊断
|
||||
report.success = False
|
||||
|
||||
+48
-66
@@ -21,7 +21,7 @@ def _fn() -> int:
|
||||
|
||||
|
||||
def _spec(name: str, deps: tuple[str, ...] = ()) -> TaskSpec[Any]:
|
||||
return TaskSpec[Any](name, _fn, depends_on=deps)
|
||||
return TaskSpec(name, _fn, depends_on=deps)
|
||||
|
||||
|
||||
def _result(
|
||||
@@ -34,7 +34,7 @@ def _result(
|
||||
) -> TaskResult[Any]:
|
||||
"""构造带时间戳的 TaskResult."""
|
||||
end = start + timedelta(seconds=duration) if duration > 0 else start
|
||||
return TaskResult[Any](
|
||||
return TaskResult(
|
||||
spec=_spec(name),
|
||||
status=status,
|
||||
value=None,
|
||||
@@ -46,7 +46,7 @@ def _result(
|
||||
|
||||
def _skipped_result(name: str, reason: str = "skip") -> TaskResult[Any]:
|
||||
"""构造 SKIPPED 结果(无时间戳)."""
|
||||
return TaskResult[Any](
|
||||
return TaskResult(
|
||||
spec=_spec(name),
|
||||
status=TaskStatus.SKIPPED,
|
||||
reason=reason,
|
||||
@@ -95,13 +95,11 @@ class TestProfileReportConstruction:
|
||||
report.results["a"] = _result("a", start, 1.0)
|
||||
report.results["b"] = _result("b", start + timedelta(seconds=1), 2.0)
|
||||
report.results["c"] = _result("c", start + timedelta(seconds=3), 1.5)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("b",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("b",)),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -138,13 +136,11 @@ class TestProfileReportConstruction:
|
||||
report.results["a"] = _result("a", start, 1.0)
|
||||
report.results["b"] = _result("b", start, 3.0)
|
||||
report.results["c"] = _result("c", start + timedelta(seconds=3), 1.0)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b"),
|
||||
_spec("c", deps=("a", "b")),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b"),
|
||||
_spec("c", deps=("a", "b")),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -192,12 +188,10 @@ class TestWaitTime:
|
||||
report = px.RunReport()
|
||||
report.results["a"] = _result("a", start, 1.0)
|
||||
report.results["b"] = _result("b", start + timedelta(seconds=1.5), 1.0)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -210,12 +204,10 @@ class TestWaitTime:
|
||||
report.results["a"] = _result("a", start, 2.0)
|
||||
# b 在 a 还没完成时就开始(不应该但可能发生)
|
||||
report.results["b"] = _result("b", start + timedelta(seconds=1), 1.0)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -228,12 +220,10 @@ class TestWaitTime:
|
||||
report = px.RunReport()
|
||||
report.results["a"] = _result("a", start, 1.0)
|
||||
report.results["b"] = _skipped_result("b")
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -251,14 +241,12 @@ class TestCriticalPath:
|
||||
report.results["b"] = _result("b", start + timedelta(seconds=1), 3.0)
|
||||
report.results["c"] = _result("c", start + timedelta(seconds=1), 1.0)
|
||||
report.results["d"] = _result("d", start + timedelta(seconds=4), 1.0)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("a",)),
|
||||
_spec("d", deps=("b", "c")),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("a",)),
|
||||
_spec("d", deps=("b", "c")),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -291,7 +279,7 @@ class TestParallelism:
|
||||
def test_no_timestamps_zero_parallelism(self) -> None:
|
||||
"""所有任务无时间戳:并行度为 0."""
|
||||
report = px.RunReport()
|
||||
report.results["a"] = TaskResult[Any](spec=_spec("a"), status=TaskStatus.SUCCESS)
|
||||
report.results["a"] = TaskResult(spec=_spec("a"), status=TaskStatus.SUCCESS)
|
||||
graph = px.Graph.from_specs([_spec("a")])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
@@ -405,14 +393,12 @@ class TestQueries:
|
||||
report.results["b"] = _result("b", start + timedelta(seconds=1), 3.0)
|
||||
report.results["c"] = _result("c", start + timedelta(seconds=1), 1.0)
|
||||
report.results["d"] = _result("d", start + timedelta(seconds=4), 1.0)
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("a",)),
|
||||
_spec("d", deps=("b", "c")),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
_spec("a"),
|
||||
_spec("b", deps=("a",)),
|
||||
_spec("c", deps=("a",)),
|
||||
_spec("d", deps=("b", "c")),
|
||||
])
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
@@ -551,13 +537,11 @@ class TestIntegrationWithRun:
|
||||
time.sleep(0.01) # 确保任务有实际耗时,避免 duration 极小导致并行度计算为 0
|
||||
return 1
|
||||
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
px.TaskSpec("a", slow),
|
||||
px.TaskSpec("b", slow, depends_on=("a",)),
|
||||
px.TaskSpec("c", slow, depends_on=("a",)),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
px.TaskSpec("a", slow),
|
||||
px.TaskSpec("b", slow, depends_on=("a",)),
|
||||
px.TaskSpec("c", slow, depends_on=("a",)),
|
||||
])
|
||||
report = px.run(graph, strategy="sequential")
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
@@ -576,13 +560,11 @@ class TestIntegrationWithRun:
|
||||
time.sleep(0.05)
|
||||
return 1
|
||||
|
||||
graph = px.Graph.from_specs(
|
||||
[
|
||||
px.TaskSpec("a", slow),
|
||||
px.TaskSpec("b", slow),
|
||||
px.TaskSpec("c", slow),
|
||||
]
|
||||
)
|
||||
graph = px.Graph.from_specs([
|
||||
px.TaskSpec("a", slow),
|
||||
px.TaskSpec("b", slow),
|
||||
px.TaskSpec("c", slow),
|
||||
])
|
||||
report = px.run(graph, strategy="thread", max_workers=3)
|
||||
|
||||
profile = ProfileReport.from_report(report, graph)
|
||||
|
||||
+11
-11
@@ -32,11 +32,11 @@ def _make_result( # noqa: PLR0913
|
||||
reason: str | None = None,
|
||||
) -> TaskResult[Any]:
|
||||
"""构造测试用 TaskResult 实例."""
|
||||
spec: TaskSpec[Any] = TaskSpec[Any](name, _fn, tags=tags, depends_on=depends_on)
|
||||
spec: TaskSpec[Any] = TaskSpec(name, _fn, tags=tags, depends_on=depends_on)
|
||||
start = datetime(2024, 1, 1, 0, 0, 0)
|
||||
# 用 timedelta 精确表达秒数,避免 int() 截断小数
|
||||
end = start + timedelta(seconds=duration) if duration else None
|
||||
return TaskResult[Any](
|
||||
return TaskResult(
|
||||
spec=spec,
|
||||
status=status,
|
||||
value=value,
|
||||
@@ -98,7 +98,7 @@ class TestRunReportSummary:
|
||||
def test_summary_with_none_duration(self) -> None:
|
||||
"""未开始/未结束的任务 duration 为 None,不应计入总时长."""
|
||||
report = px.RunReport()
|
||||
spec: TaskSpec[Any] = TaskSpec[Any]("a", _fn) # type: ignore[arg-type]
|
||||
spec: TaskSpec[Any] = TaskSpec("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult(spec=spec, status=TaskStatus.FAILED)
|
||||
s = report.summary()
|
||||
assert s["total_duration_seconds"] == 0.0
|
||||
@@ -135,8 +135,8 @@ class TestRunReportDescribe:
|
||||
def test_describe_no_duration(self) -> None:
|
||||
"""无耗时的任务应显示为 '-'."""
|
||||
report = px.RunReport()
|
||||
spec: TaskSpec[Any] = TaskSpec[Any]("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult[Any](spec=spec, status=TaskStatus.PENDING)
|
||||
spec: TaskSpec[Any] = TaskSpec("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult(spec=spec, status=TaskStatus.PENDING)
|
||||
desc = report.describe()
|
||||
assert "-" in desc # duration 显示为 "-"
|
||||
|
||||
@@ -181,8 +181,8 @@ class TestRunReportQueries:
|
||||
def test_durations_no_duration(self) -> None:
|
||||
"""无时长的任务应返回 0.0."""
|
||||
report = px.RunReport()
|
||||
spec: TaskSpec[Any] = TaskSpec[Any]("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult[Any](spec=spec, status=TaskStatus.PENDING)
|
||||
spec: TaskSpec[Any] = TaskSpec("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult(spec=spec, status=TaskStatus.PENDING)
|
||||
durs = report.durations()
|
||||
assert durs["a"] == 0.0
|
||||
|
||||
@@ -257,8 +257,8 @@ class TestRunReportToDict:
|
||||
def test_to_dict_no_timestamps(self) -> None:
|
||||
"""未开始/未结束的任务 started_at/finished_at/duration 应为 None。"""
|
||||
report = px.RunReport()
|
||||
spec: TaskSpec[Any] = TaskSpec[Any]("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult[Any](spec=spec, status=TaskStatus.PENDING)
|
||||
spec: TaskSpec[Any] = TaskSpec("a", _fn) # type: ignore[arg-type]
|
||||
report.results["a"] = TaskResult(spec=spec, status=TaskStatus.PENDING)
|
||||
d = report.to_dict()
|
||||
entry = d["results"][0]
|
||||
assert entry["started_at"] is None
|
||||
@@ -439,7 +439,7 @@ class TestRunReportToHtml:
|
||||
"""任务名含 HTML 特殊字符应被转义。"""
|
||||
report = px.RunReport()
|
||||
malicious = "<script>evil()</script>"
|
||||
spec: TaskSpec[Any] = TaskSpec[Any](malicious, _fn)
|
||||
spec: TaskSpec[Any] = TaskSpec(malicious, _fn)
|
||||
result: TaskResult[Any] = TaskResult(
|
||||
spec=spec,
|
||||
status=TaskStatus.SUCCESS,
|
||||
@@ -538,7 +538,7 @@ class TestRunReportToHtml:
|
||||
"""无时间戳时应渲染占位符。"""
|
||||
report = px.RunReport()
|
||||
# 构造无时间戳的结果
|
||||
spec: TaskSpec[Any] = TaskSpec[Any]("a", _fn)
|
||||
spec: TaskSpec[Any] = TaskSpec("a", _fn)
|
||||
result: TaskResult[Any] = TaskResult(
|
||||
spec=spec,
|
||||
status=TaskStatus.SUCCESS,
|
||||
|
||||
@@ -354,7 +354,7 @@ class TestTaskSpecVerbose:
|
||||
|
||||
def test_verbose_default_is_false(self) -> None:
|
||||
"""verbose 默认应为 False."""
|
||||
spec: px.TaskSpec[Any] = px.TaskSpec[Any]("a", cmd=[*ECHO_CMD, "hi"])
|
||||
spec: px.TaskSpec[Any] = px.TaskSpec("a", cmd=[*ECHO_CMD, "hi"])
|
||||
assert spec.verbose is False
|
||||
|
||||
def test_verbose_true_prints_command(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
@@ -367,7 +367,7 @@ class TestTaskSpecVerbose:
|
||||
|
||||
def test_verbose_false_silent(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
"""verbose=False 时不应打印命令信息."""
|
||||
graph = px.Graph.from_specs([px.TaskSpec[Any]("echo", cmd=[*ECHO_CMD, "silent"], verbose=False)])
|
||||
graph = px.Graph.from_specs([px.TaskSpec("echo", cmd=[*ECHO_CMD, "silent"], verbose=False)])
|
||||
_ = px.run(graph, strategy="sequential")
|
||||
captured = capsys.readouterr()
|
||||
assert "执行命令" not in captured.out
|
||||
@@ -390,7 +390,7 @@ class TestTaskSpecVerbose:
|
||||
import tempfile
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
graph = px.Graph.from_specs([px.TaskSpec[Any]("ls", cmd=ECHO_CMD, cwd=Path(tmpdir), verbose=True)])
|
||||
graph = px.Graph.from_specs([px.TaskSpec("ls", cmd=ECHO_CMD, cwd=Path(tmpdir), verbose=True)])
|
||||
_ = px.run(graph, strategy="sequential")
|
||||
captured = capsys.readouterr()
|
||||
assert "工作目录" in captured.out
|
||||
|
||||
Reference in New Issue
Block a user