From f15f235ecf73485765c2ece8aca50e8777eef15f Mon Sep 17 00:00:00 2001 From: gooker_young Date: Sat, 27 Jun 2026 08:45:48 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=8F=91=E5=B8=83v0.2.6=E7=89=88?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E6=96=B0=E5=A2=9E=E9=87=8D=E7=BD=AE=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E7=BC=93=E5=AD=98=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增reseticon命令行工具用于重置Windows图标缓存 2. 重构平台常量导出逻辑,移除顶层直接导出的IS_*变量 3. 为系统任务相关的TaskSpec添加verbose输出 4. 优化测试用例的列表格式和平台条件写法 5. 更新依赖锁定文件和项目配置 --- pyproject.toml | 1 + src/pyflowx/__init__.py | 8 - ...{restarticoncache.py => reseticoncache.py} | 0 src/pyflowx/conditions.py | 27 -- src/pyflowx/tasks/system.py | 6 +- tests/test_conditions.py | 34 -- tests/test_taskspec_commands.py | 300 ++++++++---------- uv.lock | 2 +- 8 files changed, 139 insertions(+), 239 deletions(-) rename src/pyflowx/cli/{restarticoncache.py => reseticoncache.py} (100%) diff --git a/pyproject.toml b/pyproject.toml index 4e72b8a..37d0f09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ packtool = "pyflowx.cli.packtool:main" pdftool = "pyflowx.cli.pdftool:main" piptool = "pyflowx.cli.piptool:main" pymake = "pyflowx.cli.pymake:main" +reseticon = "pyflowx.cli.reseticoncache:main" scrcap = "pyflowx.cli.screenshot:main" sshcopy = "pyflowx.cli.sshcopyid:main" taskk = "pyflowx.cli.taskkill:main" diff --git a/src/pyflowx/__init__.py b/src/pyflowx/__init__.py index bfb5965..9de4c40 100644 --- a/src/pyflowx/__init__.py +++ b/src/pyflowx/__init__.py @@ -58,10 +58,6 @@ from __future__ import annotations from .conditions import ( - IS_LINUX, - IS_MACOS, - IS_POSIX, - IS_WINDOWS, BuiltinConditions, Condition, Constants, @@ -87,10 +83,6 @@ from .task import TaskCmd, TaskEvent, TaskResult, TaskSpec, TaskStatus __version__ = "0.2.6" __all__ = [ - "IS_LINUX", - "IS_MACOS", - "IS_POSIX", - "IS_WINDOWS", "BuiltinConditions", "CliExitCode", # CLI 运行器 diff --git a/src/pyflowx/cli/restarticoncache.py b/src/pyflowx/cli/reseticoncache.py similarity index 100% rename from src/pyflowx/cli/restarticoncache.py rename to src/pyflowx/cli/reseticoncache.py diff --git a/src/pyflowx/conditions.py b/src/pyflowx/conditions.py index cf2eaf7..e904296 100644 --- a/src/pyflowx/conditions.py +++ b/src/pyflowx/conditions.py @@ -27,26 +27,6 @@ class Constants: class BuiltinConditions: """内置条件判断函数集合.""" - @staticmethod - def IS_WINDOWS() -> bool: - """是否为 Windows 平台.""" - return Constants.IS_WINDOWS - - @staticmethod - def IS_LINUX() -> bool: - bool = Constants.IS_LINUX - return bool - - @staticmethod - def IS_MACOS() -> bool: - """是否为 macOS 平台.""" - return Constants.IS_MACOS - - @staticmethod - def IS_POSIX() -> bool: - """是否为 POSIX 系统 (Linux/macOS).""" - return Constants.IS_POSIX - @staticmethod def PYTHON_VERSION(major: int, minor: int | None = None) -> bool: """检查 Python 版本是否匹配. @@ -214,10 +194,3 @@ class BuiltinConditions: names = [getattr(c, "__name__", repr(c)) for c in conditions] _check.__name__ = f"OR({', '.join(names)})" return _check - - -# 导出常用条件 -IS_WINDOWS: Callable[[], bool] = BuiltinConditions.IS_WINDOWS -IS_LINUX: Callable[[], bool] = BuiltinConditions.IS_LINUX -IS_MACOS: Callable[[], bool] = BuiltinConditions.IS_MACOS -IS_POSIX: Callable[[], bool] = BuiltinConditions.IS_POSIX diff --git a/src/pyflowx/tasks/system.py b/src/pyflowx/tasks/system.py index b431822..27fd635 100644 --- a/src/pyflowx/tasks/system.py +++ b/src/pyflowx/tasks/system.py @@ -26,18 +26,20 @@ def reset_icon_cache() -> list[px.TaskSpec]: return [] return [ - px.TaskSpec("kill_explorer", fn=lambda: subprocess.run(["taskkill", "/f", "/im", "explorer.exe"], check=False)), + px.TaskSpec("kill_explorer", fn=lambda: subprocess.run(["taskkill", "/f", "/im", "explorer.exe"], check=False), verbose=True), px.TaskSpec( "delete_icon_cache", fn=lambda: subprocess.run(["del", "/a", "/q", r"%localappdata%\IconCache.db"], check=False), + verbose=True ), px.TaskSpec( "delete_icon_cache_all", fn=lambda: subprocess.run( ["del", "/a", "/q", r"%localappdata%\Microsoft\Windows\Explorer\iconcache*"], check=False ), + verbose=True ), - px.TaskSpec("restart_explorer", fn=lambda: subprocess.run(["start", "explorer.exe"], check=False)), + px.TaskSpec("restart_explorer", fn=lambda: subprocess.run(["cmd", "/c", "start", "explorer.exe"], check=False), verbose=True), ] diff --git a/tests/test_conditions.py b/tests/test_conditions.py index b866bb2..3195eca 100644 --- a/tests/test_conditions.py +++ b/tests/test_conditions.py @@ -5,10 +5,6 @@ import sys from unittest.mock import patch from pyflowx.conditions import ( - IS_LINUX, - IS_MACOS, - IS_POSIX, - IS_WINDOWS, BuiltinConditions, Constants, ) @@ -34,29 +30,6 @@ def test_constants_is_posix(): assert (sys.platform != "win32") == Constants.IS_POSIX -def test_builtin_conditions_is_windows(): - """Test BuiltinConditions.IS_WINDOWS.""" - result = BuiltinConditions.IS_WINDOWS() - assert result == Constants.IS_WINDOWS - - -def test_builtin_conditions_is_linux(): - """Test BuiltinConditions.IS_LINUX.""" - result = BuiltinConditions.IS_LINUX() - assert result == Constants.IS_LINUX - - -def test_builtin_conditions_is_macos(): - """Test BuiltinConditions.IS_MACOS.""" - result = BuiltinConditions.IS_MACOS() - assert result == Constants.IS_MACOS - - -def test_builtin_conditions_is_posix(): - """Test BuiltinConditions.IS_POSIX.""" - result = BuiltinConditions.IS_POSIX() - assert result == Constants.IS_POSIX - def test_builtin_conditions_python_version_major_only(): """Test BuiltinConditions.PYTHON_VERSION with major only.""" @@ -167,10 +140,3 @@ def test_builtin_conditions_or_one_true(): condition = BuiltinConditions.OR(false_condition, true_condition, false_condition) assert condition() is True - -def test_exported_conditions(): - """Test exported condition functions.""" - assert IS_WINDOWS() == Constants.IS_WINDOWS - assert IS_LINUX() == Constants.IS_LINUX - assert IS_MACOS() == Constants.IS_MACOS - assert IS_POSIX() == Constants.IS_POSIX diff --git a/tests/test_taskspec_commands.py b/tests/test_taskspec_commands.py index 341108f..22e4746 100644 --- a/tests/test_taskspec_commands.py +++ b/tests/test_taskspec_commands.py @@ -8,10 +8,8 @@ import pytest import pyflowx as px from pyflowx.conditions import ( - IS_LINUX, - IS_MACOS, - IS_WINDOWS, BuiltinConditions, + Constants, ) # 跨平台的 echo 命令 @@ -23,11 +21,9 @@ else: def test_taskspec_with_cmd_list(): """测试使用命令列表的 TaskSpec.""" - graph = px.Graph.from_specs( - [ - px.TaskSpec("echo_test", cmd=[*ECHO_CMD, "hello"]), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec("echo_test", cmd=[*ECHO_CMD, "hello"]), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -42,11 +38,9 @@ def test_taskspec_with_cmd_string(): else: shell_cmd = "echo 'hello from shell'" - graph = px.Graph.from_specs( - [ - px.TaskSpec("shell_test", cmd=shell_cmd), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec("shell_test", cmd=shell_cmd), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -61,15 +55,13 @@ def test_taskspec_with_conditions_skip(): def never_true(): return False - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "should_skip", - cmd=[*ECHO_CMD, "this should not run"], - conditions=(never_true,), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "should_skip", + cmd=[*ECHO_CMD, "this should not run"], + conditions=(never_true,), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -84,15 +76,13 @@ def test_taskspec_with_conditions_execute(): def always_true(): return True - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "should_run", - cmd=[*ECHO_CMD, "this should run"], - conditions=(always_true,), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "should_run", + cmd=[*ECHO_CMD, "this should run"], + conditions=(always_true,), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -109,25 +99,23 @@ def test_platform_conditions(): win_cmd = ["echo", "Windows"] posix_cmd = ["echo", "POSIX"] - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "win_task", - cmd=win_cmd, - conditions=(IS_WINDOWS,), - ), - px.TaskSpec( - "linux_task", - cmd=posix_cmd, - conditions=(IS_LINUX,), - ), - px.TaskSpec( - "macos_task", - cmd=posix_cmd, - conditions=(IS_MACOS,), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "win_task", + cmd=win_cmd, + conditions=(lambda: Constants.IS_WINDOWS,), + ), + px.TaskSpec( + "linux_task", + cmd=posix_cmd, + conditions=(lambda: Constants.IS_LINUX,), + ), + px.TaskSpec( + "macos_task", + cmd=posix_cmd, + conditions=(lambda: Constants.IS_MACOS,), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -155,15 +143,13 @@ def test_app_installed_conditions(): else: python_cmd = ["python3", "--version"] - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "python_check", - cmd=python_cmd, - conditions=(BuiltinConditions.HAS_INSTALLED("python"),), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "python_check", + cmd=python_cmd, + conditions=(BuiltinConditions.HAS_INSTALLED("python"),), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -189,25 +175,23 @@ def test_combined_conditions(): # NOT 条件 not_condition = BuiltinConditions.NOT(lambda: False) - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "and_test", - cmd=[*ECHO_CMD, "AND"], - conditions=(and_condition,), - ), - px.TaskSpec( - "or_test", - cmd=[*ECHO_CMD, "OR"], - conditions=(or_condition,), - ), - px.TaskSpec( - "not_test", - cmd=[*ECHO_CMD, "NOT"], - conditions=(not_condition,), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "and_test", + cmd=[*ECHO_CMD, "AND"], + conditions=(and_condition,), + ), + px.TaskSpec( + "or_test", + cmd=[*ECHO_CMD, "OR"], + conditions=(or_condition,), + ), + px.TaskSpec( + "not_test", + cmd=[*ECHO_CMD, "NOT"], + conditions=(not_condition,), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -223,15 +207,13 @@ def test_taskspec_with_cwd(): else: ls_cmd = ["ls", "-la"] - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "list_current", - cmd=ls_cmd, - cwd=Path.cwd(), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "list_current", + cmd=ls_cmd, + cwd=Path.cwd(), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -242,16 +224,14 @@ def test_taskspec_with_cwd(): @pytest.mark.slow def test_taskspec_with_timeout(): """测试超时设置.""" - graph = px.Graph.from_specs( - [ - # 短时间任务应该成功 - px.TaskSpec( - "short_task", - cmd=["python", "-c", "import time; time.sleep(0.1)"], - timeout=1.0, - ), - ] - ) + graph = px.Graph.from_specs([ + # 短时间任务应该成功 + px.TaskSpec( + "short_task", + cmd=["python", "-c", "import time; time.sleep(0.1)"], + timeout=1.0, + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -261,26 +241,24 @@ def test_taskspec_with_timeout(): def test_taskspec_dependency_with_conditions(): """测试依赖和条件的组合.""" - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "first", - cmd=[*ECHO_CMD, "first"], - conditions=(lambda: True,), - ), - px.TaskSpec( - "second", - cmd=[*ECHO_CMD, "second"], - depends_on=("first",), - conditions=(lambda: True,), - ), - px.TaskSpec( - "third", - cmd=[*ECHO_CMD, "third"], - depends_on=("second",), - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "first", + cmd=[*ECHO_CMD, "first"], + conditions=(lambda: True,), + ), + px.TaskSpec( + "second", + cmd=[*ECHO_CMD, "second"], + depends_on=("first",), + conditions=(lambda: True,), + ), + px.TaskSpec( + "third", + cmd=[*ECHO_CMD, "third"], + depends_on=("second",), + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -295,12 +273,10 @@ def test_taskspec_mixed_fn_and_cmd(): def my_function(): return "result from function" - graph = px.Graph.from_specs( - [ - px.TaskSpec("fn_task", fn=my_function), - px.TaskSpec("cmd_task", cmd=[*ECHO_CMD, "from command"]), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec("fn_task", fn=my_function), + px.TaskSpec("cmd_task", cmd=[*ECHO_CMD, "from command"]), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -315,15 +291,13 @@ def test_taskspec_cmd_overrides_fn(): def my_function(): return "should not run" - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "cmd_priority", - fn=my_function, - cmd=[*ECHO_CMD, "cmd takes priority"], - ), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "cmd_priority", + fn=my_function, + cmd=[*ECHO_CMD, "cmd takes priority"], + ), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -338,11 +312,9 @@ def test_taskspec_callable_cmd(): def my_callable(): return "callable result" - graph = px.Graph.from_specs( - [ - px.TaskSpec("callable_cmd", cmd=my_callable), - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec("callable_cmd", cmd=my_callable), + ]) report = px.run(graph, strategy="sequential") assert report.success @@ -403,15 +375,13 @@ class TestTaskSpecVerbose: """verbose=True 时失败也应打印返回码.""" from pyflowx.errors import TaskFailedError - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "fail", - cmd=["python", "-c", "import sys; sys.exit(1)"], - verbose=True, - ) - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "fail", + cmd=["python", "-c", "import sys; sys.exit(1)"], + verbose=True, + ) + ]) with pytest.raises(TaskFailedError): _ = px.run(graph, strategy="sequential") captured = capsys.readouterr() @@ -440,18 +410,16 @@ class TestTaskSpecCmdErrors: """命令失败时错误信息应包含 stderr.""" from pyflowx.errors import TaskFailedError - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "fail", - cmd=[ - "python", - "-c", - "import sys; sys.stderr.write('error-msg'); sys.exit(1)", - ], - ) - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "fail", + cmd=[ + "python", + "-c", + "import sys; sys.stderr.write('error-msg'); sys.exit(1)", + ], + ) + ]) with pytest.raises(TaskFailedError) as exc_info: _ = px.run(graph, strategy="sequential") # 非 verbose 模式下, stderr 应包含在错误信息中 @@ -479,15 +447,13 @@ class TestTaskSpecCmdErrors: """命令超时应抛出 RuntimeError.""" from pyflowx.errors import TaskFailedError - graph = px.Graph.from_specs( - [ - px.TaskSpec( - "slow", - cmd=["python", "-c", "import time; time.sleep(5)"], - timeout=0.1, - ) - ] - ) + graph = px.Graph.from_specs([ + px.TaskSpec( + "slow", + cmd=["python", "-c", "import time; time.sleep(5)"], + timeout=0.1, + ) + ]) with pytest.raises(TaskFailedError) as exc_info: _ = px.run(graph, strategy="sequential") assert "超时" in str(exc_info.value.cause) diff --git a/uv.lock b/uv.lock index 725825f..535647e 100644 --- a/uv.lock +++ b/uv.lock @@ -2184,7 +2184,7 @@ wheels = [ [[package]] name = "pyflowx" -version = "0.2.5" +version = "0.2.6" source = { editable = "." } dependencies = [ { name = "graphlib-backport", marker = "python_full_version < '3.9'" },