p115client 0.0.5.8.2__py3-none-any.whl → 0.0.5.8.4__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.
p115client/_upload.py CHANGED
@@ -10,8 +10,8 @@ __all__ = [
10
10
 
11
11
  from base64 import b64encode
12
12
  from collections.abc import (
13
- AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Callable, Coroutine, Generator,
14
- ItemsView, Iterable, Iterator, Mapping, Sequence, Sized,
13
+ AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable,
14
+ Coroutine, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence, Sized,
15
15
  )
16
16
  from datetime import datetime
17
17
  from email.utils import formatdate
@@ -25,7 +25,7 @@ from xml.etree.ElementTree import fromstring
25
25
 
26
26
  from asynctools import ensure_aiter, ensure_async
27
27
  from filewrap import (
28
- Buffer, SupportsRead,
28
+ SupportsRead, buffer_length,
29
29
  bio_chunk_iter, bio_chunk_async_iter,
30
30
  bio_skip_iter, bio_skip_async_iter,
31
31
  bytes_iter_to_async_reader, bytes_iter_to_reader,
@@ -41,20 +41,13 @@ from .exception import MultipartUploadAbort
41
41
  from .type import MultipartResumeData
42
42
 
43
43
 
44
- def buffer_length(b: Buffer, /) -> int:
45
- if isinstance(b, Sized):
46
- return len(b)
47
- else:
48
- return len(memoryview(b))
49
-
50
-
51
- def to_base64(s: bytes | str, /) -> str:
44
+ def to_base64(s: Buffer | str, /) -> str:
52
45
  if isinstance(s, str):
53
46
  s = bytes(s, "utf-8")
54
47
  return str(b64encode(s), "ascii")
55
48
 
56
49
 
57
- def to_integer(n, /):
50
+ def maybe_integer(n: int | str, /) -> int | str:
58
51
  if isinstance(n, str) and n.isdecimal():
59
52
  n = int(n)
60
53
  return n
@@ -155,19 +148,19 @@ def oss_upload_sign(
155
148
  # "replicationProgress", "requestPayment", "requesterQosInfo", "resourceGroup", "resourcePool",
156
149
  # "resourcePoolBuckets", "resourcePoolInfo", "response-cache-control", "response-content-disposition",
157
150
  # "response-content-encoding", "response-content-language", "response-content-type", "response-expires",
158
- # "restore", "security-token", "sequential", "startTime", "stat", "status", "style", "styleName", "symlink",
159
- # "tagging", "transferAcceleration", "uploadId", "uploads", "versionId", "versioning", "versions", "vod",
160
- # "website", "worm", "wormExtend", "wormId", "x-oss-ac-forward-allow", "x-oss-ac-source-ip",
161
- # "x-oss-ac-subnet-mask", "x-oss-ac-vpc-id", "x-oss-access-point-name", "x-oss-async-process", "x-oss-process",
162
- # "x-oss-redundancy-transition-taskid", "x-oss-request-payer", "x-oss-target-redundancy-type",
163
- # "x-oss-traffic-limit", "x-oss-write-get-object-response",
151
+ # "restore", "security-token", "sequential", "startTime", "stat", "status", "style", "styleName",
152
+ # "symlink", "tagging", "transferAcceleration", "uploadId", "uploads", "versionId", "versioning",
153
+ # "versions", "vod", "website", "worm", "wormExtend", "wormId", "x-oss-ac-forward-allow",
154
+ # "x-oss-ac-source-ip", "x-oss-ac-subnet-mask", "x-oss-ac-vpc-id", "x-oss-access-point-name",
155
+ # "x-oss-async-process", "x-oss-process", "x-oss-redundancy-transition-taskid", "x-oss-request-payer",
156
+ # "x-oss-target-redundancy-type", "x-oss-traffic-limit", "x-oss-write-get-object-response",
164
157
  # )
165
158
  date = formatdate(usegmt=True)
166
159
  if params is None:
167
160
  params = ""
168
161
  elif not isinstance(params, str):
169
162
  params = urlencode(params)
170
- if params:
163
+ if params and not params.startswith("?"):
171
164
  params = "?" + params
172
165
  if headers:
173
166
  if isinstance(headers, Mapping):
@@ -183,7 +176,7 @@ def oss_upload_sign(
183
176
  headers_str = ""
184
177
  content_md5 = headers.setdefault("content-md5", "")
185
178
  content_type = headers.setdefault("content-type", "")
186
- date = headers.get("x-oss-date") or headers.get("date", "")
179
+ date = headers.get("x-oss-date") or headers.get("date") or ""
187
180
  if not date:
188
181
  date = headers["date"] = formatdate(usegmt=True)
189
182
  signature_data = f"""\
@@ -269,13 +262,16 @@ def oss_multipart_part_iter(
269
262
  ) -> Iterator[dict] | AsyncIterator[dict]:
270
263
  """罗列某个分块上传任务,已经上传的分块
271
264
  """
265
+ request_kwargs.update(
266
+ method="GET",
267
+ params={"uploadId": upload_id},
268
+ headers={"x-oss-security-token": token["SecurityToken"]},
269
+ )
270
+ request_kwargs.setdefault("parse", lambda _, content: fromstring(content))
272
271
  def gen_step():
273
- request_kwargs["method"] = "GET"
274
- request_kwargs["headers"] = {"x-oss-security-token": token["SecurityToken"]}
275
- request_kwargs["params"] = params = {"uploadId": upload_id}
276
- request_kwargs.setdefault("parse", False)
272
+ params = request_kwargs["params"]
277
273
  while True:
278
- content = yield oss_upload_request(
274
+ etree = yield oss_upload_request(
279
275
  request,
280
276
  url=url,
281
277
  bucket=bucket,
@@ -284,12 +280,11 @@ def oss_multipart_part_iter(
284
280
  async_=async_,
285
281
  **request_kwargs,
286
282
  )
287
- etree = fromstring(content)
288
283
  for el in etree.iterfind("Part"):
289
- yield Yield({sel.tag: to_integer(sel.text) for sel in el}, identity=True)
290
- if etree.find("IsTruncated").text == "false": # type: ignore
284
+ yield Yield({sel.tag: maybe_integer(sel.text) for sel in el}, identity=True)
285
+ if getattr(etree.find("IsTruncated"), "text") == "false":
291
286
  break
292
- params["part-number-marker"] = etree.find("NextPartNumberMarker").text # type: ignore
287
+ params["part-number-marker"] = getattr(etree.find("NextPartNumberMarker"), "text")
293
288
  return run_gen_step_iter(gen_step, async_=async_)
294
289
 
295
290
 
@@ -327,12 +322,14 @@ def oss_multipart_upload_init(
327
322
  async_: Literal[False, True] = False,
328
323
  **request_kwargs,
329
324
  ) -> str | Coroutine[Any, Any, str]:
330
- """分片上传的初始化,获取 upload_id
325
+ """分块上传的初始化,获取 upload_id
331
326
  """
327
+ request_kwargs.update(
328
+ method="POST",
329
+ params={"sequential": "1", "uploads": "1"},
330
+ headers={"x-oss-security-token": token["SecurityToken"]},
331
+ )
332
332
  request_kwargs.setdefault("parse", parse_upload_id)
333
- request_kwargs["method"] = "POST"
334
- request_kwargs["params"] = {"sequential": "1", "uploads": "1"}
335
- request_kwargs["headers"] = {"x-oss-security-token": token["SecurityToken"]}
336
333
  return oss_upload_request(
337
334
  request,
338
335
  url=url,
@@ -387,20 +384,26 @@ def oss_multipart_upload_complete(
387
384
  async_: Literal[False, True] = False,
388
385
  **request_kwargs,
389
386
  ) -> dict | Coroutine[Any, Any, dict]:
390
- """完成分片上传任务,会执行回调然后 115 上就能看到文件
387
+ """完成分块上传任务,会在请求头中包含回调数据,请求体中包含分块信息
391
388
  """
392
- request_kwargs["method"] = "POST"
393
- request_kwargs["params"] = {"uploadId": upload_id}
394
- request_kwargs["headers"] = {
395
- "x-oss-security-token": token["SecurityToken"],
396
- "x-oss-callback": to_base64(callback["callback"]),
397
- "x-oss-callback-var": to_base64(callback["callback_var"]),
398
- "content-type": "text/xml"
399
- }
400
- request_kwargs["data"] = ("<CompleteMultipartUpload>%s</CompleteMultipartUpload>" % "".join(map(
401
- "<Part><PartNumber>{PartNumber}</PartNumber><ETag>{ETag}</ETag></Part>".format_map,
402
- parts,
403
- ))).encode()
389
+ request_kwargs.update(
390
+ method="POST",
391
+ params={"uploadId": upload_id},
392
+ data=b"".join((
393
+ b"<CompleteMultipartUpload>",
394
+ *map(
395
+ b"<Part><PartNumber>%d</PartNumber><ETag>%s</ETag></Part>".__mod__,
396
+ ((part["PartNumber"], bytes(part["ETag"], "ascii")) for part in parts),
397
+ ),
398
+ b"</CompleteMultipartUpload>",
399
+ )),
400
+ headers={
401
+ "x-oss-security-token": token["SecurityToken"],
402
+ "x-oss-callback": to_base64(callback["callback"]),
403
+ "x-oss-callback-var": to_base64(callback["callback_var"]),
404
+ "content-type": "text/xml",
405
+ },
406
+ )
404
407
  return oss_upload_request(
405
408
  request,
406
409
  url=url,
@@ -449,12 +452,14 @@ def oss_multipart_upload_cancel(
449
452
  async_: Literal[False, True] = False,
450
453
  **request_kwargs,
451
454
  ) -> bool | Coroutine[Any, Any, bool]:
452
- """取消分片上传任务,返回成功与否
455
+ """取消分块上传任务,返回成功与否
453
456
  """
457
+ request_kwargs.update(
458
+ method="DELETE",
459
+ params={"uploadId": upload_id},
460
+ headers={"x-oss-security-token": token["SecurityToken"]},
461
+ )
454
462
  request_kwargs.setdefault("parse", lambda resp: 200 <= resp.status_code < 300 or resp.status_code == 404)
455
- request_kwargs["method"] = "DELETE"
456
- request_kwargs["params"] = {"uploadId": upload_id}
457
- request_kwargs["headers"] = {"x-oss-security-token": token["SecurityToken"]}
458
463
  return oss_upload_request(
459
464
  request,
460
465
  url=url,
@@ -520,7 +525,7 @@ def oss_multipart_upload_part(
520
525
  ) -> dict | Coroutine[Any, Any, dict]:
521
526
  """上传一个分片,返回一个字典,包含如下字段:
522
527
 
523
- .. python:
528
+ .. code:: python
524
529
 
525
530
  {
526
531
  "PartNumber": int, # 分块序号,从 1 开始计数
@@ -924,3 +929,22 @@ def oss_multipart_upload(
924
929
  yield close_reporthook
925
930
  return run_gen_step(gen_step, async_=async_)
926
931
 
932
+
933
+ # class MultipartUploader:
934
+ # def __init__
935
+ # def __del__
936
+ # async def __aiter__
937
+ # def __iter__
938
+ # async def __aenter__
939
+ # async def __aexit__
940
+ # def __enter__
941
+ # def __exit__
942
+ # # 0. 应该设计 1 个类,支持同步和异步,实例化不会进行初始化(为了对异步进行适配)
943
+ # # 1. 可以作为上下文管理器或者迭代器使用
944
+ # # 2. 上下文管理器也返回迭代器(迭代器迭代时,如果未打开文件或者没有上传信息,则会初始化以获取)
945
+ # # 3. 中途可以暂停或取消
946
+ # # 4. seekable: path, url (支持 range request), file reader (seekable)
947
+ # # 5. 支持进度条
948
+ # # 6. 设计一个工具函数,放到 p115client.tool.upload 模块中
949
+ # ...
950
+