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
@@ -0,0 +1,487 @@
1
+ """性能分析工具模块测试."""
2
+
3
+ import os
4
+ import time
5
+ from typing import Generator, NoReturn
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+
10
+ from bitool.utils.profiler import (
11
+ _MS_PER_SEC,
12
+ _FuncStats,
13
+ _PyProfileRecord,
14
+ _PythonPerformanceProfiler,
15
+ )
16
+
17
+
18
+ @pytest.fixture(autouse=True)
19
+ def enable_profiling() -> Generator[None, None, None]:
20
+ """启用性能分析环境变量."""
21
+ with patch.dict(os.environ, {"BITOOL_PROFILE": "1"}):
22
+ yield
23
+
24
+
25
+ @pytest.fixture
26
+ def python_profiler() -> _PythonPerformanceProfiler:
27
+ """创建 Python 性能分析器实例."""
28
+ return _PythonPerformanceProfiler()
29
+
30
+
31
+ class TestPyProfileRecord:
32
+ """测试性能记录数据结构."""
33
+
34
+ def test_create_record(self) -> None:
35
+ """测试创建性能记录."""
36
+ record = _PyProfileRecord(
37
+ name="test_func",
38
+ duration_secs=1.5,
39
+ memory_before_mb=10.0,
40
+ memory_after_mb=12.5,
41
+ memory_delta_mb=2.5,
42
+ timestamp="2024-01-01 12:00:00",
43
+ )
44
+
45
+ assert record.name == "test_func"
46
+ assert record.duration_secs == 1.5
47
+ assert record.memory_before_mb == 10.0
48
+ assert record.memory_after_mb == 12.5
49
+ assert record.memory_delta_mb == 2.5
50
+ assert record.timestamp == "2024-01-01 12:00:00"
51
+
52
+ def test_default_values(self) -> None:
53
+ """测试默认值."""
54
+ record = _PyProfileRecord(name="test", duration_secs=1.0)
55
+
56
+ assert record.memory_before_mb == 0.0
57
+ assert record.memory_after_mb == 0.0
58
+ assert record.memory_delta_mb == 0.0
59
+ assert record.timestamp == ""
60
+
61
+ def test_record_with_zero_duration(self) -> None:
62
+ """测试零耗时记录."""
63
+ record = _PyProfileRecord(name="instant_func", duration_secs=0.0)
64
+ assert record.duration_secs == 0.0
65
+ assert record.name == "instant_func"
66
+
67
+ def test_record_with_negative_memory(self) -> None:
68
+ """测试负内存变化记录."""
69
+ record = _PyProfileRecord(
70
+ name="memory_release_func",
71
+ duration_secs=0.5,
72
+ memory_before_mb=20.0,
73
+ memory_after_mb=15.0,
74
+ memory_delta_mb=-5.0,
75
+ )
76
+ assert record.memory_delta_mb == -5.0
77
+
78
+
79
+ class TestPythonPerformanceProfiler:
80
+ """测试 Python 性能分析器实现."""
81
+
82
+ def test_start_and_stop_timer(self, python_profiler: _PythonPerformanceProfiler) -> None:
83
+ """测试开始和停止计时."""
84
+ python_profiler.start_timer("test_func")
85
+ time.sleep(0.01)
86
+ record = python_profiler.stop_timer("test_func")
87
+
88
+ assert record is not None
89
+ assert record.name == "test_func"
90
+ assert record.duration_secs >= 0.005 # 降低阈值,考虑时间精度问题
91
+ assert record.duration_secs < 1.0 # 应该小于 1 秒
92
+
93
+ def test_stop_nonexistent_timer(self, python_profiler: _PythonPerformanceProfiler) -> None:
94
+ """测试停止不存在的计时器."""
95
+ record = python_profiler.stop_timer("nonexistent")
96
+
97
+ assert record is None
98
+
99
+ def test_multiple_timers(self, python_profiler: _PythonPerformanceProfiler) -> None:
100
+ """测试多个计时器."""
101
+ python_profiler.start_timer("func1")
102
+ python_profiler.start_timer("func2")
103
+ time.sleep(0.01)
104
+
105
+ record1 = python_profiler.stop_timer("func1")
106
+ record2 = python_profiler.stop_timer("func2")
107
+
108
+ assert record1 is not None
109
+ assert record2 is not None
110
+ assert record1.name == "func1"
111
+ assert record2.name == "func2"
112
+
113
+ def test_get_records(self, python_profiler: _PythonPerformanceProfiler) -> None:
114
+ """测试获取记录."""
115
+ python_profiler.start_timer("func1")
116
+ python_profiler.stop_timer("func1")
117
+ python_profiler.start_timer("func2")
118
+ python_profiler.stop_timer("func2")
119
+
120
+ records = python_profiler.get_records()
121
+
122
+ assert len(records) == 2
123
+ assert records[0].name == "func1"
124
+ assert records[1].name == "func2"
125
+
126
+ def test_get_call_count(self, python_profiler: _PythonPerformanceProfiler) -> None:
127
+ """测试获取调用次数."""
128
+ python_profiler.start_timer("func1")
129
+ python_profiler.stop_timer("func1")
130
+ python_profiler.start_timer("func1")
131
+ python_profiler.stop_timer("func1")
132
+ python_profiler.start_timer("func2")
133
+ python_profiler.stop_timer("func2")
134
+
135
+ assert python_profiler.get_call_count("func1") == 2
136
+ assert python_profiler.get_call_count("func2") == 1
137
+ assert python_profiler.get_call_count("nonexistent") == 0
138
+
139
+ def test_clear(self, python_profiler: _PythonPerformanceProfiler) -> None:
140
+ """测试清除记录."""
141
+ python_profiler.start_timer("func1")
142
+ python_profiler.stop_timer("func1")
143
+
144
+ python_profiler.clear()
145
+
146
+ assert len(python_profiler.get_records()) == 0
147
+ assert python_profiler.get_call_count("func1") == 0
148
+
149
+ def test_memory_tracking(self, python_profiler: _PythonPerformanceProfiler) -> None:
150
+ """测试内存追踪."""
151
+ python_profiler.start_timer("memory_test")
152
+ # 分配一些内存
153
+ _data = list(range(10000))
154
+ record = python_profiler.stop_timer("memory_test")
155
+
156
+ assert record is not None
157
+ # 内存追踪应该工作(可能为 0 如果 tracemalloc 未启动)
158
+ assert isinstance(record.memory_before_mb, float)
159
+ assert isinstance(record.memory_after_mb, float)
160
+ assert isinstance(record.memory_delta_mb, float)
161
+
162
+ def test_records_are_copied(self, python_profiler: _PythonPerformanceProfiler) -> None:
163
+ """测试获取记录返回副本."""
164
+ python_profiler.start_timer("func1")
165
+ python_profiler.stop_timer("func1")
166
+
167
+ records1 = python_profiler.get_records()
168
+ records2 = python_profiler.get_records()
169
+
170
+ # 应该是不同的列表对象
171
+ assert records1 is not records2
172
+ # 但内容应该相同
173
+ assert len(records1) == len(records2)
174
+ assert records1[0].name == records2[0].name
175
+
176
+ def test_timer_overwrite(self, python_profiler: _PythonPerformanceProfiler) -> None:
177
+ """测试同名计时器覆盖."""
178
+ python_profiler.start_timer("func1")
179
+ time.sleep(0.01)
180
+ # 再次启动同名计时器会覆盖
181
+ python_profiler.start_timer("func1")
182
+ time.sleep(0.01)
183
+ record = python_profiler.stop_timer("func1")
184
+
185
+ assert record is not None
186
+ # 应该只记录了第二次的时间
187
+ assert record.duration_secs >= 0.005
188
+
189
+
190
+ class TestProfileDecorator:
191
+ """测试 @profile 装饰器."""
192
+
193
+ def test_profile_disabled(self) -> None:
194
+ """测试性能分析禁用时."""
195
+ with patch.dict(os.environ, {"BITOOL_PROFILE": "0"}, clear=True):
196
+ # 重新导入以应用环境变量
197
+ import importlib
198
+
199
+ from bitool.utils import profiler
200
+
201
+ importlib.reload(profiler)
202
+
203
+ @profiler.profile
204
+ def test_func() -> int:
205
+ return 42
206
+
207
+ result = test_func()
208
+ assert result == 42
209
+
210
+ def test_profile_enabled(self) -> None:
211
+ """测试性能分析启用时."""
212
+ # 创建新的包装器实例以避免缓存问题
213
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
214
+
215
+ wrapper = _PerformanceProfilerWrapper()
216
+
217
+ @wrapper.profile
218
+ def test_func() -> str:
219
+ time.sleep(0.01)
220
+ return "test_result"
221
+
222
+ result = test_func()
223
+
224
+ assert result == "test_result"
225
+ # 应该有记录
226
+ assert len(wrapper.records) > 0
227
+ assert wrapper.records[-1].name == "test_func"
228
+
229
+ def test_profile_with_exception(self) -> None:
230
+ """测试装饰器处理异常."""
231
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
232
+
233
+ wrapper = _PerformanceProfilerWrapper()
234
+
235
+ @wrapper.profile
236
+ def failing_func() -> NoReturn:
237
+ raise ValueError("测试异常")
238
+
239
+ with pytest.raises(ValueError, match="测试异常"):
240
+ failing_func()
241
+
242
+ def test_profile_preserves_function_metadata(self) -> None:
243
+ """测试装饰器保留函数元数据."""
244
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
245
+
246
+ wrapper = _PerformanceProfilerWrapper()
247
+
248
+ @wrapper.profile
249
+ def documented_func() -> str:
250
+ """这是一个有文档的函数."""
251
+ return "test"
252
+
253
+ # functools.wraps 应该保留这些属性
254
+ assert documented_func.__name__ == "documented_func"
255
+ # 注意:装饰器可能会修改 __doc__,这不是必须的
256
+
257
+ def test_profile_recursive_function(self) -> None:
258
+ """测试装饰器处理递归函数."""
259
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
260
+
261
+ wrapper = _PerformanceProfilerWrapper()
262
+ call_count_holder = {"count": 0}
263
+
264
+ def factorial_impl(n: int) -> int:
265
+ """不使用装饰器的递归实现."""
266
+ call_count_holder["count"] += 1
267
+ if n <= 1:
268
+ return 1
269
+ return n * factorial_impl(n - 1)
270
+
271
+ @wrapper.profile
272
+ def factorial(n: int) -> int:
273
+ return factorial_impl(n)
274
+
275
+ result = factorial(5)
276
+ assert result == 120
277
+ # 应该只有 1 次 profiler 记录(外层调用)
278
+ assert len(wrapper.records) == 1
279
+
280
+ def test_profile_nested_calls(self) -> None:
281
+ """测试嵌套函数调用."""
282
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
283
+
284
+ wrapper = _PerformanceProfilerWrapper()
285
+
286
+ @wrapper.profile
287
+ def inner_func() -> str:
288
+ time.sleep(0.01)
289
+ return "inner"
290
+
291
+ @wrapper.profile
292
+ def outer_func() -> str:
293
+ result = inner_func()
294
+ time.sleep(0.01)
295
+ return f"outer_{result}"
296
+
297
+ result = outer_func()
298
+
299
+ assert result == "outer_inner"
300
+ # 应该有两个函数的记录
301
+ assert len(wrapper.records) >= 2
302
+
303
+ def test_profile_with_args(self) -> None:
304
+ """测试装饰器处理带参数的函数."""
305
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
306
+
307
+ wrapper = _PerformanceProfilerWrapper()
308
+
309
+ @wrapper.profile
310
+ def add(a: int, b: int) -> int:
311
+ return a + b
312
+
313
+ result = add(3, 5)
314
+
315
+ assert result == 8
316
+ assert len(wrapper.records) > 0
317
+
318
+ def test_profile_with_kwargs(self) -> None:
319
+ """测试装饰器处理带关键字参数的函数."""
320
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
321
+
322
+ wrapper = _PerformanceProfilerWrapper()
323
+
324
+ @wrapper.profile
325
+ def greet(name: str, greeting: str = "Hello") -> str:
326
+ return f"{greeting}, {name}!"
327
+
328
+ result = greet("World", greeting="Hi")
329
+
330
+ assert result == "Hi, World!"
331
+ assert len(wrapper.records) > 0
332
+
333
+
334
+ class TestFuncStats:
335
+ """测试函数统计数据."""
336
+
337
+ def test_default_values(self) -> None:
338
+ """测试默认值."""
339
+ stats = _FuncStats()
340
+
341
+ assert stats.total_time == 0.0
342
+ assert stats.call_count == 0
343
+ assert stats.memory_delta == 0.0
344
+
345
+ def test_accumulation(self) -> None:
346
+ """测试数据累加."""
347
+ stats = _FuncStats()
348
+ stats.total_time += 1.0
349
+ stats.call_count += 1
350
+ stats.memory_delta += 0.5
351
+
352
+ assert stats.total_time == 1.0
353
+ assert stats.call_count == 1
354
+ assert stats.memory_delta == 0.5
355
+
356
+
357
+ class TestProfilerConstants:
358
+ """测试常量定义."""
359
+
360
+ def test_time_constants(self) -> None:
361
+ """测试时间相关常量."""
362
+ assert _MS_PER_SEC == 1000
363
+
364
+ def test_display_constants_types(self) -> None:
365
+ """测试显示常量类型."""
366
+ from bitool.utils.profiler import (
367
+ _COL_CALL_COUNT_STYLE,
368
+ _COL_DURATION_STYLE,
369
+ _COL_FUNC_NAME_STYLE,
370
+ _COL_MEMORY_STYLE,
371
+ _COL_PERCENTAGE_STYLE,
372
+ _CONSOLE_WIDTH,
373
+ _TABLE_BORDER_STYLE,
374
+ _TABLE_HEADER_STYLE,
375
+ _TABLE_TITLE_STYLE,
376
+ )
377
+
378
+ assert isinstance(_CONSOLE_WIDTH, int)
379
+ assert isinstance(_TABLE_TITLE_STYLE, str)
380
+ assert isinstance(_TABLE_BORDER_STYLE, str)
381
+ assert isinstance(_TABLE_HEADER_STYLE, str)
382
+ assert isinstance(_COL_FUNC_NAME_STYLE, str)
383
+ assert isinstance(_COL_DURATION_STYLE, str)
384
+ assert isinstance(_COL_PERCENTAGE_STYLE, str)
385
+ assert isinstance(_COL_CALL_COUNT_STYLE, str)
386
+ assert isinstance(_COL_MEMORY_STYLE, str)
387
+
388
+
389
+ class TestProfilerWrapper:
390
+ """测试性能分析器包装器."""
391
+
392
+ def test_clear_resets_state(self) -> None:
393
+ """测试清除操作重置状态."""
394
+ from bitool.utils.profiler import _profiler_wrapper
395
+
396
+ # 添加一些记录
397
+ _profiler_wrapper.profiler.start_timer("test")
398
+ _profiler_wrapper.profiler.stop_timer("test")
399
+
400
+ # 清除
401
+ _profiler_wrapper.clear()
402
+
403
+ # 记录应该被清除
404
+ # 注意:cached_property 会缓存,所以需要重新访问
405
+ assert len(_profiler_wrapper.profiler.get_records()) == 0
406
+
407
+ def test_cached_properties(self) -> None:
408
+ """测试缓存属性."""
409
+ from bitool.utils.profiler import _PerformanceProfilerWrapper
410
+
411
+ wrapper = _PerformanceProfilerWrapper()
412
+
413
+ # 添加记录
414
+ wrapper.profiler.start_timer("func1")
415
+ time.sleep(0.01)
416
+ wrapper.profiler.stop_timer("func1")
417
+
418
+ # 访问缓存属性
419
+ records = wrapper.records
420
+ total_time = wrapper.total_time
421
+ func_stats = wrapper.func_stats
422
+ sorted_funcs = wrapper.sorted_funcs
423
+
424
+ assert isinstance(records, list)
425
+ assert isinstance(total_time, (int, float)) # 可能是 0 或 float
426
+ assert isinstance(func_stats, dict)
427
+ assert isinstance(sorted_funcs, list)
428
+
429
+ # 验证排序
430
+ if len(sorted_funcs) > 1:
431
+ for i in range(len(sorted_funcs) - 1):
432
+ assert sorted_funcs[i][1].total_time >= sorted_funcs[i + 1][1].total_time
433
+
434
+ def test_format_duration(self) -> None:
435
+ """测试时间格式化."""
436
+ from bitool.utils.profiler import _profiler_wrapper
437
+
438
+ # 测试秒级
439
+ assert _profiler_wrapper._format_duration(1.5) == "1.50s"
440
+ assert _profiler_wrapper._format_duration(2.0) == "2.00s"
441
+ assert _profiler_wrapper._format_duration(1.0) == "1.00s"
442
+
443
+ # 测试毫秒级
444
+ assert _profiler_wrapper._format_duration(0.5) == "500ms"
445
+ assert _profiler_wrapper._format_duration(0.001) == "1ms"
446
+ assert _profiler_wrapper._format_duration(0.999) == "999ms"
447
+
448
+ # 测试微秒级
449
+ assert _profiler_wrapper._format_duration(0.0005) == "500μs"
450
+ assert _profiler_wrapper._format_duration(0.0001) == "100μs"
451
+ assert _profiler_wrapper._format_duration(0.0) == "0μs"
452
+
453
+ def test_format_memory(self) -> None:
454
+ """测试内存格式化."""
455
+ from bitool.utils.profiler import _profiler_wrapper
456
+
457
+ # 测试显著变化
458
+ assert _profiler_wrapper._format_memory(1.5) == "+1.50MB"
459
+ assert _profiler_wrapper._format_memory(-0.5) == "-0.50MB"
460
+ assert _profiler_wrapper._format_memory(0.0) == "~0MB" # 0 在阈值内
461
+
462
+ # 测试阈值内
463
+ assert _profiler_wrapper._format_memory(0.005) == "~0MB"
464
+ assert _profiler_wrapper._format_memory(-0.005) == "~0MB"
465
+ assert _profiler_wrapper._format_memory(0.009) == "~0MB" # 略小于阈值
466
+ assert _profiler_wrapper._format_memory(0.011) == "+0.01MB" # 略大于阈值
467
+
468
+ def test_truncate_func_name(self) -> None:
469
+ """测试函数名截断."""
470
+ from bitool.utils.profiler import _profiler_wrapper
471
+
472
+ # 短名称不截断
473
+ assert _profiler_wrapper._truncate_func_name("short_name") == "short_name"
474
+ assert _profiler_wrapper._truncate_func_name("exact_28_chars_long_name_") == "exact_28_chars_long_name_"
475
+
476
+ # 长名称截断
477
+ long_name = "very_very_very_very_long_function_name"
478
+ truncated = _profiler_wrapper._truncate_func_name(long_name)
479
+ assert len(truncated) == 28 # 25 + 3 for "..."
480
+ assert truncated.endswith("...")
481
+ assert truncated == "very_very_very_very_long_..." # 修正期望值
482
+
483
+ # 边界情况
484
+ name_29_chars = "a" * 29
485
+ truncated_29 = _profiler_wrapper._truncate_func_name(name_29_chars)
486
+ assert len(truncated_29) == 28
487
+ assert truncated_29 == "a" * 25 + "..."
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: bitool
3
+ Version: 0.1.2
4
+ Summary: Bitool - Rust + Python 混合架构工具集
5
+ Project-URL: Homepage, https://gitee.com/gooker_young/bitool
6
+ Project-URL: Issues, https://gitee.com/gooker_young/bitool/issues
7
+ Author-email: gooker_young <gooker_young@qq.com>
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: Implementation :: CPython
16
+ Classifier: Programming Language :: Rust
17
+ Requires-Python: >=3.8
18
+ Requires-Dist: psutil>=7.2.2
19
+ Requires-Dist: pymupdf>=1.24.11
20
+ Requires-Dist: rich>=14.3.4
21
+ Requires-Dist: tomli-w>=1.0.0
22
+ Requires-Dist: tomli>=2.4.1
23
+ Requires-Dist: typing-extensions>=4.13.2
24
+ Provides-Extra: build
25
+ Requires-Dist: maturin<2.0,>=1.0; extra == 'build'
26
+ Requires-Dist: pip>=25.0.1; extra == 'build'
27
+ Requires-Dist: prek>=0.3.3; extra == 'build'
28
+ Requires-Dist: ruff>=0.15.1; extra == 'build'
29
+ Requires-Dist: setuptools>=75.3.4; extra == 'build'
30
+ Requires-Dist: tox-uv>=1.13.1; extra == 'build'
31
+ Requires-Dist: tox>=4.24.1; extra == 'build'
32
+ Requires-Dist: wheel>=0.45.1; extra == 'build'
33
+ Provides-Extra: core
34
+ Provides-Extra: docs
35
+ Requires-Dist: sphinx-autobuild>=2021.3.14; extra == 'docs'
36
+ Requires-Dist: sphinx-rtd-theme>=1.5.2; extra == 'docs'
37
+ Provides-Extra: test
38
+ Requires-Dist: bandit>=1.7.10; extra == 'test'
39
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'test'
40
+ Requires-Dist: pytest-benchmark>=4.0.0; extra == 'test'
41
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'test'
42
+ Requires-Dist: pytest-html>=4.1.1; extra == 'test'
43
+ Requires-Dist: pytest-mock>=3.14.0; extra == 'test'
44
+ Requires-Dist: pytest-timeout>=2.4.0; extra == 'test'
45
+ Requires-Dist: pytest-xdist>=3.6.1; extra == 'test'
46
+ Requires-Dist: pytest>=8.3.4; extra == 'test'
47
+ Provides-Extra: typecheck
48
+ Requires-Dist: ty>=0.0.24; extra == 'typecheck'
49
+ Description-Content-Type: text/markdown
50
+
51
+ # Bitool
52
+
53
+ 基于 Maturin 的 Rust + Python 混合架构工具集,将性能瓶颈和共性部分使用 Rust 实现,接口和逻辑部分使用 Python 实现。
54
+
55
+ ## 特性
56
+
57
+ - **高性能**:核心计算和 I/O 操作使用 Rust 实现
58
+ - **易用性**:Python 提供简洁的 API 接口
59
+ - **可扩展**:插件系统支持功能扩展
60
+ - **跨平台**:支持 Windows、Linux、macOS
61
+
62
+ ## 安装
63
+
64
+ ```bash
65
+ pip install bitool
66
+ ```
67
+
68
+ ## 开发
69
+
70
+ ### 快速开始(推荐)
71
+
72
+ ```bash
73
+ # 安装开发环境(自动构建 Rust + Python 双包)
74
+ pymake ia
75
+
76
+ # 验证安装
77
+ pymake t
78
+ ```
79
+
80
+ ### 常用命令
81
+
82
+ ```bash
83
+ # 构建所有包(Rust + Python)
84
+ pymake ba
85
+
86
+ # 代码格式化与检查
87
+ pymake lint
88
+
89
+ # 运行测试
90
+ pymake t
91
+
92
+ # 清理所有构建产物
93
+ pymake ca
94
+
95
+ # 查看完整帮助
96
+ pymake help
97
+ ```
98
+
99
+ ### 手动构建(可选)
100
+
101
+ ```bash
102
+ # 构建 Rust 核心模块
103
+ cd bitool-core && maturin develop
104
+
105
+ # 构建 Python 主包
106
+ cd .. && pip install -e .
107
+
108
+ # 构建发布包
109
+ cd bitool-core && maturin build -r
110
+ cd .. && python -m build
111
+ ```
112
+
113
+ > 📖 详细文档:[PyMake 使用指南](docs/PYMAKE_GUIDE.md) | [双包构建指南](docs/DUAL_PACKAGE_BUILD.md)
114
+
115
+ ## 项目结构
116
+
117
+ ```bash
118
+ bitool/
119
+ ├── bitool-core/ # Rust 核心模块
120
+ │ ├── src/ # Rust 源代码
121
+ │ │ ├── core/ # 核心功能模块
122
+ │ │ ├── plugins/ # 插件系统核心
123
+ │ │ └── python/ # Python 绑定
124
+ │ ├── Cargo.toml # Rust 依赖配置
125
+ │ └── pyproject.toml # Maturin 构建配置
126
+ ├── src/bitool/ # Python 主包
127
+ │ ├── core/ # 核心业务逻辑
128
+ │ ├── cmd/ # 命令系统
129
+ │ ├── scripts/ # CLI 工具集
130
+ │ ├── utils/ # 工具模块
131
+ │ └── cli/ # 命令行入口
132
+ ├── docs/ # 项目文档
133
+ ├── pyproject.toml # Python 包配置
134
+ ├── Makefile # 构建脚本(可选)
135
+ └── build_all.py # Python 构建脚本
136
+ ```
137
+
138
+ ## 使用示例
139
+
140
+ ```python
141
+ from bitool import FileOps, TextProcessor
142
+
143
+ # 文件操作
144
+ file_ops = FileOps()
145
+ files = file_ops.scan_directory("/path/to/dir")
146
+
147
+ # 文本处理
148
+ processor = TextProcessor()
149
+ result = processor.regex_search(pattern, text)
150
+ ```
151
+
152
+ ## 许可证
153
+
154
+ MIT License
@@ -0,0 +1,51 @@
1
+ bitool/__init__.py,sha256=_BTVmBvdG90-R1OmF_sP_uWB3JnLNygYV5sztqAkUsY,706
2
+ bitool/consts.py,sha256=ylKOKSMZCyjtSkHZoa4XvJfgeVgwdcDaXLdn3omTh3s,497
3
+ bitool/types.py,sha256=O9Oj5CvSIfmZkMwazXTYWxIOMaO5E0LT4p-9_HHseaU,203
4
+ bitool/cmd/__init__.py,sha256=iaw0W9n2bIOXqLo7JhxNxJsMfo7nPoZkmfQvmeO5uIc,1640
5
+ bitool/cmd/_base.py,sha256=0PAcMm__o9PMuz5RPdqmbsC7B7NQtYFVtQZKMgmclhQ,3416
6
+ bitool/cmd/_condition.py,sha256=shXfkfPcs1QwY-tZEhtOVAjQfuwmK2CPci0TykgiZFs,1654
7
+ bitool/cmd/_scheduler.py,sha256=ndn0vXyB4cOQKBz6qm953M6JWCHAqzo-_pl3UG3O4jg,19816
8
+ bitool/cmd/env.py,sha256=986OP91TmTlhKMDf2okr2fP_ALUUB-ACALM6sF9K0Qc,15373
9
+ bitool/cmd/git.py,sha256=J8gvgEOq-hQk2PQAJ1lN9E-2YFlo-ZQoov1J781TAck,3461
10
+ bitool/cmd/io.py,sha256=GA0KF-UY8nKj-PJLv0iK6rKAfcCGCDnU0S4Ie2Bip4w,7716
11
+ bitool/cmd/pdf.py,sha256=noRg20dsduzXr84acAfQ72nN0wtI8HHVW-4Yf6pqFP8,12262
12
+ bitool/cmd/run.py,sha256=61fyZ77fYredwKLO5U6Y5-dvlhVEgFtv55kb1Aum_Ow,10454
13
+ bitool/cmd/toml.py,sha256=Ud02H7d3yEnNJQt5dVU7Uuf6hhXMUpWLZ9mWrSSHZp8,7249
14
+ bitool/cmd/version.py,sha256=cuPUu-Cz0ORKu3sVqHhWoRfCYZagzbhE_2PFlhtgBN4,21334
15
+ bitool/core/__init__.py,sha256=62w3qCwQ7ncMVUCTAFelcHEitXjOKLuHIYKpdB1K2hk,192
16
+ bitool/core/app.py,sha256=jcUTavzgL71c8h5rfsxKVNA67552jiUIvu1pVMi_M1Y,4406
17
+ bitool/core/commands.py,sha256=sxwq6Bac6nUan4uEU0hwtfYbkV2L_4kLjaJN2AsAcUw,5134
18
+ bitool/core/config.py,sha256=wwR08-EEYOtnodK_WB1txWkLKmqd4OJoX7XRT-N26YQ,23311
19
+ bitool/core/env.py,sha256=jkEUTaiJxXPCUJfjU_9ZXF6XVsRnYZMwAkoKL2J92Gs,324
20
+ bitool/core/logger.py,sha256=6JS5YezKVp8_2f3-mgGBqgCq8r5Fq6PoD8sM8ditl5c,8531
21
+ bitool/core/plugin.py,sha256=WZ2squ0gVSjLFwpLU9JKVLaZGf0B3K13HS1C2UtqKpg,3594
22
+ bitool/core/workspace.py,sha256=dhLg0IZtMOkQ0zfEBa6PDUwju9k_hx_U80b5N2KN-WU,1924
23
+ bitool/models/__init__.py,sha256=26KXRuM5SPWi_WJH_D4dJ1D5ri7yJty6LsC4sxYaP8g,113
24
+ bitool/models/version.py,sha256=vwm1dCLKFGW7KNU3scLU8RNPfvlI1FxRP1qaBgpnbis,5054
25
+ bitool/scripts/__init__.py,sha256=mzcYTQ2M2-KyXwrgbp2-j2vhCS6HTF26PyIJl6fS2lc,29
26
+ bitool/scripts/bumpversion.py,sha256=GiRUA4I6ucThrIIMDAzMd-evqExnoCbulnVPpUUAx1E,6107
27
+ bitool/scripts/clearscreen.py,sha256=Hp1WbburnSUTqJaIf7WtxufjwFFNY-9LiCJpBvPh0Ic,899
28
+ bitool/scripts/envpy.py,sha256=b7Qz3uBqgnfN-KHVF-03jvCCCdjBFko9w6cBRwj1yt0,6485
29
+ bitool/scripts/envrs.py,sha256=AgpaDc_0Aqp4nyZcaAnvyH6ECnuu7TYYETD7pVsfhRs,3487
30
+ bitool/scripts/filedate.py,sha256=_dPjFKSS5sbIJYQzfP-jRPKBTZJCAJhDEHq7ANOS1rw,7799
31
+ bitool/scripts/filelevel.py,sha256=jTOSvYC0xfYTPiWD5A1z7PtSBGio64qgqiE5DQ1zmJo,6181
32
+ bitool/scripts/gittool.py,sha256=hOSeNxxTfYQJh2m30Sq3Rz3N6qqQdPnw4nlaMw6YYRg,5494
33
+ bitool/scripts/img2pdf.py,sha256=m2jABrwAUShhzDym3CKDwom1stSn4CRiqFH_Zfsgmfc,4741
34
+ bitool/scripts/pdf2img.py,sha256=ew9cSfYmDuyz-qeAeRrzSU2CwUp7_AYna64CPNwcvJ0,4415
35
+ bitool/scripts/piptool.py,sha256=BXwIPSYqlPf2dPPdfajyIb43FR2xZ4sekwXkjaTtS1o,4062
36
+ bitool/scripts/pymake.py,sha256=XNW9Ny8fhX6fh6UMcyUlylxfdjc6D3qUHW4w-7tUzko,11401
37
+ bitool/scripts/sshcopyid.py,sha256=DdggEUD9GUBJXEpXbKPBEcqL7QZEli5BEXtn9LU9aHw,15524
38
+ bitool/scripts/taskkill.py,sha256=6jp5M0ZDB__zxD9fLWp0DxyFaGlM-PjUfwSpBfEAmH4,12260
39
+ bitool/scripts/which.py,sha256=N7PP0bh2PpF41XcrKkIQ6YIhH-PwOit_Fmo2Ayf49J8,5285
40
+ bitool/utils/__init__.py,sha256=32lxkAm_Cnjji36ncMijzo4MRfoFbDpCblNfzZ_zXJs,188
41
+ bitool/utils/cli_parser.py,sha256=XiZZi1kTPnqlv0dzo1q-CXaUnoh0oZgmNZm1sS2Iai4,13693
42
+ bitool/utils/executor.py,sha256=ErPqApPlCR7a1VKfJ1Qlf2YZ-d3E-KcysGUhzPfCRlo,30277
43
+ bitool/utils/profiler.py,sha256=v9rKKsqgG1DYcH3D9sx_EasJzJZnAfzJIlNUWlIPV1M,13368
44
+ bitool/utils/task.py,sha256=garkjLmcQnGfDsLIki_g5oAhKCwBcFQ6mFd_qveQ75s,3579
45
+ bitool/utils/task_group.py,sha256=Ri5JCXOeaMlckhoucIvjNYB2OXLgfbHdWhnIloRqpB4,23581
46
+ bitool/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ bitool/utils/tests/test_profiler.py,sha256=oGeRO0L4z7_aD5AzvtzSJFTLiQPKsyZ2ZnccHnyKHtA,17160
48
+ bitool-0.1.2.dist-info/METADATA,sha256=e18xbVLf4WvtAAfESiX1asEe2TyeudYkDZFssh44xzw,4465
49
+ bitool-0.1.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
50
+ bitool-0.1.2.dist-info/entry_points.txt,sha256=Ulq4kMZtxfqYti8h3J11WKvyp01JK8CGFRkTdH-ZnRM,545
51
+ bitool-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any