mectl 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.
- mectl-0.1.0/PKG-INFO +124 -0
- mectl-0.1.0/README.md +107 -0
- mectl-0.1.0/pyproject.toml +34 -0
- mectl-0.1.0/src/__init__.py +5 -0
- mectl-0.1.0/src/cli.py +179 -0
mectl-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mectl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A command line tool for downloading files from MinIO
|
|
5
|
+
Author: luxu1220
|
|
6
|
+
Author-email: luxu1220@gmail.com
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: click (>=8.0.0)
|
|
15
|
+
Requires-Dist: minio (>=7.0.0)
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# mectl
|
|
19
|
+
|
|
20
|
+
`mectl` 是一个命令行工具,用于辅助mindedge的模型开发,例如从 MinIO 服务器下载文件。
|
|
21
|
+
|
|
22
|
+
## 功能特性
|
|
23
|
+
|
|
24
|
+
- 通过命令行从 MinIO 服务器下载文件
|
|
25
|
+
- 支持通过配置文件或环境变量设置连接参数
|
|
26
|
+
- 灵活的输出路径配置
|
|
27
|
+
- 支持指定存储桶名称
|
|
28
|
+
- 自动从文件路径提取文件名
|
|
29
|
+
- 自动检测并解压ZIP文件
|
|
30
|
+
|
|
31
|
+
## 安装
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 使用 pip 安装
|
|
35
|
+
pip install mectl
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 使用方法
|
|
39
|
+
|
|
40
|
+
### 基本用法
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
mectl get [OPTIONS] FILE_KEY
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 参数说明
|
|
47
|
+
|
|
48
|
+
- `FILE_KEY`: MinIO 对象路径,格式:`bucket_name/object_key`
|
|
49
|
+
- `-o, --output`: 本地保存路径(默认为从 FILE_KEY 提取的文件名)
|
|
50
|
+
- `-c, --config`: 配置文件路径(JSON,含 oss.accessKey 等)
|
|
51
|
+
- `-b, --bucket`: MinIO 存储桶名称(默认为 `suanpan`)
|
|
52
|
+
|
|
53
|
+
### 配置
|
|
54
|
+
|
|
55
|
+
工具需要通过配置文件或环境变量提供 MinIO 连接信息。
|
|
56
|
+
|
|
57
|
+
#### 配置文件示例 (config.json)
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"oss": {
|
|
62
|
+
"internalEndpoint": "your-minio-server.com:9000",
|
|
63
|
+
"accessKey": "your-access-key",
|
|
64
|
+
"accessSecret": "your-access-secret"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### 环境变量方式
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
export NODE_CONFIG='{"oss":{"internalEndpoint":"your-minio-server.com:9000","accessKey":"your-access-key","accessSecret":"your-access-secret"}}'
|
|
73
|
+
mectl get bucket-name/file-path
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 使用示例
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# 使用配置文件下载文件
|
|
80
|
+
mectl get my-file.txt -c config.json -o ./local-file.txt
|
|
81
|
+
|
|
82
|
+
# 使用环境变量下载文件(自动提取文件名)
|
|
83
|
+
mectl get studio/100029/models/MindEdge.zip -c config.json
|
|
84
|
+
|
|
85
|
+
# 指定特定存储桶
|
|
86
|
+
mectl get my-file.txt -b another-bucket -c config.json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 开发
|
|
90
|
+
|
|
91
|
+
### 本地运行
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 克隆项目
|
|
95
|
+
git clone <repository-url>
|
|
96
|
+
|
|
97
|
+
# 安装依赖
|
|
98
|
+
cd mectl
|
|
99
|
+
pip install -e .
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 发布到 PyPI
|
|
103
|
+
|
|
104
|
+
要发布新版本到 PyPI,请执行以下步骤:
|
|
105
|
+
|
|
106
|
+
1. 更新 [pyproject.toml](file:///home/luxu/project/mindedge-model-tool/pyproject.toml) 中的版本号
|
|
107
|
+
2. 运行测试确保一切正常
|
|
108
|
+
3. 使用发布脚本发布:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 运行发布脚本
|
|
112
|
+
./release.sh
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
发布脚本将:
|
|
116
|
+
- 检查环境和依赖
|
|
117
|
+
- 构建包
|
|
118
|
+
- 询问发布目标(测试PyPI或正式PyPI)
|
|
119
|
+
- 执行发布
|
|
120
|
+
|
|
121
|
+
## 依赖
|
|
122
|
+
|
|
123
|
+
- Python >= 3.10
|
|
124
|
+
- minio >= 7.0.0
|
mectl-0.1.0/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# mectl
|
|
2
|
+
|
|
3
|
+
`mectl` 是一个命令行工具,用于辅助mindedge的模型开发,例如从 MinIO 服务器下载文件。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 通过命令行从 MinIO 服务器下载文件
|
|
8
|
+
- 支持通过配置文件或环境变量设置连接参数
|
|
9
|
+
- 灵活的输出路径配置
|
|
10
|
+
- 支持指定存储桶名称
|
|
11
|
+
- 自动从文件路径提取文件名
|
|
12
|
+
- 自动检测并解压ZIP文件
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 使用 pip 安装
|
|
18
|
+
pip install mectl
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用方法
|
|
22
|
+
|
|
23
|
+
### 基本用法
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mectl get [OPTIONS] FILE_KEY
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 参数说明
|
|
30
|
+
|
|
31
|
+
- `FILE_KEY`: MinIO 对象路径,格式:`bucket_name/object_key`
|
|
32
|
+
- `-o, --output`: 本地保存路径(默认为从 FILE_KEY 提取的文件名)
|
|
33
|
+
- `-c, --config`: 配置文件路径(JSON,含 oss.accessKey 等)
|
|
34
|
+
- `-b, --bucket`: MinIO 存储桶名称(默认为 `suanpan`)
|
|
35
|
+
|
|
36
|
+
### 配置
|
|
37
|
+
|
|
38
|
+
工具需要通过配置文件或环境变量提供 MinIO 连接信息。
|
|
39
|
+
|
|
40
|
+
#### 配置文件示例 (config.json)
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"oss": {
|
|
45
|
+
"internalEndpoint": "your-minio-server.com:9000",
|
|
46
|
+
"accessKey": "your-access-key",
|
|
47
|
+
"accessSecret": "your-access-secret"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### 环境变量方式
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
export NODE_CONFIG='{"oss":{"internalEndpoint":"your-minio-server.com:9000","accessKey":"your-access-key","accessSecret":"your-access-secret"}}'
|
|
56
|
+
mectl get bucket-name/file-path
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 使用示例
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 使用配置文件下载文件
|
|
63
|
+
mectl get my-file.txt -c config.json -o ./local-file.txt
|
|
64
|
+
|
|
65
|
+
# 使用环境变量下载文件(自动提取文件名)
|
|
66
|
+
mectl get studio/100029/models/MindEdge.zip -c config.json
|
|
67
|
+
|
|
68
|
+
# 指定特定存储桶
|
|
69
|
+
mectl get my-file.txt -b another-bucket -c config.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 开发
|
|
73
|
+
|
|
74
|
+
### 本地运行
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# 克隆项目
|
|
78
|
+
git clone <repository-url>
|
|
79
|
+
|
|
80
|
+
# 安装依赖
|
|
81
|
+
cd mectl
|
|
82
|
+
pip install -e .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 发布到 PyPI
|
|
86
|
+
|
|
87
|
+
要发布新版本到 PyPI,请执行以下步骤:
|
|
88
|
+
|
|
89
|
+
1. 更新 [pyproject.toml](file:///home/luxu/project/mindedge-model-tool/pyproject.toml) 中的版本号
|
|
90
|
+
2. 运行测试确保一切正常
|
|
91
|
+
3. 使用发布脚本发布:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 运行发布脚本
|
|
95
|
+
./release.sh
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
发布脚本将:
|
|
99
|
+
- 检查环境和依赖
|
|
100
|
+
- 构建包
|
|
101
|
+
- 询问发布目标(测试PyPI或正式PyPI)
|
|
102
|
+
- 执行发布
|
|
103
|
+
|
|
104
|
+
## 依赖
|
|
105
|
+
|
|
106
|
+
- Python >= 3.10
|
|
107
|
+
- minio >= 7.0.0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mectl"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A command line tool for downloading files from MinIO"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "luxu1220",email = "luxu1220@gmail.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"minio>=7.0.0",
|
|
12
|
+
"click>=8.0.0"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
mectl = "src.cli:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
20
|
+
build-backend = "poetry.core.masonry.api"
|
|
21
|
+
|
|
22
|
+
[tool.poetry]
|
|
23
|
+
name = "mectl"
|
|
24
|
+
version = "0.1.0"
|
|
25
|
+
description = "A command line tool for downloading files from MinIO"
|
|
26
|
+
packages = [{include = "src"}]
|
|
27
|
+
|
|
28
|
+
[tool.poetry.dependencies]
|
|
29
|
+
python = "^3.10"
|
|
30
|
+
minio = "^7.0.0"
|
|
31
|
+
click = "^8.0.0"
|
|
32
|
+
|
|
33
|
+
[tool.poetry.scripts]
|
|
34
|
+
mectl = "src.cli:main"
|
mectl-0.1.0/src/cli.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# src/minio_downloader/cli.py
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import argparse
|
|
6
|
+
from minio import Minio
|
|
7
|
+
from minio.error import S3Error
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
import subprocess
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_node_config(config_path=None):
|
|
14
|
+
if config_path:
|
|
15
|
+
try:
|
|
16
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
17
|
+
return json.load(f)
|
|
18
|
+
except FileNotFoundError:
|
|
19
|
+
raise FileNotFoundError(f"配置文件不存在: {config_path}")
|
|
20
|
+
except json.JSONDecodeError as e:
|
|
21
|
+
raise ValueError(f"配置文件格式错误: {config_path} - {e}")
|
|
22
|
+
else:
|
|
23
|
+
node_config_str = os.getenv('NODE_CONFIG')
|
|
24
|
+
if not node_config_str:
|
|
25
|
+
raise ValueError("请通过 --config 指定配置文件,或设置 NODE_CONFIG 环境变量")
|
|
26
|
+
try:
|
|
27
|
+
return json.loads(node_config_str)
|
|
28
|
+
except json.JSONDecodeError as e:
|
|
29
|
+
raise ValueError(f"环境变量 NODE_CONFIG 格式错误: {e}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def parse_minio_url(endpoint):
|
|
33
|
+
if not endpoint.startswith(('http://', 'https://')):
|
|
34
|
+
endpoint = 'http://' + endpoint
|
|
35
|
+
parsed = urlparse(endpoint)
|
|
36
|
+
secure = parsed.scheme == 'https'
|
|
37
|
+
host = parsed.hostname
|
|
38
|
+
if not host:
|
|
39
|
+
# 如果hostname为None,尝试从netloc中提取(不包含端口)
|
|
40
|
+
host = parsed.netloc.split(':')[0] if parsed.netloc else None
|
|
41
|
+
if not host:
|
|
42
|
+
raise ValueError(f"无法从endpoint中解析主机名: {endpoint}")
|
|
43
|
+
|
|
44
|
+
port = parsed.port
|
|
45
|
+
|
|
46
|
+
if port:
|
|
47
|
+
host = f"{host}:{port}"
|
|
48
|
+
elif not port and parsed.scheme == 'https':
|
|
49
|
+
host = f"{host}:443"
|
|
50
|
+
elif not port:
|
|
51
|
+
host = f"{host}:9000" # MinIO默认端口是9000,不是80
|
|
52
|
+
|
|
53
|
+
return host, secure
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def download_file(file_key, output_path, config_path=None, bucket_name="suanpan"):
|
|
57
|
+
"""实际的下载函数,供命令行工具调用"""
|
|
58
|
+
try:
|
|
59
|
+
config = load_node_config(config_path)
|
|
60
|
+
oss = config.get('oss', {})
|
|
61
|
+
|
|
62
|
+
if not oss:
|
|
63
|
+
print("❌ 配置文件中未找到 'oss' 配置项", file=sys.stderr)
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
if 'internalEndpoint' not in oss:
|
|
67
|
+
print("❌ 配置文件中缺少 'internalEndpoint' 配置项", file=sys.stderr)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
|
|
70
|
+
if 'accessKey' not in oss or 'accessSecret' not in oss:
|
|
71
|
+
print("❌ 配置文件中缺少 'accessKey' 或 'accessSecret' 配置项", file=sys.stderr)
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
|
|
74
|
+
endpoint, secure = parse_minio_url(oss['internalEndpoint'])
|
|
75
|
+
client = Minio(
|
|
76
|
+
endpoint,
|
|
77
|
+
access_key=oss['accessKey'],
|
|
78
|
+
secret_key=oss['accessSecret'],
|
|
79
|
+
secure=secure
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# 确保输出目录存在
|
|
83
|
+
output_dir = os.path.dirname(os.path.abspath(output_path))
|
|
84
|
+
if output_dir:
|
|
85
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
# 下载文件
|
|
88
|
+
client.fget_object(bucket_name, file_key, output_path)
|
|
89
|
+
print(f"✅ 已下载: {file_key} → {output_path}")
|
|
90
|
+
return output_path
|
|
91
|
+
|
|
92
|
+
except S3Error as e:
|
|
93
|
+
print(f"❌ MinIO 错误: {e}", file=sys.stderr)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
except (FileNotFoundError, ValueError) as e:
|
|
96
|
+
print(f"❌ {e}", file=sys.stderr)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
print(f"❌ 错误: {e}", file=sys.stderr)
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def extract_filename_from_key(file_key):
|
|
104
|
+
"""从file_key中提取文件名"""
|
|
105
|
+
# 获取key的最后一部分作为文件名
|
|
106
|
+
return os.path.basename(file_key)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def ask_unzip(file_path):
|
|
110
|
+
"""询问用户是否需要解压ZIP文件"""
|
|
111
|
+
response = input(f"检测到文件 {file_path} 是ZIP格式,是否需要解压?[y/N]: ").strip().lower()
|
|
112
|
+
return response in ['y', 'yes', '是']
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def unzip_file(file_path):
|
|
116
|
+
"""解压ZIP文件"""
|
|
117
|
+
try:
|
|
118
|
+
# 获取文件所在目录
|
|
119
|
+
dir_path = os.path.dirname(file_path)
|
|
120
|
+
if not dir_path:
|
|
121
|
+
dir_path = '.'
|
|
122
|
+
|
|
123
|
+
# 使用unzip命令解压
|
|
124
|
+
result = subprocess.run(['unzip', '-o', file_path, '-d', dir_path],
|
|
125
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
126
|
+
|
|
127
|
+
if result.returncode == 0:
|
|
128
|
+
print(f"✅ 已解压: {file_path}")
|
|
129
|
+
return True
|
|
130
|
+
else:
|
|
131
|
+
print(f"❌ 解压失败: {result.stderr}")
|
|
132
|
+
return False
|
|
133
|
+
except FileNotFoundError:
|
|
134
|
+
print("❌ 系统未找到unzip命令,请安装unzip后再试")
|
|
135
|
+
return False
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f"❌ 解压过程中出现错误: {e}")
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def download_file_with_cli(file_key, output_path=None, config_path=None, bucket_name="suanpan"):
|
|
142
|
+
"""命令行接口的下载函数"""
|
|
143
|
+
# 如果未指定输出路径,则从file_key中提取文件名
|
|
144
|
+
if not output_path:
|
|
145
|
+
filename = extract_filename_from_key(file_key)
|
|
146
|
+
output_path = os.path.join("./", filename)
|
|
147
|
+
|
|
148
|
+
# 执行下载
|
|
149
|
+
downloaded_path = download_file(file_key, output_path, config_path, bucket_name)
|
|
150
|
+
|
|
151
|
+
# 检查是否为zip文件,如果是则询问是否需要解压
|
|
152
|
+
if downloaded_path.lower().endswith('.zip'):
|
|
153
|
+
if ask_unzip(downloaded_path):
|
|
154
|
+
unzip_file(downloaded_path)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@click.group()
|
|
158
|
+
@click.version_option(version='1.0.0')
|
|
159
|
+
def cli():
|
|
160
|
+
"""MindEdge模型工具命令行"""
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@cli.command()
|
|
165
|
+
@click.argument('file_key')
|
|
166
|
+
@click.option('-o', '--output', default=None, help='本地保存路径')
|
|
167
|
+
@click.option('-c', '--config', help='配置文件路径(JSON,含 oss.accessKey 等)')
|
|
168
|
+
@click.option('-b', '--bucket', default='suanpan', help='MinIO 存储桶名称,默认为 \'suanpan\'')
|
|
169
|
+
def get(file_key, output, config, bucket):
|
|
170
|
+
"""下载MinIO中的文件"""
|
|
171
|
+
download_file_with_cli(file_key, output, config, bucket)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main():
|
|
175
|
+
cli()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
main()
|