bitool 0.1.2__py3-none-any.whl

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.
Files changed (51) hide show
  1. bitool/__init__.py +27 -0
  2. bitool/cmd/__init__.py +65 -0
  3. bitool/cmd/_base.py +105 -0
  4. bitool/cmd/_condition.py +60 -0
  5. bitool/cmd/_scheduler.py +548 -0
  6. bitool/cmd/env.py +454 -0
  7. bitool/cmd/git.py +123 -0
  8. bitool/cmd/io.py +248 -0
  9. bitool/cmd/pdf.py +385 -0
  10. bitool/cmd/run.py +300 -0
  11. bitool/cmd/toml.py +237 -0
  12. bitool/cmd/version.py +630 -0
  13. bitool/consts.py +14 -0
  14. bitool/core/__init__.py +7 -0
  15. bitool/core/app.py +142 -0
  16. bitool/core/commands.py +194 -0
  17. bitool/core/config.py +647 -0
  18. bitool/core/env.py +18 -0
  19. bitool/core/logger.py +237 -0
  20. bitool/core/plugin.py +117 -0
  21. bitool/core/workspace.py +76 -0
  22. bitool/models/__init__.py +3 -0
  23. bitool/models/version.py +173 -0
  24. bitool/scripts/__init__.py +1 -0
  25. bitool/scripts/bumpversion.py +189 -0
  26. bitool/scripts/clearscreen.py +37 -0
  27. bitool/scripts/envpy.py +161 -0
  28. bitool/scripts/envrs.py +119 -0
  29. bitool/scripts/filedate.py +246 -0
  30. bitool/scripts/filelevel.py +191 -0
  31. bitool/scripts/gittool.py +178 -0
  32. bitool/scripts/img2pdf.py +151 -0
  33. bitool/scripts/pdf2img.py +139 -0
  34. bitool/scripts/piptool.py +130 -0
  35. bitool/scripts/pymake.py +345 -0
  36. bitool/scripts/sshcopyid.py +491 -0
  37. bitool/scripts/taskkill.py +366 -0
  38. bitool/scripts/which.py +227 -0
  39. bitool/types.py +7 -0
  40. bitool/utils/__init__.py +9 -0
  41. bitool/utils/cli_parser.py +412 -0
  42. bitool/utils/executor.py +881 -0
  43. bitool/utils/profiler.py +369 -0
  44. bitool/utils/task.py +133 -0
  45. bitool/utils/task_group.py +668 -0
  46. bitool/utils/tests/__init__.py +0 -0
  47. bitool/utils/tests/test_profiler.py +487 -0
  48. bitool-0.1.2.dist-info/METADATA +154 -0
  49. bitool-0.1.2.dist-info/RECORD +51 -0
  50. bitool-0.1.2.dist-info/WHEEL +4 -0
  51. bitool-0.1.2.dist-info/entry_points.txt +15 -0
bitool/cmd/env.py ADDED
@@ -0,0 +1,454 @@
1
+ """环境变量操作模块, 提供用户友好的环境变量配置接口."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ from ..consts import Constants
10
+ from ..core import logger
11
+ from ..utils.executor import execute
12
+ from ._base import BaseCommand
13
+
14
+
15
+ @dataclass
16
+ class _EnvironmentVar:
17
+ """环境变量数据类"""
18
+
19
+ name: str
20
+ value: str
21
+
22
+
23
+ __all__ = [
24
+ "ExportEnvCommand",
25
+ "GetEnvCommand",
26
+ "ImportEnvCommand",
27
+ "ListEnvCommand",
28
+ "SetEnvCommand",
29
+ "UnsetEnvCommand",
30
+ ]
31
+
32
+
33
+ class GetEnvCommand(BaseCommand):
34
+ """获取环境变量命令"""
35
+
36
+ name = "getenv"
37
+ description = "获取环境变量值"
38
+
39
+ def __init__(self, name: str, default: str | None = None) -> None:
40
+ super().__init__()
41
+ self.name_val: str = name
42
+ self.default: str | None = default
43
+ self.value: str | None = None
44
+
45
+ def run(self) -> bool:
46
+ """获取环境变量值"""
47
+ self.value = os.environ.get(self.name_val, self.default)
48
+ if self.value is not None:
49
+ logger.info(f"环境变量 {self.name_val}={self.value}")
50
+ else:
51
+ logger.warning(f"环境变量 {self.name_val} 未设置")
52
+ return self.value is not None
53
+
54
+
55
+ class UnsetEnvCommand(BaseCommand):
56
+ """删除环境变量命令"""
57
+
58
+ name = "unsetenv"
59
+ description = "删除环境变量"
60
+
61
+ def __init__(self, name: str) -> None:
62
+ super().__init__()
63
+ self.name = name
64
+
65
+ def run(self) -> bool:
66
+ """删除环境变量"""
67
+ if self.name in os.environ:
68
+ del os.environ[self.name]
69
+ logger.info(f"已删除环境变量: {self.name}")
70
+ return True
71
+ else:
72
+ logger.warning(f"环境变量 {self.name} 不存在")
73
+ return False
74
+
75
+
76
+ class ListEnvCommand(BaseCommand):
77
+ """列出环境变量命令"""
78
+
79
+ name = "listenv"
80
+ description = "列出环境变量"
81
+
82
+ def __init__(self, pattern: str | None = None) -> None:
83
+ super().__init__()
84
+ self.pattern: str | None = pattern
85
+ self.envs: dict[str, str] = {}
86
+
87
+ def run(self) -> bool:
88
+ """列出环境变量"""
89
+ self.envs = dict(os.environ)
90
+ if self.pattern:
91
+ self.envs = {
92
+ k: v for k, v in self.envs.items() if self.pattern.lower() in k.lower()
93
+ }
94
+
95
+ if self.envs:
96
+ logger.info(f"找到 {len(self.envs)} 个环境变量")
97
+ for key, value in self.envs.items():
98
+ logger.info(f" {key}={value}")
99
+ else:
100
+ logger.warning("未找到匹配的环境变量")
101
+ return len(self.envs) > 0
102
+
103
+
104
+ class ExportEnvCommand(BaseCommand):
105
+ """导出环境变量命令"""
106
+
107
+ name = "exportenv"
108
+ description = "导出环境变量到文件"
109
+
110
+ def __init__(self, filepath: str, shell_type: str = "bash") -> None:
111
+ super().__init__()
112
+ self.filepath: str = filepath
113
+ self.shell_type: str = shell_type
114
+
115
+ def run(self) -> bool:
116
+ """导出环境变量到文件"""
117
+
118
+ from bitool.cmd.io import WriteFileCommand
119
+
120
+ # 生成环境变量脚本内容
121
+ filepath_path = Path(self.filepath)
122
+ lines: list[str] = []
123
+ if self.shell_type == "cmd":
124
+ lines.append("@echo off")
125
+ lines.append(":: 环境变量配置脚本")
126
+ for key, value in os.environ.items():
127
+ lines.append(f"set {key}={value}")
128
+ elif self.shell_type == "powershell":
129
+ lines.append("# 环境变量配置脚本 (PowerShell)")
130
+ for key, value in os.environ.items():
131
+ lines.append(f'$env:{key}="{value}"')
132
+ else: # bash/sh 脚本
133
+ lines.append("#!/bin/bash")
134
+ lines.append("# 环境变量配置脚本")
135
+ for key, value in os.environ.items():
136
+ lines.append(f'export {key}="{value}"')
137
+
138
+ content = "\n".join(lines) + "\n"
139
+
140
+ # 使用 WriteFileCommand 写入文件
141
+ write_cmd = WriteFileCommand(
142
+ filepath=filepath_path, content=content, overwrite=True, backup=False
143
+ )
144
+ return write_cmd.run()
145
+
146
+
147
+ class ImportEnvCommand(BaseCommand):
148
+ """导入环境变量命令"""
149
+
150
+ name = "importenv"
151
+ description = "从文件导入环境变量"
152
+
153
+ def __init__(self, filepath: str) -> None:
154
+ super().__init__()
155
+ self.filepath: str = filepath
156
+ self.count: int = 0
157
+
158
+ def run(self) -> bool:
159
+ """从文件导入环境变量"""
160
+ filepath_path = Path(self.filepath)
161
+ if not filepath_path.exists():
162
+ logger.error(f"文件不存在: {self.filepath}")
163
+ return False
164
+
165
+ try:
166
+ count = 0
167
+ with filepath_path.open("r", encoding="utf-8") as f:
168
+ for line in f:
169
+ line = line.strip()
170
+ # 跳过注释和空行
171
+ if not line or line.startswith("#") or line.startswith("@"):
172
+ continue
173
+
174
+ # 解析 export VAR=value (bash)
175
+ if line.startswith("export "):
176
+ line = line[7:] # 移除 "export "
177
+ if "=" in line:
178
+ key, value = line.split("=", 1)
179
+ # 移除引号
180
+ value = value.strip("'\"")
181
+ os.environ[key] = value
182
+ count += 1
183
+
184
+ # 解析 set VAR=value (cmd)
185
+ elif line.startswith("set "):
186
+ line = line[4:] # 移除 "set "
187
+ if "=" in line:
188
+ key, value = line.split("=", 1)
189
+ os.environ[key] = value
190
+ count += 1
191
+
192
+ # 解析 $env:VAR="value" (powershell)
193
+ elif line.startswith("$env:"):
194
+ line = line[5:] # 移除 "$env:"
195
+ if "=" in line:
196
+ key, value = line.split("=", 1)
197
+ # 移除引号
198
+ value = value.strip("'\"")
199
+ os.environ[key] = value
200
+ count += 1
201
+
202
+ self.count = count
203
+ logger.info(f"从文件导入了 {count} 个环境变量: {self.filepath}")
204
+ except Exception as e: # noqa: BLE001
205
+ logger.error(f"导入环境变量失败: {e}")
206
+ return False
207
+ else:
208
+ return count > 0
209
+
210
+
211
+ class SetEnvCommand(BaseCommand):
212
+ """设置环境变量命令"""
213
+
214
+ name = "setenv"
215
+ description = "设置环境变量"
216
+
217
+ def __init__(self, envs: list[tuple[str, str]]) -> None:
218
+ super().__init__()
219
+
220
+ self.envs: list[_EnvironmentVar] = [
221
+ _EnvironmentVar(name, value) for name, value in envs
222
+ ]
223
+
224
+ def run(self) -> bool:
225
+ """设置环境变量"""
226
+ if not self.envs:
227
+ logger.warning("未提供环境变量对")
228
+ return False
229
+
230
+ if Constants.IS_WINDOWS:
231
+ for env_var in self.envs:
232
+ execute(["setx", env_var.name, env_var.value])
233
+ else:
234
+ bashrc_path = os.path.expanduser("~/.bashrc")
235
+ # 读取现有的 bashrc 内容
236
+ existing_lines = []
237
+ if os.path.exists(bashrc_path):
238
+ with open(bashrc_path, encoding="utf-8") as f:
239
+ existing_lines = f.readlines()
240
+
241
+ # 追踪需要更新的环境变量
242
+ env_names = {env_var.name for env_var in self.envs}
243
+
244
+ # 过滤掉已存在的环境变量行,并构建新的内容
245
+ new_lines = []
246
+ for line in existing_lines:
247
+ # 检查是否是 export 语句且匹配我们要设置的环境变量
248
+ if line.strip().startswith("export "):
249
+ # 提取变量名
250
+ export_part = line.strip()[7:] # 去掉 "export "
251
+ if "=" in export_part:
252
+ var_name = export_part.split("=", 1)[0]
253
+ if var_name not in env_names:
254
+ new_lines.append(line)
255
+ else:
256
+ new_lines.append(line)
257
+ else:
258
+ new_lines.append(line)
259
+
260
+ # 添加新的环境变量配置
261
+ new_lines.extend(
262
+ f"export {env_var.name}={env_var.value}\n" for env_var in self.envs
263
+ )
264
+
265
+ # 写回 bashrc 文件
266
+ with open(bashrc_path, "w", encoding="utf-8") as f:
267
+ f.writelines(new_lines)
268
+
269
+ return True
270
+
271
+
272
+ class EnvOps:
273
+ """环境变量操作类
274
+
275
+ 提供环境变量的查看、设置、删除、导入/导出等功能
276
+ """
277
+
278
+ def __init__(self) -> None:
279
+ """初始化环境变量操作实例"""
280
+ self._envs: dict[str, str] = {}
281
+
282
+ def get(self, name: str, default: str | None = None) -> str | None:
283
+ """获取环境变量值
284
+
285
+ Args:
286
+ name: 环境变量名称
287
+ default: 默认值
288
+
289
+ Returns:
290
+ 环境变量值或默认值
291
+ """
292
+ return os.environ.get(name, default)
293
+
294
+ def set(self, name: str, value: str, *, persistent: bool = False) -> None:
295
+ """设置环境变量
296
+
297
+ Args:
298
+ name: 环境变量名称
299
+ value: 环境变量值
300
+ persistent: 是否保存到内部配置(用于后续恢复)
301
+ """
302
+ os.environ[name] = value
303
+ logger.info(f"已设置环境变量: {name}={value}")
304
+
305
+ if persistent:
306
+ self._envs[name] = value
307
+ logger.info(f"已保存环境变量到配置: {name}")
308
+
309
+ def unset(self, name: str, *, remove_persistent: bool = False) -> None:
310
+ """删除环境变量
311
+
312
+ Args:
313
+ name: 环境变量名称
314
+ remove_persistent: 是否同时从内部配置中删除
315
+ """
316
+ if name in os.environ:
317
+ del os.environ[name]
318
+ logger.info(f"已删除环境变量: {name}")
319
+
320
+ if remove_persistent and name in self._envs:
321
+ del self._envs[name]
322
+ logger.info(f"已从配置中删除: {name}")
323
+
324
+ def list_env(self, pattern: str | None = None) -> dict[str, str]:
325
+ """列出当前环境变量
326
+
327
+ Args:
328
+ pattern: 可选的过滤模式,只返回包含该模式的变量
329
+
330
+ Returns:
331
+ 环境变量字典
332
+ """
333
+ envs = dict(os.environ)
334
+ if pattern:
335
+ envs = {k: v for k, v in envs.items() if pattern.lower() in k.lower()}
336
+ return envs
337
+
338
+ def show_config(self) -> dict[str, str]:
339
+ """显示持久化的环境变量配置
340
+
341
+ Returns:
342
+ 持久化的环境变量字典
343
+ """
344
+ return self._envs.copy()
345
+
346
+ def restore(self) -> dict[str, str]:
347
+ """恢复持久化的环境变量
348
+
349
+ Returns:
350
+ 已恢复的环境变量字典
351
+ """
352
+ restored = {}
353
+ for key, value in self._envs.items():
354
+ os.environ[key] = value
355
+ restored[key] = value
356
+ logger.info(f"已恢复环境变量: {key}={value}")
357
+ return restored
358
+
359
+ def clear_all(self) -> None:
360
+ """清除所有配置的环境变量"""
361
+ for key in list(self._envs.keys()):
362
+ if key in os.environ:
363
+ del os.environ[key]
364
+ logger.info(f"已删除环境变量: {key}")
365
+
366
+ self._envs.clear()
367
+ logger.info("已清除所有环境变量配置")
368
+
369
+ def export_to_file(self, filepath: str, *, shell_type: str = "bash") -> None:
370
+ """导出环境变量到脚本文件
371
+
372
+ Args:
373
+ filepath: 输出文件路径
374
+ shell_type: 脚本类型 (bash/cmd/powershell),默认"bash"
375
+ """
376
+ filepath_path = Path(filepath)
377
+ filepath_path.parent.mkdir(parents=True, exist_ok=True)
378
+
379
+ lines = []
380
+ if shell_type == "cmd":
381
+ lines.append("@echo off")
382
+ lines.append(":: 环境变量配置脚本")
383
+ for key, value in self._envs.items():
384
+ lines.append(f"set {key}={value}")
385
+ elif shell_type == "powershell":
386
+ lines.append("# 环境变量配置脚本 (PowerShell)")
387
+ for key, value in self._envs.items():
388
+ lines.append(f'$env:{key}="{value}"')
389
+ else: # bash/sh 脚本
390
+ lines.append("#!/bin/bash")
391
+ lines.append("# 环境变量配置脚本")
392
+ for key, value in self._envs.items():
393
+ lines.append(f'export {key}="{value}"')
394
+
395
+ content = "\n".join(lines) + "\n"
396
+ with filepath_path.open("w", encoding="utf-8") as f:
397
+ f.write(content)
398
+ logger.info(f"已导出环境变量到: {filepath}")
399
+
400
+ def import_from_file(self, filepath: str) -> int:
401
+ """从脚本文件导入环境变量
402
+
403
+ Args:
404
+ filepath: 导入文件路径
405
+
406
+ Returns:
407
+ 成功导入的环境变量数量
408
+ """
409
+ filepath_path = Path(filepath)
410
+ if not filepath_path.exists():
411
+ msg = f"文件不存在: {filepath}"
412
+ raise FileNotFoundError(msg)
413
+
414
+ count = 0
415
+ with filepath_path.open("r", encoding="utf-8") as f:
416
+ for line in f:
417
+ line = line.strip()
418
+ # 跳过注释和空行
419
+ if not line or line.startswith("#") or line.startswith("@"):
420
+ continue
421
+
422
+ # 解析 export VAR=value (bash)
423
+ if line.startswith("export "):
424
+ line = line[7:] # 移除 "export "
425
+ if "=" in line:
426
+ key, value = line.split("=", 1)
427
+ # 移除引号
428
+ value = value.strip("'\"")
429
+ self.set(key, value)
430
+ count += 1
431
+
432
+ # 解析 set VAR=value (cmd)
433
+ elif line.startswith("set "):
434
+ line = line[4:] # 移除 "set "
435
+ if "=" in line:
436
+ key, value = line.split("=", 1)
437
+ self.set(key, value)
438
+ count += 1
439
+
440
+ # 解析 $env:VAR="value" (powershell)
441
+ elif line.startswith("$env:"):
442
+ line = line[5:] # 移除 "$env:"
443
+ if "=" in line:
444
+ key, value = line.split("=", 1)
445
+ # 移除引号
446
+ value = value.strip("'\"")
447
+ self.set(key, value)
448
+ count += 1
449
+
450
+ logger.info(f"从文件导入了 {count} 个环境变量: {filepath}")
451
+ return count
452
+
453
+ def __repr__(self) -> str:
454
+ return "EnvOps(环境变量配置工具)"
bitool/cmd/git.py ADDED
@@ -0,0 +1,123 @@
1
+ """Git命令封装模块.
2
+
3
+ 提供常用的Git操作命令封装, 包括提交、标签、检测等功能.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+
11
+ from bitool.cmd._base import BaseCommand
12
+ from bitool.cmd._condition import BuiltinConditions
13
+ from bitool.cmd.run import RunCommand
14
+ from bitool.core import logger
15
+ from bitool.utils import execute
16
+
17
+ # Git 条件检查
18
+ _GIT_INSTALLED = BuiltinConditions.HAS_APP_INSTALLED("git")
19
+
20
+
21
+ def _is_git_repo() -> bool:
22
+ """检查当前目录是否为 Git 仓库.
23
+
24
+ Returns
25
+ -------
26
+ bool
27
+ 如果是 Git 仓库返回 True, 否则返回 False
28
+ """
29
+ try:
30
+ execute(["git", "rev-parse", "--git-dir"], check=True, capture_output=True)
31
+ except (FileNotFoundError, OSError, RuntimeError):
32
+ return False
33
+ else:
34
+ return True
35
+
36
+
37
+ @dataclass
38
+ class GitAddCommand(RunCommand):
39
+ """Git add 命令.
40
+
41
+ Attributes:
42
+ files: 要添加的文件列表, 默认 None
43
+ all_files: 是否添加所有文件, 默认 False
44
+ """
45
+
46
+ name: str = "git-add"
47
+ description: str = "添加文件到 Git 暂存区"
48
+ files: list[Path] | None = None
49
+ all_files: bool = False
50
+ allow_conditions: list = field(default_factory=lambda: [_GIT_INSTALLED])
51
+
52
+ def __post_init__(self) -> None:
53
+ """数据类初始化后的处理, 构建 cmd 列表."""
54
+ if self.all_files:
55
+ self.cmd = ["git", "add", "."]
56
+ elif self.files:
57
+ self.cmd = ["git", "add"] + [str(f) for f in self.files]
58
+ else:
59
+ self.cmd = ["git", "add", "."]
60
+
61
+
62
+ @dataclass
63
+ class GitCommitCommand(RunCommand):
64
+ """Git commit 命令.
65
+
66
+ Attributes:
67
+ message: 提交消息, 默认 "chore: update"
68
+ """
69
+
70
+ name: str = "git-commit"
71
+ description: str = "提交 Git 更改"
72
+ message: str = "chore: update"
73
+ allow_conditions: list = field(default_factory=lambda: [_GIT_INSTALLED])
74
+
75
+ def __post_init__(self) -> None:
76
+ """数据类初始化后的处理, 构建 cmd 列表."""
77
+ self.cmd = ["git", "commit", "-m", self.message]
78
+
79
+
80
+ @dataclass
81
+ class GitTagCommand(RunCommand):
82
+ """Git tag 命令.
83
+
84
+ Attributes:
85
+ tag_name: 标签名称
86
+ message: 标签消息, 默认 None (创建轻量标签)
87
+ """
88
+
89
+ name: str = "git-tag"
90
+ description: str = "创建 Git 标签"
91
+ tag_name: str = ""
92
+ message: str | None = None
93
+ allow_conditions: list = field(default_factory=lambda: [_GIT_INSTALLED])
94
+
95
+ def __post_init__(self) -> None:
96
+ """数据类初始化后的处理, 构建 cmd 列表."""
97
+ if self.message:
98
+ self.cmd = ["git", "tag", "-a", self.tag_name, "-m", self.message]
99
+ else:
100
+ self.cmd = ["git", "tag", self.tag_name]
101
+
102
+
103
+ @dataclass
104
+ class GitCheckIsRepoCommand(BaseCommand):
105
+ """检查是否为 Git 仓库的命令."""
106
+
107
+ name: str = "git-is-repo"
108
+ description: str = "检查当前目录是否为 Git 仓库"
109
+
110
+ def run(self) -> bool:
111
+ """执行检查.
112
+
113
+ Returns
114
+ -------
115
+ bool
116
+ 如果是 Git 仓库返回 True
117
+ """
118
+ is_repo = _is_git_repo()
119
+ if is_repo:
120
+ logger.info("当前目录是 Git 仓库")
121
+ else:
122
+ logger.warning("当前目录不是 Git 仓库")
123
+ return is_repo