maque 0.2.1__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.
- maque/__init__.py +30 -0
- maque/__main__.py +926 -0
- maque/ai_platform/__init__.py +0 -0
- maque/ai_platform/crawl.py +45 -0
- maque/ai_platform/metrics.py +258 -0
- maque/ai_platform/nlp_preprocess.py +67 -0
- maque/ai_platform/webpage_screen_shot.py +195 -0
- maque/algorithms/__init__.py +78 -0
- maque/algorithms/bezier.py +15 -0
- maque/algorithms/bktree.py +117 -0
- maque/algorithms/core.py +104 -0
- maque/algorithms/hilbert.py +16 -0
- maque/algorithms/rate_function.py +92 -0
- maque/algorithms/transform.py +27 -0
- maque/algorithms/trie.py +272 -0
- maque/algorithms/utils.py +63 -0
- maque/algorithms/video.py +587 -0
- maque/api/__init__.py +1 -0
- maque/api/common.py +110 -0
- maque/api/fetch.py +26 -0
- maque/api/static/icon.png +0 -0
- maque/api/static/redoc.standalone.js +1782 -0
- maque/api/static/swagger-ui-bundle.js +3 -0
- maque/api/static/swagger-ui.css +3 -0
- maque/cli/__init__.py +1 -0
- maque/cli/clean_invisible_chars.py +324 -0
- maque/cli/core.py +34 -0
- maque/cli/groups/__init__.py +26 -0
- maque/cli/groups/config.py +205 -0
- maque/cli/groups/data.py +615 -0
- maque/cli/groups/doctor.py +259 -0
- maque/cli/groups/embedding.py +222 -0
- maque/cli/groups/git.py +29 -0
- maque/cli/groups/help.py +410 -0
- maque/cli/groups/llm.py +223 -0
- maque/cli/groups/mcp.py +241 -0
- maque/cli/groups/mllm.py +1795 -0
- maque/cli/groups/mllm_simple.py +60 -0
- maque/cli/groups/quant.py +210 -0
- maque/cli/groups/service.py +490 -0
- maque/cli/groups/system.py +570 -0
- maque/cli/mllm_run.py +1451 -0
- maque/cli/script.py +52 -0
- maque/cli/tree.py +49 -0
- maque/clustering/__init__.py +52 -0
- maque/clustering/analyzer.py +347 -0
- maque/clustering/clusterers.py +464 -0
- maque/clustering/sampler.py +134 -0
- maque/clustering/visualizer.py +205 -0
- maque/constant.py +13 -0
- maque/core.py +133 -0
- maque/cv/__init__.py +1 -0
- maque/cv/image.py +219 -0
- maque/cv/utils.py +68 -0
- maque/cv/video/__init__.py +3 -0
- maque/cv/video/keyframe_extractor.py +368 -0
- maque/embedding/__init__.py +43 -0
- maque/embedding/base.py +56 -0
- maque/embedding/multimodal.py +308 -0
- maque/embedding/server.py +523 -0
- maque/embedding/text.py +311 -0
- maque/git/__init__.py +24 -0
- maque/git/pure_git.py +912 -0
- maque/io/__init__.py +29 -0
- maque/io/core.py +38 -0
- maque/io/ops.py +194 -0
- maque/llm/__init__.py +111 -0
- maque/llm/backend.py +416 -0
- maque/llm/base.py +411 -0
- maque/llm/server.py +366 -0
- maque/mcp_server.py +1096 -0
- maque/mllm_data_processor_pipeline/__init__.py +17 -0
- maque/mllm_data_processor_pipeline/core.py +341 -0
- maque/mllm_data_processor_pipeline/example.py +291 -0
- maque/mllm_data_processor_pipeline/steps/__init__.py +56 -0
- maque/mllm_data_processor_pipeline/steps/data_alignment.py +267 -0
- maque/mllm_data_processor_pipeline/steps/data_loader.py +172 -0
- maque/mllm_data_processor_pipeline/steps/data_validation.py +304 -0
- maque/mllm_data_processor_pipeline/steps/format_conversion.py +411 -0
- maque/mllm_data_processor_pipeline/steps/mllm_annotation.py +331 -0
- maque/mllm_data_processor_pipeline/steps/mllm_refinement.py +446 -0
- maque/mllm_data_processor_pipeline/steps/result_validation.py +501 -0
- maque/mllm_data_processor_pipeline/web_app.py +317 -0
- maque/nlp/__init__.py +14 -0
- maque/nlp/ngram.py +9 -0
- maque/nlp/parser.py +63 -0
- maque/nlp/risk_matcher.py +543 -0
- maque/nlp/sentence_splitter.py +202 -0
- maque/nlp/simple_tradition_cvt.py +31 -0
- maque/performance/__init__.py +21 -0
- maque/performance/_measure_time.py +70 -0
- maque/performance/_profiler.py +367 -0
- maque/performance/_stat_memory.py +51 -0
- maque/pipelines/__init__.py +15 -0
- maque/pipelines/clustering.py +252 -0
- maque/quantization/__init__.py +42 -0
- maque/quantization/auto_round.py +120 -0
- maque/quantization/base.py +145 -0
- maque/quantization/bitsandbytes.py +127 -0
- maque/quantization/llm_compressor.py +102 -0
- maque/retriever/__init__.py +35 -0
- maque/retriever/chroma.py +654 -0
- maque/retriever/document.py +140 -0
- maque/retriever/milvus.py +1140 -0
- maque/table_ops/__init__.py +1 -0
- maque/table_ops/core.py +133 -0
- maque/table_viewer/__init__.py +4 -0
- maque/table_viewer/download_assets.py +57 -0
- maque/table_viewer/server.py +698 -0
- maque/table_viewer/static/element-plus-icons.js +5791 -0
- maque/table_viewer/static/element-plus.css +1 -0
- maque/table_viewer/static/element-plus.js +65236 -0
- maque/table_viewer/static/main.css +268 -0
- maque/table_viewer/static/main.js +669 -0
- maque/table_viewer/static/vue.global.js +18227 -0
- maque/table_viewer/templates/index.html +401 -0
- maque/utils/__init__.py +56 -0
- maque/utils/color.py +68 -0
- maque/utils/color_string.py +45 -0
- maque/utils/compress.py +66 -0
- maque/utils/constant.py +183 -0
- maque/utils/core.py +261 -0
- maque/utils/cursor.py +143 -0
- maque/utils/distance.py +58 -0
- maque/utils/docker.py +96 -0
- maque/utils/downloads.py +51 -0
- maque/utils/excel_helper.py +542 -0
- maque/utils/helper_metrics.py +121 -0
- maque/utils/helper_parser.py +168 -0
- maque/utils/net.py +64 -0
- maque/utils/nvidia_stat.py +140 -0
- maque/utils/ops.py +53 -0
- maque/utils/packages.py +31 -0
- maque/utils/path.py +57 -0
- maque/utils/tar.py +260 -0
- maque/utils/untar.py +129 -0
- maque/web/__init__.py +0 -0
- maque/web/image_downloader.py +1410 -0
- maque-0.2.1.dist-info/METADATA +450 -0
- maque-0.2.1.dist-info/RECORD +143 -0
- maque-0.2.1.dist-info/WHEEL +4 -0
- maque-0.2.1.dist-info/entry_points.txt +3 -0
- maque-0.2.1.dist-info/licenses/LICENSE +21 -0
maque/utils/tar.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
"""
|
|
4
|
+
Optimized Parallel Tar with stdin piping, compression support, and timing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
import argparse
|
|
10
|
+
import shutil
|
|
11
|
+
import sys
|
|
12
|
+
import time # 导入 time 模块用于计时
|
|
13
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def check_command(cmd):
|
|
17
|
+
"""检查指定的命令是否存在于系统的PATH中。"""
|
|
18
|
+
if shutil.which(cmd) is None:
|
|
19
|
+
print(f"错误: 必需的命令 '{cmd}' 未找到。", file=sys.stderr)
|
|
20
|
+
print(f"请确保 '{cmd}' 已安装并且在您的系统PATH中。", file=sys.stderr)
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parse_size_to_bytes(size_str):
|
|
25
|
+
"""将带有单位的尺寸字符串 (如 '5G', '100M') 解析为字节。"""
|
|
26
|
+
size_str = size_str.strip().upper()
|
|
27
|
+
units = {"K": 1024, "M": 1024**2, "G": 1024**3, "T": 1024**4}
|
|
28
|
+
try:
|
|
29
|
+
if size_str and size_str[-1] in units:
|
|
30
|
+
num = float(size_str[:-1])
|
|
31
|
+
unit = size_str[-1]
|
|
32
|
+
return int(num * units[unit])
|
|
33
|
+
else:
|
|
34
|
+
return int(size_str)
|
|
35
|
+
except (ValueError, TypeError):
|
|
36
|
+
print(f"错误: 无法解析尺寸字符串 '{size_str}'。", file=sys.stderr)
|
|
37
|
+
sys.exit(1)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run_tar_job(job_info):
|
|
41
|
+
"""
|
|
42
|
+
为单个文件块执行 tar 命令。
|
|
43
|
+
此版本通过管道将文件列表传递给 tar 的 stdin,避免了临时文件。
|
|
44
|
+
"""
|
|
45
|
+
file_list, archive_path, source_dir, job_id, compression_args = job_info
|
|
46
|
+
|
|
47
|
+
# 基本命令: -c (创建), --no-recursion (不递归), -f (指定文件), -C (切换目录), -T - (从stdin读取列表)
|
|
48
|
+
tar_cmd = (
|
|
49
|
+
["tar"]
|
|
50
|
+
+ compression_args
|
|
51
|
+
+ ["-cf", archive_path, "-C", source_dir, "--no-recursion", "-T", "-"]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# 将文件列表(Python列表)转换为 tar -T- 所需的格式(换行符分隔的字符串)
|
|
55
|
+
# 必须编码为字节串才能传递给 stdin
|
|
56
|
+
files_to_pipe = "\n".join(file_list).encode("utf-8")
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# 使用 Popen 以便我们可以访问 stdin
|
|
60
|
+
process = subprocess.Popen(
|
|
61
|
+
tar_cmd,
|
|
62
|
+
stdin=subprocess.PIPE,
|
|
63
|
+
stdout=subprocess.PIPE,
|
|
64
|
+
stderr=subprocess.PIPE,
|
|
65
|
+
)
|
|
66
|
+
# 将文件列表写入 stdin 并关闭它,然后等待进程完成
|
|
67
|
+
stdout, stderr = process.communicate(input=files_to_pipe)
|
|
68
|
+
|
|
69
|
+
if process.returncode != 0:
|
|
70
|
+
# 如果 tar 命令失败,则格式化错误信息
|
|
71
|
+
raise subprocess.CalledProcessError(
|
|
72
|
+
returncode=process.returncode, cmd=tar_cmd, stderr=stderr
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return f"任务 {job_id} ({os.path.basename(archive_path)}) 成功。"
|
|
76
|
+
except subprocess.CalledProcessError as e:
|
|
77
|
+
error_message = (
|
|
78
|
+
f"错误: 任务 {job_id} ({os.path.basename(archive_path)}) 失败。\n"
|
|
79
|
+
)
|
|
80
|
+
# stderr 是字节串,需要解码
|
|
81
|
+
error_message += (
|
|
82
|
+
f"错误信息: {e.stderr.decode('utf-8', errors='ignore').strip()}"
|
|
83
|
+
)
|
|
84
|
+
return error_message
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return f"错误: 任务 {job_id} 发生意外异常: {e}"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def archive_by_size(
|
|
90
|
+
source_dir, dest_dir, max_size_str, parallel_jobs, compression, compression_level
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
根据文件大小将源目录打包,并使用线程池控制并行度。
|
|
94
|
+
"""
|
|
95
|
+
# --- 新增:记录开始时间 ---
|
|
96
|
+
start_time = time.time()
|
|
97
|
+
|
|
98
|
+
# --- 1. 检查和准备 ---
|
|
99
|
+
print("--- 步骤 1: 检查与准备 ---")
|
|
100
|
+
check_command("tar")
|
|
101
|
+
|
|
102
|
+
source_dir = os.path.abspath(source_dir)
|
|
103
|
+
dest_dir = os.path.abspath(dest_dir)
|
|
104
|
+
max_size_bytes = parse_size_to_bytes(max_size_str)
|
|
105
|
+
|
|
106
|
+
if not os.path.isdir(source_dir):
|
|
107
|
+
print(f"错误: 源目录 '{source_dir}' 不存在。", file=sys.stderr)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
111
|
+
|
|
112
|
+
# 根据压缩选项确定文件扩展名和 tar 参数
|
|
113
|
+
compression_map = {
|
|
114
|
+
"none": (".tar", []),
|
|
115
|
+
"gzip": (".tar.gz", ["-z"]),
|
|
116
|
+
"zstd": (
|
|
117
|
+
".tar.zst",
|
|
118
|
+
["--zstd", f"--compress-program=zstd -{compression_level}"],
|
|
119
|
+
),
|
|
120
|
+
}
|
|
121
|
+
if compression not in compression_map:
|
|
122
|
+
print(f"错误: 不支持的压缩格式 '{compression}'。", file=sys.stderr)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
archive_ext, compression_args = compression_map[compression]
|
|
126
|
+
|
|
127
|
+
# --- 2. 扫描文件并按大小分组 ---
|
|
128
|
+
print(f"--- 步骤 2: 扫描文件并按最大 {max_size_str} 每包进行分组 ---")
|
|
129
|
+
job_definitions = []
|
|
130
|
+
current_chunk_files = []
|
|
131
|
+
current_chunk_size = 0
|
|
132
|
+
part_num = 1
|
|
133
|
+
|
|
134
|
+
# 使用 os.walk 遍历目录
|
|
135
|
+
for root, _, files in os.walk(source_dir):
|
|
136
|
+
for filename in files:
|
|
137
|
+
full_path = os.path.join(root, filename)
|
|
138
|
+
relative_path = os.path.relpath(full_path, source_dir)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
if not os.path.islink(full_path):
|
|
142
|
+
file_size = os.path.getsize(full_path)
|
|
143
|
+
else:
|
|
144
|
+
continue
|
|
145
|
+
except OSError as e:
|
|
146
|
+
print(f"警告: 无法访问文件 {full_path},已跳过。错误: {e}")
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
if current_chunk_files and (
|
|
150
|
+
current_chunk_size + file_size > max_size_bytes
|
|
151
|
+
):
|
|
152
|
+
archive_name = f"archive_part_{part_num:04d}{archive_ext}"
|
|
153
|
+
archive_path = os.path.join(dest_dir, archive_name)
|
|
154
|
+
# 直接将文件列表添加到任务定义中,而不是写入文件
|
|
155
|
+
job_definitions.append(
|
|
156
|
+
(
|
|
157
|
+
list(current_chunk_files),
|
|
158
|
+
archive_path,
|
|
159
|
+
source_dir,
|
|
160
|
+
part_num,
|
|
161
|
+
compression_args,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
part_num += 1
|
|
166
|
+
current_chunk_files = []
|
|
167
|
+
current_chunk_size = 0
|
|
168
|
+
|
|
169
|
+
current_chunk_files.append(relative_path)
|
|
170
|
+
current_chunk_size += file_size
|
|
171
|
+
|
|
172
|
+
if current_chunk_files:
|
|
173
|
+
archive_name = f"archive_part_{part_num:04d}{archive_ext}"
|
|
174
|
+
archive_path = os.path.join(dest_dir, archive_name)
|
|
175
|
+
job_definitions.append(
|
|
176
|
+
(
|
|
177
|
+
list(current_chunk_files),
|
|
178
|
+
archive_path,
|
|
179
|
+
source_dir,
|
|
180
|
+
part_num,
|
|
181
|
+
compression_args,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if not job_definitions:
|
|
186
|
+
print("警告: 源目录中没有文件可打包。")
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
print(f"分组完成,总共将生成 {len(job_definitions)} 个归档包。")
|
|
190
|
+
|
|
191
|
+
# --- 3. 使用线程池并行执行打包 ---
|
|
192
|
+
print(f"\n--- 步骤 3: 并行启动打包任务 (最多 {parallel_jobs} 个任务同时运行) ---")
|
|
193
|
+
with ThreadPoolExecutor(max_workers=parallel_jobs) as executor:
|
|
194
|
+
futures = [executor.submit(run_tar_job, job) for job in job_definitions]
|
|
195
|
+
for future in as_completed(futures):
|
|
196
|
+
result = future.result()
|
|
197
|
+
print(f" -> {result}")
|
|
198
|
+
|
|
199
|
+
print("\n----------------------------------------")
|
|
200
|
+
print("所有打包任务已成功完成!")
|
|
201
|
+
print(f"归档文件位于: {dest_dir}")
|
|
202
|
+
|
|
203
|
+
# --- 新增:计算并打印总耗时 ---
|
|
204
|
+
end_time = time.time()
|
|
205
|
+
duration = end_time - start_time
|
|
206
|
+
print(f"总耗时: {duration:.2f} 秒。")
|
|
207
|
+
print("----------------------------------------")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
parser = argparse.ArgumentParser(
|
|
212
|
+
description="根据指定的最大文件大小,快速、并行地打包一个文件夹。此版本通过管道向tar传递文件列表以提高性能,并支持压缩。",
|
|
213
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
214
|
+
)
|
|
215
|
+
parser.add_argument("source_dir", type=str, help="要打包的源文件夹路径。")
|
|
216
|
+
parser.add_argument("dest_dir", type=str, help="用于存放打包文件的目标文件夹。")
|
|
217
|
+
parser.add_argument(
|
|
218
|
+
"-s",
|
|
219
|
+
"--max-size",
|
|
220
|
+
type=str,
|
|
221
|
+
default="5G",
|
|
222
|
+
help="每个归档包内容物的最大预估大小。\n支持单位: K, M, G, T (例如: '2G', '500M')。默认为 '5G'。",
|
|
223
|
+
)
|
|
224
|
+
parser.add_argument(
|
|
225
|
+
"-j",
|
|
226
|
+
"--jobs",
|
|
227
|
+
type=int,
|
|
228
|
+
default=os.cpu_count() or 8,
|
|
229
|
+
help="并行的任务数量 (即同时运行多少个 tar 进程)。默认为系统的CPU核心数。",
|
|
230
|
+
)
|
|
231
|
+
parser.add_argument(
|
|
232
|
+
"-c",
|
|
233
|
+
"--compression",
|
|
234
|
+
type=str,
|
|
235
|
+
default="none",
|
|
236
|
+
choices=["none", "gzip", "zstd"],
|
|
237
|
+
help="选择压缩算法。\n"
|
|
238
|
+
" - none: 不压缩,速度最快,文件最大 (.tar)\n"
|
|
239
|
+
" - gzip: 通用压缩,兼容性好 (.tar.gz)\n"
|
|
240
|
+
" - zstd: 现代高效压缩,速度和压缩率俱佳 (.tar.zst)\n"
|
|
241
|
+
"默认为 'none'。",
|
|
242
|
+
)
|
|
243
|
+
parser.add_argument(
|
|
244
|
+
"--level",
|
|
245
|
+
dest="compression_level",
|
|
246
|
+
type=int,
|
|
247
|
+
default=3,
|
|
248
|
+
help="压缩级别 (仅对 zstd 有效)。范围 1-19。\n"
|
|
249
|
+
"较低的级别速度更快,压缩率较低。默认为 3。",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
args = parser.parse_args()
|
|
253
|
+
archive_by_size(
|
|
254
|
+
args.source_dir,
|
|
255
|
+
args.dest_dir,
|
|
256
|
+
args.max_size,
|
|
257
|
+
args.jobs,
|
|
258
|
+
args.compression,
|
|
259
|
+
args.compression_level,
|
|
260
|
+
)
|
maque/utils/untar.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
"""
|
|
4
|
+
parallel_untar.py
|
|
5
|
+
Unpacks various tar archives (.tar, .tar.gz, .tar.zst) in parallel.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import argparse
|
|
11
|
+
import shutil
|
|
12
|
+
import sys
|
|
13
|
+
import glob
|
|
14
|
+
import time
|
|
15
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def check_command(cmd):
|
|
19
|
+
"""检查指定的命令是否存在于系统的PATH中。"""
|
|
20
|
+
if shutil.which(cmd) is None:
|
|
21
|
+
print(f"错误: 必需的命令 '{cmd}' 未找到。", file=sys.stderr)
|
|
22
|
+
print(f"请确保 '{cmd}' 已安装并且在您的系统PATH中。", file=sys.stderr)
|
|
23
|
+
sys.exit(1)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_untar_job(archive_path, dest_dir):
|
|
27
|
+
"""
|
|
28
|
+
为单个归档文件执行 'tar -xf' 解压命令。
|
|
29
|
+
现代 tar 会自动检测压缩格式 (gzip, zstd, etc.)。
|
|
30
|
+
"""
|
|
31
|
+
job_name = os.path.basename(archive_path)
|
|
32
|
+
# -x: 提取 (extract)
|
|
33
|
+
# -f: 指定文件 (file)
|
|
34
|
+
# -C: 指定目标目录 (Change directory)
|
|
35
|
+
tar_cmd = ["tar", "-xf", archive_path, "-C", dest_dir]
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
subprocess.run(
|
|
39
|
+
tar_cmd, check=True, capture_output=True, text=True, encoding="utf-8"
|
|
40
|
+
)
|
|
41
|
+
return f"文件 '{job_name}' 解压成功。"
|
|
42
|
+
except subprocess.CalledProcessError as e:
|
|
43
|
+
error_message = f"错误: 解压 '{job_name}' 失败。\n"
|
|
44
|
+
error_message += f"错误信息: {e.stderr.strip()}"
|
|
45
|
+
return error_message
|
|
46
|
+
except Exception as e:
|
|
47
|
+
return f"错误: 解压 '{job_name}' 时发生意外异常: {e}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parallel_unpack(source_dir, dest_dir, parallel_jobs):
|
|
51
|
+
"""
|
|
52
|
+
并行地将源目录中的所有 .tar, .tar.gz, .tar.zst 文件解压到目标目录。
|
|
53
|
+
"""
|
|
54
|
+
# --- 新增:记录开始时间 ---
|
|
55
|
+
start_time = time.time()
|
|
56
|
+
|
|
57
|
+
# --- 1. 检查和准备 ---
|
|
58
|
+
print("--- 步骤 1: 检查与准备 ---")
|
|
59
|
+
check_command("tar")
|
|
60
|
+
|
|
61
|
+
source_dir = os.path.abspath(source_dir)
|
|
62
|
+
dest_dir = os.path.abspath(dest_dir)
|
|
63
|
+
|
|
64
|
+
if not os.path.isdir(source_dir):
|
|
65
|
+
print(f"错误: 源目录 '{source_dir}' 不存在或不是一个目录。", file=sys.stderr)
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# 创建目标目录,如果不存在的话
|
|
69
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
70
|
+
|
|
71
|
+
# --- 2. 查找所有要解压的归档文件 ---
|
|
72
|
+
print(f"--- 步骤 2: 在 '{source_dir}' 中查找归档文件 ---")
|
|
73
|
+
|
|
74
|
+
# --- 已修改:查找多种压缩格式 ---
|
|
75
|
+
patterns = ["*.tar", "*.tar.gz", "*.tar.zst"]
|
|
76
|
+
archive_files = []
|
|
77
|
+
for pattern in patterns:
|
|
78
|
+
archive_files.extend(glob.glob(os.path.join(source_dir, pattern)))
|
|
79
|
+
|
|
80
|
+
if not archive_files:
|
|
81
|
+
print(f"在 '{source_dir}' 中未找到任何支持的归档文件 ({', '.join(patterns)})。")
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
print(f"找到 {len(archive_files)} 个归档文件,准备解压。")
|
|
85
|
+
|
|
86
|
+
# --- 3. 使用线程池并行执行解压 ---
|
|
87
|
+
print(f"\n--- 步骤 3: 并行启动解压任务 (最多 {parallel_jobs} 个任务同时运行) ---")
|
|
88
|
+
|
|
89
|
+
with ThreadPoolExecutor(max_workers=parallel_jobs) as executor:
|
|
90
|
+
# 提交所有解压任务
|
|
91
|
+
futures = {
|
|
92
|
+
executor.submit(run_untar_job, archive, dest_dir): archive
|
|
93
|
+
for archive in archive_files
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# 等待任务完成并打印结果
|
|
97
|
+
for future in as_completed(futures):
|
|
98
|
+
result = future.result()
|
|
99
|
+
print(f" -> {result}")
|
|
100
|
+
|
|
101
|
+
print("\n----------------------------------------")
|
|
102
|
+
print("所有解压任务已完成!")
|
|
103
|
+
print(f"文件已解压至: {dest_dir}")
|
|
104
|
+
|
|
105
|
+
# --- 新增:计算并打印总耗时 ---
|
|
106
|
+
end_time = time.time()
|
|
107
|
+
duration = end_time - start_time
|
|
108
|
+
print(f"总耗时: {duration:.2f} 秒。")
|
|
109
|
+
print("----------------------------------------")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
parser = argparse.ArgumentParser(
|
|
114
|
+
description="并行地将一个目录中的所有 .tar, .tar.gz, .tar.zst 归档文件解压到目标位置。",
|
|
115
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
116
|
+
)
|
|
117
|
+
parser.add_argument("source_dir", type=str, help="包含归档文件的源文件夹路径。")
|
|
118
|
+
parser.add_argument("dest_dir", type=str, help="用于存放解压后文件的目标文件夹。")
|
|
119
|
+
parser.add_argument(
|
|
120
|
+
"-j",
|
|
121
|
+
"--jobs",
|
|
122
|
+
type=int,
|
|
123
|
+
default=os.cpu_count() or 4,
|
|
124
|
+
help="并行的解压任务数量 (即同时运行多少个 tar 进程)。\n"
|
|
125
|
+
"默认为系统的CPU核心数。",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
args = parser.parse_args()
|
|
129
|
+
parallel_unpack(args.source_dir, args.dest_dir, args.jobs)
|
maque/web/__init__.py
ADDED
|
File without changes
|