atomgit 1.0.10__tar.gz → 1.0.12__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.
Files changed (24) hide show
  1. {atomgit-1.0.10 → atomgit-1.0.12}/PKG-INFO +1 -1
  2. {atomgit-1.0.10 → atomgit-1.0.12}/api.py +20 -67
  3. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/PKG-INFO +1 -1
  4. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit_hub.py +39 -1
  5. {atomgit-1.0.10 → atomgit-1.0.12}/setup.py +1 -1
  6. {atomgit-1.0.10 → atomgit-1.0.12}/MANIFEST.in +0 -0
  7. {atomgit-1.0.10 → atomgit-1.0.12}/README.md +0 -0
  8. {atomgit-1.0.10 → atomgit-1.0.12}/__init__.py +0 -0
  9. {atomgit-1.0.10 → atomgit-1.0.12}/__main__.py +0 -0
  10. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/SOURCES.txt +0 -0
  11. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/dependency_links.txt +0 -0
  12. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/entry_points.txt +0 -0
  13. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/requires.txt +0 -0
  14. {atomgit-1.0.10 → atomgit-1.0.12}/atomgit.egg-info/top_level.txt +0 -0
  15. {atomgit-1.0.10 → atomgit-1.0.12}/cli.py +0 -0
  16. {atomgit-1.0.10 → atomgit-1.0.12}/config.py +0 -0
  17. {atomgit-1.0.10 → atomgit-1.0.12}/rate_limiter.py +0 -0
  18. {atomgit-1.0.10 → atomgit-1.0.12}/requirements.txt +0 -0
  19. {atomgit-1.0.10 → atomgit-1.0.12}/setup.cfg +0 -0
  20. {atomgit-1.0.10 → atomgit-1.0.12}/tests/test_download_real.py +0 -0
  21. {atomgit-1.0.10 → atomgit-1.0.12}/tests/test_endpoint_config.py +0 -0
  22. {atomgit-1.0.10 → atomgit-1.0.12}/tests/test_repo_id_normalize.py +0 -0
  23. {atomgit-1.0.10 → atomgit-1.0.12}/tests/test_repo_name_validation.py +0 -0
  24. {atomgit-1.0.10 → atomgit-1.0.12}/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atomgit
3
- Version: 1.0.10
3
+ Version: 1.0.12
4
4
  Summary: AtomGit模型文件上传下载CLI工具
5
5
  Home-page: https://atomgit.com/gitcode-ai/atomgit_cli
6
6
  Author: AtomGit CLI Team
@@ -15,7 +15,12 @@ os.makedirs(cache_dir, exist_ok=True)
15
15
  os.environ["HF_HOME"] = cache_dir
16
16
 
17
17
 
18
- from huggingface_hub import hf_hub_download, upload_folder, create_repo, snapshot_download, constants as hf_constants
18
+ from huggingface_hub import hf_hub_download, upload_folder, create_repo, constants as hf_constants
19
+
20
+ try:
21
+ from . import atomgit_hub
22
+ except ImportError:
23
+ import atomgit_hub
19
24
 
20
25
  try:
21
26
  from .rate_limiter import install_hf_rate_limiter
@@ -62,53 +67,6 @@ class HuggingFaceAPI:
62
67
  # 二层或单层格式直接返回
63
68
  return repo_id
64
69
 
65
- def _probe_once(self, probe_id: str, token: Optional[str] = None):
66
- """探测一次仓库是否可访问,返回 (可访问, 原因)。
67
-
68
- 原因取值:ok / not_found / unauthorized / status=<code> / 探测失败
69
- """
70
- endpoint = os.environ.get("HF_ENDPOINT", "https://hub.atomgit.com").rstrip("/")
71
- headers = {"User-Agent": "atomgit-cli"}
72
- if token:
73
- headers["Authorization"] = f"Bearer {token}"
74
-
75
- status = None
76
- # 默认分支可能是 main 或 master,404 时再试另一个
77
- for revision in ("main", "master"):
78
- request = urllib.request.Request(
79
- f"{endpoint}/api/models/{probe_id}/revision/{revision}", headers=headers
80
- )
81
- try:
82
- with urllib.request.urlopen(request, timeout=15):
83
- return True, "ok"
84
- except urllib.error.HTTPError as e:
85
- status = e.code
86
- if status != 404:
87
- break
88
- except Exception as e:
89
- # 网络抖动不阻断下载,交给 SDK 自己重试
90
- return True, f"探测失败: {e}"
91
- if status == 404:
92
- return False, "not_found"
93
- if status in (401, 403):
94
- return False, "unauthorized"
95
- return True, f"status={status}"
96
-
97
- def _remote_repo_accessible(self, repo_id: str, token: Optional[str] = None):
98
- """确认远端仓库真的可访问。
99
-
100
- huggingface_hub 的 snapshot_download 在远端 404 且 local_dir 已存在时,
101
- 会打印 "Returning existing local_dir ..." 并正常返回,
102
- 导致"下载不存在的仓库"被误报成功,所以下载前先探测一次。
103
- """
104
- accessible, reason = self._probe_once(repo_id, None)
105
- if accessible or reason == "not_found":
106
- return accessible, reason
107
- # 匿名探测失败时,若本地有令牌就再用令牌试一次
108
- if token:
109
- return self._probe_once(repo_id, token)
110
- return accessible, reason
111
-
112
70
  def login(self, token: str) -> bool:
113
71
  """登录验证"""
114
72
  if not token or len(token) < 10:
@@ -262,20 +220,8 @@ class HuggingFaceAPI:
262
220
  def download_repo(self, repo_id: str, local_path: Path = None, force_download: bool = False) -> bool:
263
221
  """下载仓库 - 使用Hugging Face Hub SDK,支持公开仓库无需token和断点续传"""
264
222
  try:
265
- # 标准化仓库ID(处理三层格式)
223
+ # repo_id 交给 atomgit_hub 归一化(三层 -> dash),探测也在那边完成
266
224
  normalized_repo_id = self._normalize_repo_id(repo_id)
267
-
268
- # 远端不可访问且本地目录已存在时,SDK 会静默回退到本地目录并正常返回,
269
- # 会把"上次残留"误报成下载成功,所以先探测远端仓库是否真的存在
270
- credentials = config.get_credentials()
271
- accessible, reason = self._remote_repo_accessible(
272
- normalized_repo_id, credentials['token'] if credentials else None)
273
- if not accessible:
274
- if reason == "unauthorized":
275
- print(f"❌ 仓库无权访问(令牌无效或权限不足): {repo_id}")
276
- else:
277
- print(f"❌ 仓库不存在: {repo_id}")
278
- return False
279
225
 
280
226
  if local_path is None:
281
227
  local_path = Path.cwd() / repo_id.split('/')[-1]
@@ -283,14 +229,16 @@ class HuggingFaceAPI:
283
229
  # 创建本地目录
284
230
  local_path.mkdir(parents=True, exist_ok=True)
285
231
 
286
- # 首先尝试不使用token下载(适用于公开仓库)
287
232
  credentials = config.get_credentials()
288
233
  try:
289
- snapshot_download(
290
- repo_id=normalized_repo_id,
234
+ # 复用 atomgit_hub:内部会先探测仓库可访问性,令牌无效但仓库公开时自动降级匿名,
235
+ # 避免 huggingface_hub 在 403/404 时静默回退本地目录导致假成功
236
+ atomgit_hub.snapshot_download(
237
+ repo_id=repo_id,
291
238
  local_dir=str(local_path),
292
- force_download=force_download, # 根据用户选择决定是否强制下载
293
- token=credentials['token'] if credentials and 'token' in credentials else None
239
+ force_download=force_download,
240
+ token=credentials['token'] if credentials and 'token' in credentials else None,
241
+ max_workers=8,
294
242
  )
295
243
  print(f"✅ 仓库下载成功")
296
244
  return True
@@ -314,7 +262,12 @@ class HuggingFaceAPI:
314
262
 
315
263
  # 创建本地目录
316
264
  local_path.mkdir(parents=True, exist_ok=True)
317
-
265
+
266
+ # 先探测文件是否存在,给出准确提示(避免把 404 报成网络/缓存故障)
267
+ if atomgit_hub._file_http_status(normalized_repo_id, filename) == 404:
268
+ print(f"文件不存在:{repo_id}/{filename}(revision: main,HTTP 404)。请检查文件路径或版本。")
269
+ return False
270
+
318
271
  # 首先尝试不使用token下载(适用于公开仓库)
319
272
  try:
320
273
  hf_hub_download(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atomgit
3
- Version: 1.0.10
3
+ Version: 1.0.12
4
4
  Summary: AtomGit模型文件上传下载CLI工具
5
5
  Home-page: https://atomgit.com/gitcode-ai/atomgit_cli
6
6
  Author: AtomGit CLI Team
@@ -161,6 +161,29 @@ def _probe_repo(repo_id: str, token: Optional[str] = None) -> Optional[str]:
161
161
  return None
162
162
 
163
163
 
164
+ def _file_http_status(repo_id: str, filename: str, revision: Optional[str] = None,
165
+ token: Optional[str] = None) -> Optional[int]:
166
+ """探测单个文件的可访问性,返回 HTTP 状态码;网络异常返回 None。"""
167
+ endpoint = os.environ.get("HF_ENDPOINT", "https://hub.atomgit.com").rstrip("/")
168
+ rev = revision or "main"
169
+ headers = {"User-Agent": "atomgit-cli"}
170
+ if token:
171
+ headers["Authorization"] = f"Bearer {token}"
172
+ # 注意:HF 兼容的 resolve 路由是 /{namespace}/{repo}/resolve/...,不带 /api/models 前缀
173
+ request = urllib.request.Request(
174
+ f"{endpoint}/{repo_id}/resolve/{rev}/{urllib.parse.quote(filename, safe='/')}",
175
+ headers=headers,
176
+ method="HEAD",
177
+ )
178
+ try:
179
+ with urllib.request.urlopen(request, timeout=15):
180
+ return 200
181
+ except urllib.error.HTTPError as e:
182
+ return e.code
183
+ except Exception:
184
+ return None
185
+
186
+
164
187
  def snapshot_download(
165
188
  repo_id: str,
166
189
  revision: Optional[str] = None,
@@ -360,10 +383,25 @@ def download_file(
360
383
  return result
361
384
  except Exception as e:
362
385
  error_msg = str(e)
386
+ rev = revision or "main"
387
+ # huggingface_hub 常把“文件不存在”报成缓存/网络故障(LocalEntryNotFoundError),
388
+ # 这里用一次 HEAD 请求还原真实状态码,给出准确提示
389
+ status = _file_http_status(normalized_repo_id, filename, revision, token)
390
+ if status == 404:
391
+ raise Exception(
392
+ f"文件不存在:{repo_id}/{filename}(revision: {rev},HTTP 404)。请检查文件路径或版本。"
393
+ )
394
+ if status in (401, 403):
395
+ raise Exception(
396
+ f"认证失败:{repo_id}/{filename}(revision: {rev},HTTP {status})。"
397
+ "请检查 token 是否有效,或是否拥有该仓库权限。"
398
+ )
363
399
  if "401" in error_msg or "403" in error_msg:
364
400
  raise Exception(f"认证失败:{error_msg}")
365
401
  elif "404" in error_msg:
366
- raise Exception(f"文件不存在:{repo_id}/{filename}")
402
+ raise Exception(
403
+ f"文件不存在:{repo_id}/{filename}(revision: {rev},HTTP 404)。请检查文件路径或版本。"
404
+ )
367
405
  else:
368
406
  raise Exception(f"下载失败:{error_msg}")
369
407
 
@@ -31,7 +31,7 @@ def read_requirements():
31
31
 
32
32
  setup(
33
33
  name='atomgit',
34
- version='1.0.10',
34
+ version='1.0.12',
35
35
  author='AtomGit CLI Team',
36
36
  author_email='sa@atomgit.com',
37
37
  description='AtomGit模型文件上传下载CLI工具',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes