fileflow-cli 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.
- fileflow_cli-0.1.0/LICENSE +21 -0
- fileflow_cli-0.1.0/PKG-INFO +204 -0
- fileflow_cli-0.1.0/README.md +165 -0
- fileflow_cli-0.1.0/pyproject.toml +81 -0
- fileflow_cli-0.1.0/setup.cfg +4 -0
- fileflow_cli-0.1.0/src/fileflow/__init__.py +3 -0
- fileflow_cli-0.1.0/src/fileflow/__main__.py +6 -0
- fileflow_cli-0.1.0/src/fileflow/actions.py +164 -0
- fileflow_cli-0.1.0/src/fileflow/cli.py +193 -0
- fileflow_cli-0.1.0/src/fileflow/config.py +183 -0
- fileflow_cli-0.1.0/src/fileflow/engine.py +167 -0
- fileflow_cli-0.1.0/src/fileflow/matchers.py +214 -0
- fileflow_cli-0.1.0/src/fileflow/report.py +164 -0
- fileflow_cli-0.1.0/src/fileflow/rules.py +86 -0
- fileflow_cli-0.1.0/src/fileflow/templates.py +124 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/PKG-INFO +204 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/SOURCES.txt +25 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/dependency_links.txt +1 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/entry_points.txt +2 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/requires.txt +14 -0
- fileflow_cli-0.1.0/src/fileflow_cli.egg-info/top_level.txt +1 -0
- fileflow_cli-0.1.0/tests/test_actions.py +147 -0
- fileflow_cli-0.1.0/tests/test_config.py +130 -0
- fileflow_cli-0.1.0/tests/test_engine.py +142 -0
- fileflow_cli-0.1.0/tests/test_matchers.py +175 -0
- fileflow_cli-0.1.0/tests/test_rules.py +78 -0
- fileflow_cli-0.1.0/tests/test_templates.py +90 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuluo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fileflow-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 🧹 智能文件整理器 — YAML 规则引擎驱动的 CLI 文件分类/移动/清理工具
|
|
5
|
+
Author-email: Yuluo <lingeryuluo@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Linger668/fileflow
|
|
8
|
+
Project-URL: Repository, https://github.com/Linger668/fileflow
|
|
9
|
+
Project-URL: Issues, https://github.com/Linger668/fileflow/issues
|
|
10
|
+
Keywords: file-organization,file-management,cli,productivity
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Desktop Environment :: File Managers
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: typer>=0.12.0
|
|
27
|
+
Requires-Dist: rich>=13.7.0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Provides-Extra: watch
|
|
30
|
+
Requires-Dist: watchdog>=4.0.0; extra == "watch"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
36
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# 🧹 FileFlow
|
|
41
|
+
|
|
42
|
+
> 智能文件整理器 — 基于 YAML 规则引擎的 CLI 文件分类/移动/清理工具
|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/fileflow/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
[](https://github.com/astral-sh/ruff)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## ✨ 功能
|
|
51
|
+
|
|
52
|
+
- 📝 **YAML 规则引擎** — 声明式配置,一目了然
|
|
53
|
+
- 🔍 **预览模式** — 先看效果再执行,安全无副作用
|
|
54
|
+
- 📂 **多种匹配方式** — 扩展名/通配符/正则/文件名/大小
|
|
55
|
+
- 🎯 **智能文件移动** — 自动创建目录,冲突策略可选
|
|
56
|
+
- ⏪ **支持撤销** — 操作历史可追溯(即将推出)
|
|
57
|
+
- 🎨 **Rich 终端美化** — 彩色表格,清晰直观
|
|
58
|
+
- 🔌 **可扩展架构** — 匹配器和操作都支持注册表模式
|
|
59
|
+
|
|
60
|
+
## 🚀 快速开始
|
|
61
|
+
|
|
62
|
+
### 安装
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install fileflow
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 1. 初始化配置
|
|
69
|
+
|
|
70
|
+
在要整理的目录下生成配置文件:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
cd ~/Downloads
|
|
74
|
+
fileflow init
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
这会生成 `.fileflow.yaml`,内置了图片/文档/视频/代码等常用规则。
|
|
78
|
+
|
|
79
|
+
### 2. 预览效果
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
fileflow run .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
以预览模式运行,会展示每个文件将被如何整理,**不会真正移动文件**。
|
|
86
|
+
|
|
87
|
+
### 3. 执行整理
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
fileflow run . --force
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
确认无误后,使用 `--force` 真正执行。
|
|
94
|
+
|
|
95
|
+
### 一键完成(跳过确认)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
fileflow run . --force --yes
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 📖 使用指南
|
|
102
|
+
|
|
103
|
+
### 命令概览
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
fileflow init # 生成默认配置文件
|
|
107
|
+
fileflow run # 运行整理(默认预览)
|
|
108
|
+
fileflow validate # 验证配置文件
|
|
109
|
+
fileflow list-rules # 查看规则列表
|
|
110
|
+
fileflow --help # 查看全部命令
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 选项
|
|
114
|
+
|
|
115
|
+
| 选项 | 说明 |
|
|
116
|
+
|------|------|
|
|
117
|
+
| `--config, -c` | 指定配置文件路径 |
|
|
118
|
+
| `--dry-run, -n` | 仅预览模式 |
|
|
119
|
+
| `--force, -f` | 跳过预览直接执行 |
|
|
120
|
+
| `--yes, -y` | 自动确认,跳过交互提示 |
|
|
121
|
+
| `--no-recursive` | 不递归扫描子目录 |
|
|
122
|
+
| `--verbose, -v` | 详细输出 |
|
|
123
|
+
| `--version, -V` | 显示版本 |
|
|
124
|
+
|
|
125
|
+
### 配置示例
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
# .fileflow.yaml
|
|
129
|
+
version: "1.0"
|
|
130
|
+
|
|
131
|
+
settings:
|
|
132
|
+
dry_run_by_default: true # 默认预览模式
|
|
133
|
+
on_conflict: "skip" # skip | rename | overwrite | ask
|
|
134
|
+
create_target_dirs: true
|
|
135
|
+
|
|
136
|
+
rules:
|
|
137
|
+
- name: "整理图片"
|
|
138
|
+
match:
|
|
139
|
+
extensions: [".jpg", ".jpeg", ".png", ".gif", ".svg"]
|
|
140
|
+
action: move
|
|
141
|
+
target: "Pictures/{ext}/{filename}"
|
|
142
|
+
|
|
143
|
+
- name: "清理旧日志"
|
|
144
|
+
match:
|
|
145
|
+
extensions: [".log"]
|
|
146
|
+
modified_before_days: 30
|
|
147
|
+
action: delete
|
|
148
|
+
enabled: false # 默认禁用
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 📂 模板变量
|
|
152
|
+
|
|
153
|
+
目标路径 `target` 支持以下变量:
|
|
154
|
+
|
|
155
|
+
| 变量 | 说明 | 示例 |
|
|
156
|
+
|------|------|------|
|
|
157
|
+
| `{filename}` | 完整文件名 | `report.pdf` |
|
|
158
|
+
| `{name}` | 文件名(不含扩展名) | `report` |
|
|
159
|
+
| `{ext}` | 扩展名(含点,推荐用于文件名) | `.pdf` |
|
|
160
|
+
| `{ext_no_dot}` | 扩展名(不含点,推荐用于目录名) | `pdf` |
|
|
161
|
+
| `{year}` | 4 位年份 | `2026` |
|
|
162
|
+
| `{month}` | 2 位月份 | `07` |
|
|
163
|
+
| `{day}` | 2 位日期 | `13` |
|
|
164
|
+
| `{hour}` | 2 位小时 | `14` |
|
|
165
|
+
| `{minute}` | 2 位分钟 | `30` |
|
|
166
|
+
|
|
167
|
+
## 🏗 项目架构
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
fileflow/
|
|
171
|
+
├── src/fileflow/
|
|
172
|
+
│ ├── cli.py # CLI 命令定义 (Typer)
|
|
173
|
+
│ ├── config.py # YAML 配置加载 & 验证
|
|
174
|
+
│ ├── engine.py # 核心编排引擎
|
|
175
|
+
│ ├── rules.py # 规则数据模型
|
|
176
|
+
│ ├── matchers.py # 匹配器注册表 & 内置匹配器
|
|
177
|
+
│ ├── actions.py # 文件操作 (move/copy/delete)
|
|
178
|
+
│ ├── templates.py # 路径模板变量解析
|
|
179
|
+
│ ├── report.py # Rich 终端输出
|
|
180
|
+
│ └── watcher.py # 实时监控(可选)
|
|
181
|
+
├── tests/ # 全模块测试覆盖
|
|
182
|
+
└── pyproject.toml # 项目配置
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 🧪 运行测试
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pip install -e ".[dev]"
|
|
189
|
+
pytest
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
查看测试覆盖率:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pytest --cov --cov-report=html
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## 🤝 贡献
|
|
199
|
+
|
|
200
|
+
欢迎提交 Issue 和 PR!详情见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
201
|
+
|
|
202
|
+
## 📄 许可证
|
|
203
|
+
|
|
204
|
+
[MIT](LICENSE) © 2026 Yuluo
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# 🧹 FileFlow
|
|
2
|
+
|
|
3
|
+
> 智能文件整理器 — 基于 YAML 规则引擎的 CLI 文件分类/移动/清理工具
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/fileflow/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/astral-sh/ruff)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ 功能
|
|
12
|
+
|
|
13
|
+
- 📝 **YAML 规则引擎** — 声明式配置,一目了然
|
|
14
|
+
- 🔍 **预览模式** — 先看效果再执行,安全无副作用
|
|
15
|
+
- 📂 **多种匹配方式** — 扩展名/通配符/正则/文件名/大小
|
|
16
|
+
- 🎯 **智能文件移动** — 自动创建目录,冲突策略可选
|
|
17
|
+
- ⏪ **支持撤销** — 操作历史可追溯(即将推出)
|
|
18
|
+
- 🎨 **Rich 终端美化** — 彩色表格,清晰直观
|
|
19
|
+
- 🔌 **可扩展架构** — 匹配器和操作都支持注册表模式
|
|
20
|
+
|
|
21
|
+
## 🚀 快速开始
|
|
22
|
+
|
|
23
|
+
### 安装
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install fileflow
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 1. 初始化配置
|
|
30
|
+
|
|
31
|
+
在要整理的目录下生成配置文件:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd ~/Downloads
|
|
35
|
+
fileflow init
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
这会生成 `.fileflow.yaml`,内置了图片/文档/视频/代码等常用规则。
|
|
39
|
+
|
|
40
|
+
### 2. 预览效果
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
fileflow run .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
以预览模式运行,会展示每个文件将被如何整理,**不会真正移动文件**。
|
|
47
|
+
|
|
48
|
+
### 3. 执行整理
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
fileflow run . --force
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
确认无误后,使用 `--force` 真正执行。
|
|
55
|
+
|
|
56
|
+
### 一键完成(跳过确认)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
fileflow run . --force --yes
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 📖 使用指南
|
|
63
|
+
|
|
64
|
+
### 命令概览
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
fileflow init # 生成默认配置文件
|
|
68
|
+
fileflow run # 运行整理(默认预览)
|
|
69
|
+
fileflow validate # 验证配置文件
|
|
70
|
+
fileflow list-rules # 查看规则列表
|
|
71
|
+
fileflow --help # 查看全部命令
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 选项
|
|
75
|
+
|
|
76
|
+
| 选项 | 说明 |
|
|
77
|
+
|------|------|
|
|
78
|
+
| `--config, -c` | 指定配置文件路径 |
|
|
79
|
+
| `--dry-run, -n` | 仅预览模式 |
|
|
80
|
+
| `--force, -f` | 跳过预览直接执行 |
|
|
81
|
+
| `--yes, -y` | 自动确认,跳过交互提示 |
|
|
82
|
+
| `--no-recursive` | 不递归扫描子目录 |
|
|
83
|
+
| `--verbose, -v` | 详细输出 |
|
|
84
|
+
| `--version, -V` | 显示版本 |
|
|
85
|
+
|
|
86
|
+
### 配置示例
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
# .fileflow.yaml
|
|
90
|
+
version: "1.0"
|
|
91
|
+
|
|
92
|
+
settings:
|
|
93
|
+
dry_run_by_default: true # 默认预览模式
|
|
94
|
+
on_conflict: "skip" # skip | rename | overwrite | ask
|
|
95
|
+
create_target_dirs: true
|
|
96
|
+
|
|
97
|
+
rules:
|
|
98
|
+
- name: "整理图片"
|
|
99
|
+
match:
|
|
100
|
+
extensions: [".jpg", ".jpeg", ".png", ".gif", ".svg"]
|
|
101
|
+
action: move
|
|
102
|
+
target: "Pictures/{ext}/{filename}"
|
|
103
|
+
|
|
104
|
+
- name: "清理旧日志"
|
|
105
|
+
match:
|
|
106
|
+
extensions: [".log"]
|
|
107
|
+
modified_before_days: 30
|
|
108
|
+
action: delete
|
|
109
|
+
enabled: false # 默认禁用
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 📂 模板变量
|
|
113
|
+
|
|
114
|
+
目标路径 `target` 支持以下变量:
|
|
115
|
+
|
|
116
|
+
| 变量 | 说明 | 示例 |
|
|
117
|
+
|------|------|------|
|
|
118
|
+
| `{filename}` | 完整文件名 | `report.pdf` |
|
|
119
|
+
| `{name}` | 文件名(不含扩展名) | `report` |
|
|
120
|
+
| `{ext}` | 扩展名(含点,推荐用于文件名) | `.pdf` |
|
|
121
|
+
| `{ext_no_dot}` | 扩展名(不含点,推荐用于目录名) | `pdf` |
|
|
122
|
+
| `{year}` | 4 位年份 | `2026` |
|
|
123
|
+
| `{month}` | 2 位月份 | `07` |
|
|
124
|
+
| `{day}` | 2 位日期 | `13` |
|
|
125
|
+
| `{hour}` | 2 位小时 | `14` |
|
|
126
|
+
| `{minute}` | 2 位分钟 | `30` |
|
|
127
|
+
|
|
128
|
+
## 🏗 项目架构
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
fileflow/
|
|
132
|
+
├── src/fileflow/
|
|
133
|
+
│ ├── cli.py # CLI 命令定义 (Typer)
|
|
134
|
+
│ ├── config.py # YAML 配置加载 & 验证
|
|
135
|
+
│ ├── engine.py # 核心编排引擎
|
|
136
|
+
│ ├── rules.py # 规则数据模型
|
|
137
|
+
│ ├── matchers.py # 匹配器注册表 & 内置匹配器
|
|
138
|
+
│ ├── actions.py # 文件操作 (move/copy/delete)
|
|
139
|
+
│ ├── templates.py # 路径模板变量解析
|
|
140
|
+
│ ├── report.py # Rich 终端输出
|
|
141
|
+
│ └── watcher.py # 实时监控(可选)
|
|
142
|
+
├── tests/ # 全模块测试覆盖
|
|
143
|
+
└── pyproject.toml # 项目配置
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 🧪 运行测试
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install -e ".[dev]"
|
|
150
|
+
pytest
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
查看测试覆盖率:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pytest --cov --cov-report=html
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 🤝 贡献
|
|
160
|
+
|
|
161
|
+
欢迎提交 Issue 和 PR!详情见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
162
|
+
|
|
163
|
+
## 📄 许可证
|
|
164
|
+
|
|
165
|
+
[MIT](LICENSE) © 2026 Yuluo
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fileflow-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "🧹 智能文件整理器 — YAML 规则引擎驱动的 CLI 文件分类/移动/清理工具"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Yuluo", email = "lingeryuluo@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["file-organization", "file-management", "cli", "productivity"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: End Users/Desktop",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Desktop Environment :: File Managers",
|
|
28
|
+
"Topic :: Utilities",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
dependencies = [
|
|
32
|
+
"typer>=0.12.0",
|
|
33
|
+
"rich>=13.7.0",
|
|
34
|
+
"pyyaml>=6.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
watch = ["watchdog>=4.0.0"]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=8.0",
|
|
41
|
+
"pytest-cov>=5.0",
|
|
42
|
+
"ruff>=0.5.0",
|
|
43
|
+
"mypy>=1.10.0",
|
|
44
|
+
"build>=1.0.0",
|
|
45
|
+
"twine>=5.0.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/Linger668/fileflow"
|
|
50
|
+
Repository = "https://github.com/Linger668/fileflow"
|
|
51
|
+
Issues = "https://github.com/Linger668/fileflow/issues"
|
|
52
|
+
|
|
53
|
+
[project.scripts]
|
|
54
|
+
fileflow = "fileflow.cli:app"
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.packages.find]
|
|
57
|
+
where = ["src"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
target-version = "py310"
|
|
61
|
+
line-length = 100
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = ["E", "F", "I", "N", "W", "UP", "SIM"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.format]
|
|
67
|
+
quote-style = "double"
|
|
68
|
+
|
|
69
|
+
[tool.mypy]
|
|
70
|
+
strict = true
|
|
71
|
+
python_version = "3.10"
|
|
72
|
+
exclude = ["tests/"]
|
|
73
|
+
ignore_missing_imports = true
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
testpaths = ["tests"]
|
|
77
|
+
python_files = ["test_*.py"]
|
|
78
|
+
addopts = "-v --cov=fileflow --cov-report=term-missing"
|
|
79
|
+
|
|
80
|
+
[tool.coverage.run]
|
|
81
|
+
source = ["fileflow"]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""文件操作:移动、复制、删除等.
|
|
2
|
+
|
|
3
|
+
支持冲突策略:
|
|
4
|
+
- skip: 跳过目标已存在的文件
|
|
5
|
+
- rename: 自动添加后缀 (1), (2)...
|
|
6
|
+
- overwrite: 覆盖目标文件
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import shutil
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Callable
|
|
15
|
+
|
|
16
|
+
from .templates import resolve_path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConflictStrategy(str, Enum):
|
|
20
|
+
"""文件冲突处理策略."""
|
|
21
|
+
|
|
22
|
+
SKIP = "skip"
|
|
23
|
+
RENAME = "rename"
|
|
24
|
+
OVERWRITE = "overwrite"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ActionResult:
|
|
28
|
+
"""一次文件操作的结果."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
source: Path,
|
|
33
|
+
target: Path | None = None,
|
|
34
|
+
success: bool = True,
|
|
35
|
+
action: str = "move",
|
|
36
|
+
skipped: bool = False,
|
|
37
|
+
error: str | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
self.source = source
|
|
40
|
+
self.target = target
|
|
41
|
+
self.success = success
|
|
42
|
+
self.action = action
|
|
43
|
+
self.skipped = skipped
|
|
44
|
+
self.error = error
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def summary(self) -> str:
|
|
48
|
+
if self.error:
|
|
49
|
+
return f"[red]✗ {self.action}: {self.source.name}[/] → {self.error}"
|
|
50
|
+
if self.skipped:
|
|
51
|
+
return f"[yellow]– 跳过: {self.source.name}[/] (目标已存在)"
|
|
52
|
+
return f"[green]✓ {self.action}: {self.source.name}[/] → {self.target}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def resolve_target_path(
|
|
56
|
+
target_template: str,
|
|
57
|
+
source: Path,
|
|
58
|
+
base_dir: Path,
|
|
59
|
+
conflict: ConflictStrategy = ConflictStrategy.SKIP,
|
|
60
|
+
) -> Path:
|
|
61
|
+
"""解析目标路径,并根据冲突策略处理.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
最终的目标路径(可能已根据冲突策略调整).
|
|
65
|
+
"""
|
|
66
|
+
dest = resolve_path(target_template, source, base_dir)
|
|
67
|
+
|
|
68
|
+
if conflict == ConflictStrategy.RENAME and dest.exists():
|
|
69
|
+
dest = _find_available_name(dest)
|
|
70
|
+
|
|
71
|
+
return dest
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _find_available_name(dest: Path) -> Path:
|
|
75
|
+
"""找到一个不存在的路径,如 file (1).txt, file (2).txt."""
|
|
76
|
+
parent = dest.parent
|
|
77
|
+
stem = dest.stem
|
|
78
|
+
suffix = dest.suffix
|
|
79
|
+
counter = 1
|
|
80
|
+
while True:
|
|
81
|
+
new_name = f"{stem} ({counter}){suffix}"
|
|
82
|
+
candidate = parent / new_name
|
|
83
|
+
if not candidate.exists():
|
|
84
|
+
return candidate
|
|
85
|
+
counter += 1
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def move_file(
|
|
89
|
+
source: Path,
|
|
90
|
+
target_template: str,
|
|
91
|
+
base_dir: Path,
|
|
92
|
+
create_dirs: bool = True,
|
|
93
|
+
conflict: ConflictStrategy = ConflictStrategy.SKIP,
|
|
94
|
+
) -> ActionResult:
|
|
95
|
+
"""移动文件."""
|
|
96
|
+
try:
|
|
97
|
+
dest = resolve_target_path(target_template, source, base_dir, conflict)
|
|
98
|
+
if dest == source:
|
|
99
|
+
return ActionResult(source, dest, success=True, action="move")
|
|
100
|
+
|
|
101
|
+
if dest.exists() and conflict == ConflictStrategy.SKIP:
|
|
102
|
+
return ActionResult(source, dest, skipped=True, action="move")
|
|
103
|
+
|
|
104
|
+
if create_dirs:
|
|
105
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
|
|
107
|
+
shutil.move(str(source), str(dest))
|
|
108
|
+
return ActionResult(source, dest, success=True, action="move")
|
|
109
|
+
except OSError as e:
|
|
110
|
+
return ActionResult(source, error=str(e), success=False, action="move")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def copy_file(
|
|
114
|
+
source: Path,
|
|
115
|
+
target_template: str,
|
|
116
|
+
base_dir: Path,
|
|
117
|
+
create_dirs: bool = True,
|
|
118
|
+
conflict: ConflictStrategy = ConflictStrategy.SKIP,
|
|
119
|
+
) -> ActionResult:
|
|
120
|
+
"""复制文件."""
|
|
121
|
+
try:
|
|
122
|
+
dest = resolve_target_path(target_template, source, base_dir, conflict)
|
|
123
|
+
if dest.exists() and conflict == ConflictStrategy.SKIP:
|
|
124
|
+
return ActionResult(source, dest, skipped=True, action="copy")
|
|
125
|
+
|
|
126
|
+
if create_dirs:
|
|
127
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
|
|
129
|
+
shutil.copy2(str(source), str(dest))
|
|
130
|
+
return ActionResult(source, dest, success=True, action="copy")
|
|
131
|
+
except OSError as e:
|
|
132
|
+
return ActionResult(source, error=str(e), success=False, action="copy")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def delete_file(source: Path) -> ActionResult:
|
|
136
|
+
"""删除文件."""
|
|
137
|
+
try:
|
|
138
|
+
source.unlink()
|
|
139
|
+
return ActionResult(source, success=True, action="delete")
|
|
140
|
+
except OSError as e:
|
|
141
|
+
return ActionResult(source, error=str(e), success=False, action="delete")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ── 操作注册表 ─────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
ActionFunc = Callable[..., ActionResult]
|
|
147
|
+
|
|
148
|
+
_actions: dict[str, ActionFunc] = {
|
|
149
|
+
"move": move_file,
|
|
150
|
+
"copy": copy_file,
|
|
151
|
+
"delete": delete_file,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_action(name: str) -> ActionFunc:
|
|
156
|
+
"""按名称获取操作函数."""
|
|
157
|
+
if name not in _actions:
|
|
158
|
+
raise KeyError(f"未知操作: {name!r},可选: {list(_actions.keys())}")
|
|
159
|
+
return _actions[name]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def list_actions() -> list[str]:
|
|
163
|
+
"""列出所有注册的操作名称."""
|
|
164
|
+
return list(_actions.keys())
|