abdds 0.1.0__tar.gz → 0.2.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.
@@ -1,20 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abdds
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Async Baidu Disk SDK - 百度网盘异步 Python SDK
5
5
  Author: hochenggang
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/hochenggang
7
+ Project-URL: Homepage, https://github.com/hochenggang/abdds
8
8
  Project-URL: Repository, https://github.com/hochenggang/abdds
9
9
  Project-URL: Bug Tracker, https://github.com/hochenggang/abdds/issues
10
10
  Keywords: baidu,pan,netdisk,sdk,baidupan,baidu-disk,async,aio
11
- Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Classifier: Framework :: AsyncIO
19
20
  Classifier: Topic :: Internet :: WWW/HTTP
20
21
  Classifier: Typing :: Typed
@@ -27,6 +28,8 @@ Requires-Dist: pytest>=7.0; extra == "dev"
27
28
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
28
29
  Requires-Dist: respx>=0.20; extra == "dev"
29
30
  Requires-Dist: pytest-mock>=3.0; extra == "dev"
31
+ Requires-Dist: build>=1.0; extra == "dev"
32
+ Requires-Dist: twine>=4.0; extra == "dev"
30
33
 
31
34
  # abdds
32
35
 
@@ -54,74 +57,60 @@ pip install abdds
54
57
  from pathlib import Path
55
58
  from abdds import AsyncBaiduPanClient
56
59
 
57
- client = AsyncBaiduPanClient(
60
+ async with AsyncBaiduPanClient(
58
61
  client_id="your_client_id",
59
62
  client_secret="your_client_secret",
60
63
  app_name="your_app_name",
61
- )
62
- ```
63
-
64
- ### 3. 授权登录
65
-
66
- ```python
67
- # 首次使用需要授权
68
- if not client.access_token:
69
- print(f"请在浏览器打开: {client.auth_url}")
70
- code = input("请输入授权码: ")
71
- await client.fetch_token(code)
64
+ ) as client:
65
+ if not client.is_authenticated:
66
+ print(f"请在浏览器打开: {client.auth_url}")
67
+ code = input("请输入授权码: ")
68
+ await client.fetch_token(code)
72
69
  ```
73
70
 
74
71
  Token 会自动保存到 `~/.baidupan/` 目录,下次启动无需重新授权。
75
72
 
76
- ### 4. 使用 API
73
+ ### 3. 使用 API
77
74
 
78
75
  ```python
79
76
  # 查询空间配额
80
- quota = await client.get_quota()
81
- print(f"已用: {quota.used / 1024**3:.1f} GB / 总计: {quota.total / 1024**3:.1f} GB")
77
+ q = await client.quota()
78
+ print(f"已用: {q.used / 1024**3:.1f} GB / 总计: {q.total / 1024**3:.1f} GB")
82
79
 
83
80
  # 上传文件 - 支持 Path / bytes / Generator / AsyncGenerator
84
81
  result = await client.upload(Path("local_file.txt"))
85
- result = await client.upload(Path("data.bin"), file_to=Path("/backup/data.bin"))
86
- result = await client.upload(b"hello world", file_to=Path("/hello.txt"))
82
+ result = await client.upload(Path("data.bin"), remote_path="/backup/data.bin")
83
+ result = await client.upload(b"hello world", remote_path="/hello.txt")
87
84
 
88
85
  # 获取文件元信息
89
- metas = await client.get_file_metas([result.fs_id])
86
+ metas = await client.file_metas([result.fs_id])
90
87
  dlink = metas[0].dlink
91
88
 
92
- # 下载文件 - file_to=None 返回异步流式迭代器,file_to=Path 写入文件
93
- path = await client.download(dlink, file_to=Path("downloaded.txt"))
89
+ # 下载文件到本地路径
90
+ await client.download_to_file(dlink, Path("downloaded.txt"))
94
91
 
95
92
  # 流式下载(适合大文件或自定义处理)
96
- stream = await client.download(dlink)
97
- async for chunk in stream:
93
+ async for chunk in client.download_stream(dlink):
94
+ process(chunk)
95
+
96
+ # 带断点续传的流式下载
97
+ async for chunk in client.download_stream_with_range(dlink, range=(1024, None)):
98
98
  process(chunk)
99
99
 
100
100
  # 列出文件
101
- items = await client.get_file_list("/apps/your_app_name/")
101
+ items = await client.list_files("/apps/your_app_name/")
102
102
  for item in items:
103
103
  print(f"{'[DIR]' if item.is_dir else ' '} {item.filename} ({item.size} bytes)")
104
104
 
105
105
  # 删除文件
106
- await client.delete_files(["/apps/your_app_name/old_file.txt"])
107
-
108
- # 使用完毕后关闭
109
- await client.close()
110
- ```
111
-
112
- 推荐使用异步上下文管理器自动关闭:
113
-
114
- ```python
115
- async with AsyncBaiduPanClient(client_id, client_secret, app_name) as client:
116
- quota = await client.get_quota()
117
- # ...
106
+ await client.delete(["/apps/your_app_name/old_file.txt"])
118
107
  ```
119
108
 
120
109
  或使用异步工厂方法:
121
110
 
122
111
  ```python
123
112
  client = await AsyncBaiduPanClient.create(client_id, client_secret, app_name)
124
- quota = await client.get_quota()
113
+ q = await client.quota()
125
114
  await client.close()
126
115
  ```
127
116
 
@@ -131,14 +120,17 @@ await client.close()
131
120
 
132
121
  | 方法 | 说明 |
133
122
  |------|------|
134
- | `await upload(file_from, file_to=None)` | 异步上传文件。`file_from` 支持 `Path` / `bytes` / `Generator[bytes]` / `AsyncGenerator[bytes]`;`file_to` 为远程路径 `Path`,默认使用文件名 |
135
- | `await download(dlink, file_to=None, chunk_size=1048576)` | 异步下载文件。`file_to=None` 返回异步字节迭代器;`file_to=Path` 写入本地文件 |
136
- | `await get_quota()` | 获取空间配额,返回 `ApiQuotaInfo` |
137
- | `await get_file_metas(fsids)` | 获取文件元信息,返回 `list[ApiFileMeta]` |
138
- | `await get_file_list(path)` | 列出目录文件,返回 `list[ApiFileListItem]` |
139
- | `await delete_files(file_paths)` | 批量删除文件 |
123
+ | `await upload(source, remote_path="")` | 异步上传数据。`source` 支持 `Path` / `bytes` / `Generator[bytes]` / `AsyncGenerator[bytes]`;`remote_path` 为远程路径字符串 |
124
+ | `await download_to_file(dlink, local_path, *, chunk_size=1048576)` | 下载文件到本地路径,返回 `Path` |
125
+ | `download_stream(dlink, *, chunk_size=1048576)` | 异步流式下载,返回 `AsyncGenerator[bytes]` |
126
+ | `download_stream_with_range(dlink, range, *, chunk_size=1048576, max_retries=5)` | 带断点续传的异步流式下载,`range` `(start, end)` 元组 |
127
+ | `await quota()` | 获取空间配额,返回 `ApiQuotaInfo` |
128
+ | `await file_metas(fsids)` | 获取文件元信息(含 dlink),返回 `list[ApiFileMeta]` |
129
+ | `await list_files(path="/")` | 列出目录文件,返回 `list[ApiFileListItem]` |
130
+ | `await delete(paths)` | 批量删除文件 |
140
131
  | `await fetch_token(code)` | 通过授权码获取 Token |
141
132
  | `await refresh_token()` | 刷新 Token |
133
+ | `is_authenticated` | 是否已认证(属性) |
142
134
  | `await close()` | 关闭客户端 |
143
135
 
144
136
  ### 异常体系
@@ -158,7 +150,7 @@ BaiduPanError
158
150
  - **自动重试** — 网络请求自动重试(可配置次数)
159
151
  - **Token 管理** — 自动持久化、自动刷新、文件权限限制
160
152
  - **分片上传** — 大文件自动分片上传(4MB 分片)
161
- - **流式下载** — 支持断点续传、指数重试
153
+ - **流式下载** — 支持断点续传、Range 分片、指数重试
162
154
  - **类型安全** — 完整类型标注,支持 PEP 561 (`py.typed`)
163
155
  - **报错清晰** — 异常包含 `errno`、`errmsg`、`request_id`、`url` 等上下文
164
156
 
@@ -184,7 +176,6 @@ pip install -e ".[dev]"
184
176
  python -m pytest -v
185
177
 
186
178
  # 构建
187
- pip install build twine
188
179
  python -m build
189
180
  ```
190
181
 
@@ -24,74 +24,60 @@ pip install abdds
24
24
  from pathlib import Path
25
25
  from abdds import AsyncBaiduPanClient
26
26
 
27
- client = AsyncBaiduPanClient(
27
+ async with AsyncBaiduPanClient(
28
28
  client_id="your_client_id",
29
29
  client_secret="your_client_secret",
30
30
  app_name="your_app_name",
31
- )
32
- ```
33
-
34
- ### 3. 授权登录
35
-
36
- ```python
37
- # 首次使用需要授权
38
- if not client.access_token:
39
- print(f"请在浏览器打开: {client.auth_url}")
40
- code = input("请输入授权码: ")
41
- await client.fetch_token(code)
31
+ ) as client:
32
+ if not client.is_authenticated:
33
+ print(f"请在浏览器打开: {client.auth_url}")
34
+ code = input("请输入授权码: ")
35
+ await client.fetch_token(code)
42
36
  ```
43
37
 
44
38
  Token 会自动保存到 `~/.baidupan/` 目录,下次启动无需重新授权。
45
39
 
46
- ### 4. 使用 API
40
+ ### 3. 使用 API
47
41
 
48
42
  ```python
49
43
  # 查询空间配额
50
- quota = await client.get_quota()
51
- print(f"已用: {quota.used / 1024**3:.1f} GB / 总计: {quota.total / 1024**3:.1f} GB")
44
+ q = await client.quota()
45
+ print(f"已用: {q.used / 1024**3:.1f} GB / 总计: {q.total / 1024**3:.1f} GB")
52
46
 
53
47
  # 上传文件 - 支持 Path / bytes / Generator / AsyncGenerator
54
48
  result = await client.upload(Path("local_file.txt"))
55
- result = await client.upload(Path("data.bin"), file_to=Path("/backup/data.bin"))
56
- result = await client.upload(b"hello world", file_to=Path("/hello.txt"))
49
+ result = await client.upload(Path("data.bin"), remote_path="/backup/data.bin")
50
+ result = await client.upload(b"hello world", remote_path="/hello.txt")
57
51
 
58
52
  # 获取文件元信息
59
- metas = await client.get_file_metas([result.fs_id])
53
+ metas = await client.file_metas([result.fs_id])
60
54
  dlink = metas[0].dlink
61
55
 
62
- # 下载文件 - file_to=None 返回异步流式迭代器,file_to=Path 写入文件
63
- path = await client.download(dlink, file_to=Path("downloaded.txt"))
56
+ # 下载文件到本地路径
57
+ await client.download_to_file(dlink, Path("downloaded.txt"))
64
58
 
65
59
  # 流式下载(适合大文件或自定义处理)
66
- stream = await client.download(dlink)
67
- async for chunk in stream:
60
+ async for chunk in client.download_stream(dlink):
61
+ process(chunk)
62
+
63
+ # 带断点续传的流式下载
64
+ async for chunk in client.download_stream_with_range(dlink, range=(1024, None)):
68
65
  process(chunk)
69
66
 
70
67
  # 列出文件
71
- items = await client.get_file_list("/apps/your_app_name/")
68
+ items = await client.list_files("/apps/your_app_name/")
72
69
  for item in items:
73
70
  print(f"{'[DIR]' if item.is_dir else ' '} {item.filename} ({item.size} bytes)")
74
71
 
75
72
  # 删除文件
76
- await client.delete_files(["/apps/your_app_name/old_file.txt"])
77
-
78
- # 使用完毕后关闭
79
- await client.close()
80
- ```
81
-
82
- 推荐使用异步上下文管理器自动关闭:
83
-
84
- ```python
85
- async with AsyncBaiduPanClient(client_id, client_secret, app_name) as client:
86
- quota = await client.get_quota()
87
- # ...
73
+ await client.delete(["/apps/your_app_name/old_file.txt"])
88
74
  ```
89
75
 
90
76
  或使用异步工厂方法:
91
77
 
92
78
  ```python
93
79
  client = await AsyncBaiduPanClient.create(client_id, client_secret, app_name)
94
- quota = await client.get_quota()
80
+ q = await client.quota()
95
81
  await client.close()
96
82
  ```
97
83
 
@@ -101,14 +87,17 @@ await client.close()
101
87
 
102
88
  | 方法 | 说明 |
103
89
  |------|------|
104
- | `await upload(file_from, file_to=None)` | 异步上传文件。`file_from` 支持 `Path` / `bytes` / `Generator[bytes]` / `AsyncGenerator[bytes]`;`file_to` 为远程路径 `Path`,默认使用文件名 |
105
- | `await download(dlink, file_to=None, chunk_size=1048576)` | 异步下载文件。`file_to=None` 返回异步字节迭代器;`file_to=Path` 写入本地文件 |
106
- | `await get_quota()` | 获取空间配额,返回 `ApiQuotaInfo` |
107
- | `await get_file_metas(fsids)` | 获取文件元信息,返回 `list[ApiFileMeta]` |
108
- | `await get_file_list(path)` | 列出目录文件,返回 `list[ApiFileListItem]` |
109
- | `await delete_files(file_paths)` | 批量删除文件 |
90
+ | `await upload(source, remote_path="")` | 异步上传数据。`source` 支持 `Path` / `bytes` / `Generator[bytes]` / `AsyncGenerator[bytes]`;`remote_path` 为远程路径字符串 |
91
+ | `await download_to_file(dlink, local_path, *, chunk_size=1048576)` | 下载文件到本地路径,返回 `Path` |
92
+ | `download_stream(dlink, *, chunk_size=1048576)` | 异步流式下载,返回 `AsyncGenerator[bytes]` |
93
+ | `download_stream_with_range(dlink, range, *, chunk_size=1048576, max_retries=5)` | 带断点续传的异步流式下载,`range` `(start, end)` 元组 |
94
+ | `await quota()` | 获取空间配额,返回 `ApiQuotaInfo` |
95
+ | `await file_metas(fsids)` | 获取文件元信息(含 dlink),返回 `list[ApiFileMeta]` |
96
+ | `await list_files(path="/")` | 列出目录文件,返回 `list[ApiFileListItem]` |
97
+ | `await delete(paths)` | 批量删除文件 |
110
98
  | `await fetch_token(code)` | 通过授权码获取 Token |
111
99
  | `await refresh_token()` | 刷新 Token |
100
+ | `is_authenticated` | 是否已认证(属性) |
112
101
  | `await close()` | 关闭客户端 |
113
102
 
114
103
  ### 异常体系
@@ -128,7 +117,7 @@ BaiduPanError
128
117
  - **自动重试** — 网络请求自动重试(可配置次数)
129
118
  - **Token 管理** — 自动持久化、自动刷新、文件权限限制
130
119
  - **分片上传** — 大文件自动分片上传(4MB 分片)
131
- - **流式下载** — 支持断点续传、指数重试
120
+ - **流式下载** — 支持断点续传、Range 分片、指数重试
132
121
  - **类型安全** — 完整类型标注,支持 PEP 561 (`py.typed`)
133
122
  - **报错清晰** — 异常包含 `errno`、`errmsg`、`request_id`、`url` 等上下文
134
123
 
@@ -154,7 +143,6 @@ pip install -e ".[dev]"
154
143
  python -m pytest -v
155
144
 
156
145
  # 构建
157
- pip install build twine
158
146
  python -m build
159
147
  ```
160
148
 
@@ -1,51 +1,54 @@
1
- [build-system]
2
- requires = ["setuptools>=68.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "abdds"
7
- version = "0.1.0"
8
- description = "Async Baidu Disk SDK - 百度网盘异步 Python SDK"
9
- readme = "README.md"
10
- license = "MIT"
11
- requires-python = ">=3.9"
12
- authors = [
13
- { name = "hochenggang" },
14
- ]
15
- keywords = ["baidu", "pan", "netdisk", "sdk", "baidupan", "baidu-disk", "async", "aio"]
16
- classifiers = [
17
- "Development Status :: 3 - Alpha",
18
- "Intended Audience :: Developers",
19
- "Programming Language :: Python :: 3",
20
- "Programming Language :: Python :: 3.9",
21
- "Programming Language :: Python :: 3.10",
22
- "Programming Language :: Python :: 3.11",
23
- "Programming Language :: Python :: 3.12",
24
- "Framework :: AsyncIO",
25
- "Topic :: Internet :: WWW/HTTP",
26
- "Typing :: Typed",
27
- ]
28
- dependencies = [
29
- "httpx[http2]>=0.24",
30
- "aiofiles>=23.0",
31
- ]
32
-
33
- [project.optional-dependencies]
34
- dev = [
35
- "pytest>=7.0",
36
- "pytest-asyncio>=0.21",
37
- "respx>=0.20",
38
- "pytest-mock>=3.0",
39
- ]
40
-
41
- [project.urls]
42
- Homepage = "https://github.com/hochenggang"
43
- Repository = "https://github.com/hochenggang/abdds"
44
- "Bug Tracker" = "https://github.com/hochenggang/abdds/issues"
45
-
46
- [tool.setuptools.packages.find]
47
- where = ["src"]
48
-
49
- [tool.pytest.ini_options]
50
- testpaths = ["tests"]
51
- asyncio_mode = "auto"
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "abdds"
7
+ version = "0.2.0"
8
+ description = "Async Baidu Disk SDK - 百度网盘异步 Python SDK"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ { name = "hochenggang" },
14
+ ]
15
+ keywords = ["baidu", "pan", "netdisk", "sdk", "baidupan", "baidu-disk", "async", "aio"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Framework :: AsyncIO",
26
+ "Topic :: Internet :: WWW/HTTP",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "httpx[http2]>=0.24",
31
+ "aiofiles>=23.0",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=7.0",
37
+ "pytest-asyncio>=0.21",
38
+ "respx>=0.20",
39
+ "pytest-mock>=3.0",
40
+ "build>=1.0",
41
+ "twine>=4.0",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/hochenggang/abdds"
46
+ Repository = "https://github.com/hochenggang/abdds"
47
+ "Bug Tracker" = "https://github.com/hochenggang/abdds/issues"
48
+
49
+ [tool.setuptools.packages.find]
50
+ where = ["src"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+ asyncio_mode = "auto"
@@ -4,12 +4,18 @@
4
4
 
5
5
  Usage::
6
6
 
7
+ from pathlib import Path
7
8
  from abdds import AsyncBaiduPanClient
8
9
 
9
10
  async with AsyncBaiduPanClient(client_id, client_secret, app_name) as client:
10
- quota = await client.get_quota()
11
- await client.upload(Path("file.txt"), file_to=Path("/docs/file.txt"))
12
- await client.download(dlink, file_to=Path("output.txt"))
11
+ if not client.is_authenticated:
12
+ print(client.auth_url)
13
+ code = input("Enter code: ")
14
+ await client.fetch_token(code)
15
+
16
+ q = await client.quota()
17
+ result = await client.upload(Path("file.txt"), remote_path="/docs/file.txt")
18
+ await client.download_to_file(dlink, Path("output.txt"))
13
19
  """
14
20
 
15
21
  __version__ = "0.1.0"
@@ -4,7 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import logging
7
- from typing import Any, Awaitable, Callable
7
+ from collections.abc import Awaitable, Callable
8
+ from typing import Any
8
9
 
9
10
  import httpx
10
11
 
@@ -12,7 +13,7 @@ from .errors import BaiduPanAPIError, BaiduPanNetworkError, TokenExpiredError
12
13
 
13
14
  logger = logging.getLogger("abdds")
14
15
 
15
- UserAgent = "netdisk;2.2.51.6;netdisk;10.0;PC;PC;Mac OS X 10.15.7;en-US"
16
+ _USER_AGENT = "netdisk;2.2.51.6;netdisk;10.0;PC;PC;Mac OS X 10.15.7;en-US"
16
17
 
17
18
 
18
19
  def _truncate_data(data: Any, max_len: int = 500) -> Any:
@@ -50,7 +51,7 @@ class _HttpTransport:
50
51
  client = httpx.AsyncClient(
51
52
  transport=transport,
52
53
  timeout=httpx.Timeout(self._timeout[1], connect=self._timeout[0]),
53
- headers={"User-Agent": UserAgent},
54
+ headers={"User-Agent": _USER_AGENT},
54
55
  follow_redirects=True,
55
56
  )
56
57
  return client