Skip to content

Commit d8ed878

Browse files
authored
bump to 7.14.0 (#450)
* feat: add retrier * chore(utils): deprecate etag * refactor: update circle import solution between region and conf Make `qiniu.config` more independent and move test cases to `tests/cases/test_zone` from `test_qiniu.py` BREAK CHANGE: default value of protected filed `qiniu.config._config['default_zone']` changes to `None` from `qiniu.region.Region()`, which should be never used directly in user code. * feat: add new region and endpoint for source/accelerate uploading with retrying * fix: default_client will not work with config.set_default by call once * chore: remove iter self for endpoint * chore: improve error text * fix: some features are not compatible with old version and improve error text - change preferred scheme to http from https - change UploaderBase._get_regions return type to list[LegacyRegion] from list[LegacyRegion or Region] - change all inner legacy region type name from Region to LegacyRegion - LegacyRegion.get_bucket_hosts homecache argument not work * fix: TokenExpiredRetryPolicy not work * fix: AccUnavailableRetryPolicy modify origin region service * fix: argument name typo on put_data * feat: bucket support regions and endpoints retry * test: add and improve test cases * feat: add field persistentType to strict policy fields * style: fix flake8 code styles * fix: enum on python2 * test: fix compatibility of test cases on python 2.7 * test: change test region to na0 from z0 * chore: ci add no accelerate bucket * test: fix test error with python2 * fix: LegacyRegion.get_bucket_host not working in python2 * doc: add more type info to functions * chore: change default hosts for querying regions * fix: CachedRegionsProvider shrink not working * feat: add uc backup hosts * feat: update version and changelog * add Qiniu auth verify callback * chore: remove `Region.from_region_id` backup domain qbox and s3 * feat: add idle-time fop support and get fop status * docs: fix authorization token link * fix: form retry not working by no resume recorder * chore: fix flake8 lint on python >= 3.8 * Update CHANGELOG.md * fix: legacy region get_xxx_host and add more cases
1 parent 3ac1cf8 commit d8ed878

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5828
-991
lines changed

.github/workflows/ci-test.yml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
QINIU_ACCESS_KEY: ${{ secrets.QINIU_ACCESS_KEY }}
5656
QINIU_SECRET_KEY: ${{ secrets.QINIU_SECRET_KEY }}
5757
QINIU_TEST_BUCKET: ${{ secrets.QINIU_TEST_BUCKET }}
58+
QINIU_TEST_NO_ACC_BUCKET: ${{ secrets.QINIU_TEST_NO_ACC_BUCKET }}
5859
QINIU_TEST_DOMAIN: ${{ secrets.QINIU_TEST_DOMAIN }}
5960
QINIU_UPLOAD_CALLBACK_URL: ${{secrets.QINIU_UPLOAD_CALLBACK_URL}}
6061
QINIU_TEST_ENV: "travis"

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
# Changelog
2+
## 7.14.0
3+
* 对象存储,空间管理、上传文件新增备用域名重试逻辑
4+
* 对象存储,调整查询区域主备域名
5+
* 对象存储,支持空间级别加速域名开关
6+
* 对象存储,回调签名验证函数新增兼容 Qiniu 签名
7+
* 对象存储,持久化处理支持闲时任务
8+
29
## 7.13.2(2024-05-28)
310
* 对象存储,修复上传回调设置自定义变量失效(v7.12.0 引入)
411

examples/upload.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# -*- coding: utf-8 -*-
22
# flake8: noqa
3+
# import hashlib
34

4-
from qiniu import Auth, put_file, etag, urlsafe_base64_encode
5+
from qiniu import Auth, put_file, urlsafe_base64_encode
56
import qiniu.config
67
from qiniu.compat import is_py2, is_py3
78

@@ -24,13 +25,28 @@
2425
# 要上传文件的本地路径
2526
localfile = '/Users/jemy/Documents/qiniu.png'
2627

27-
ret, info = put_file(token, key, localfile)
28+
# 上传时,sdk 会自动计算文件 hash 作为参数传递给服务端确保上传完整性
29+
# (若不一致,服务端会拒绝完成上传)
30+
# 但在访问文件时,服务端可能不会提供 MD5 或者编码格式不是期望的
31+
# 因此若有需有,请通过元数据功能自定义 MD5 或其他 hash 字段
32+
# hasher = hashlib.md5()
33+
# with open(localfile, 'rb') as f:
34+
# for d in f:
35+
# hasher.update(d)
36+
# object_metadata = {
37+
# 'x-qn-meta-md5': hasher.hexdigest()
38+
# }
39+
40+
ret, info = put_file(
41+
token,
42+
key,
43+
localfile
44+
# metadata=object_metadata
45+
)
2846
print(ret)
2947
print(info)
3048

3149
if is_py2:
3250
assert ret['key'].encode('utf-8') == key
3351
elif is_py3:
3452
assert ret['key'] == key
35-
36-
assert ret['hash'] == etag(localfile)

examples/upload_callback.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# flake8: noqa
33

4-
from qiniu import Auth, put_file, etag
4+
from qiniu import Auth, put_file
55

66
access_key = '...'
77
secret_key = '...'
@@ -25,4 +25,3 @@
2525
ret, info = put_file(token, key, localfile)
2626
print(info)
2727
assert ret['key'] == key
28-
assert ret['hash'] == etag(localfile)

examples/upload_pfops.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
# flake8: noqa
3-
from qiniu import Auth, put_file, etag, urlsafe_base64_encode
3+
from qiniu import Auth, put_file, urlsafe_base64_encode
44

55
access_key = '...'
66
secret_key = '...'
@@ -36,4 +36,3 @@
3636
ret, info = put_file(token, key, localfile)
3737
print(info)
3838
assert ret['key'] == key
39-
assert ret['hash'] == etag(localfile)

examples/upload_with_qvmzone.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# flake8: noqa
33

4-
from qiniu import Auth, put_file, etag, urlsafe_base64_encode
4+
from qiniu import Auth, put_file, urlsafe_base64_encode
55
import qiniu.config
66
from qiniu import Zone, set_default
77

@@ -37,4 +37,3 @@
3737
ret, info = put_file(token, key, localfile)
3838
print(info)
3939
assert ret['key'] == key
40-
assert ret['hash'] == etag(localfile)

qiniu/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99

1010
# flake8: noqa
1111

12-
__version__ = '7.13.2'
12+
__version__ = '7.14.0'
1313

1414
from .auth import Auth, QiniuMacAuth
1515

1616
from .config import set_default
1717
from .zone import Zone
18-
from .region import Region
18+
from .region import LegacyRegion as Region
1919

2020
from .services.storage.bucket import BucketManager, build_batch_copy, build_batch_rename, build_batch_move, \
21-
build_batch_stat, build_batch_delete, build_batch_restoreAr
21+
build_batch_stat, build_batch_delete, build_batch_restoreAr, build_batch_restore_ar
2222
from .services.storage.uploader import put_data, put_file, put_stream
2323
from .services.storage.upload_progress_recorder import UploadProgressRecorder
2424
from .services.cdn.manager import CdnManager, create_timestamp_anti_leech_url, DomainManager

qiniu/auth.py

+96-16
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
str('persistentOps'), # 持久化处理操作
3838
str('persistentNotifyUrl'), # 持久化处理结果通知URL
3939
str('persistentPipeline'), # 持久化处理独享队列
40+
str('persistentType'), # 为 `1` 时,开启闲时任务,必须是 int 类型
41+
4042
str('deleteAfterDays'), # 文件多少天后自动删除
4143
str('fileType'), # 文件的存储类型,0为标准存储,1为低频存储,2为归档存储,3为深度归档存储,4为归档直读存储
4244
str('isPrefixalScope'), # 指定上传文件必须使用的前缀
@@ -194,22 +196,56 @@ def __upload_token(self, policy):
194196
return self.token_with_data(data)
195197

196198
def verify_callback(
197-
self,
198-
origin_authorization,
199-
url,
200-
body,
201-
content_type='application/x-www-form-urlencoded'):
202-
"""回调验证
203-
204-
Args:
205-
origin_authorization: 回调时请求Header中的Authorization字段
206-
url: 回调请求的url
207-
body: 回调请求的body
208-
content_type: 回调请求body的Content-Type
209-
210-
Returns:
211-
返回true表示验证成功,返回false表示验证失败
199+
self,
200+
origin_authorization,
201+
url,
202+
body,
203+
content_type='application/x-www-form-urlencoded',
204+
method='GET',
205+
headers=None
206+
):
207+
"""
208+
Qbox 回调验证
209+
210+
Parameters
211+
----------
212+
origin_authorization: str
213+
回调时请求 Header 中的 Authorization 字段
214+
url: str
215+
回调请求的 url
216+
body: str
217+
回调请求的 body
218+
content_type: str
219+
回调请求的 Content-Type
220+
method: str
221+
回调请求的 method,Qiniu 签名必须传入,默认 GET
222+
headers: dict
223+
回调请求的 headers,Qiniu 签名必须传入,默认为空字典
224+
225+
Returns
226+
-------
227+
bool
228+
返回 True 表示验证成功,返回 False 表示验证失败
212229
"""
230+
if headers is None:
231+
headers = {}
232+
233+
# 兼容 Qiniu 签名
234+
if origin_authorization.startswith("Qiniu"):
235+
qn_auth = QiniuMacAuth(
236+
access_key=self.__access_key,
237+
secret_key=self.__secret_key,
238+
disable_qiniu_timestamp_signature=True
239+
)
240+
return qn_auth.verify_callback(
241+
origin_authorization,
242+
url=url,
243+
body=body,
244+
content_type=content_type,
245+
method=method,
246+
headers=headers
247+
)
248+
213249
token = self.token_of_request(url, body, content_type)
214250
authorization = 'QBox {0}'.format(token)
215251
return origin_authorization == authorization
@@ -243,7 +279,7 @@ class QiniuMacAuth(object):
243279
__access_key
244280
__secret_key
245281
246-
http://kirk-docs.qiniu.com/apidocs/#TOC_325b437b89e8465e62e958cccc25c63f
282+
https://developer.qiniu.com/kodo/1201/access-token
247283
"""
248284

249285
def __init__(self, access_key, secret_key, disable_qiniu_timestamp_signature=None):
@@ -326,6 +362,50 @@ def qiniu_headers(self, headers):
326362
'%s: %s' % (canonical_mime_header_key(key), headers.get(key)) for key in sorted(qiniu_fields)
327363
])
328364

365+
def verify_callback(
366+
self,
367+
origin_authorization,
368+
url,
369+
body,
370+
content_type='application/x-www-form-urlencoded',
371+
method='GET',
372+
headers=None
373+
):
374+
"""
375+
Qiniu 回调验证
376+
377+
Parameters
378+
----------
379+
origin_authorization: str
380+
回调时请求 Header 中的 Authorization 字段
381+
url: str
382+
回调请求的 url
383+
body: str
384+
回调请求的 body
385+
content_type: str
386+
回调请求的 Content-Type
387+
method: str
388+
回调请求的 Method
389+
headers: dict
390+
回调请求的 headers
391+
392+
Returns
393+
-------
394+
395+
"""
396+
if headers is None:
397+
headers = {}
398+
token = self.token_of_request(
399+
method=method,
400+
host=headers.get('Host', None),
401+
url=url,
402+
qheaders=self.qiniu_headers(headers),
403+
content_type=content_type,
404+
body=body
405+
)
406+
authorization = 'Qiniu {0}'.format(token)
407+
return origin_authorization == authorization
408+
329409
@staticmethod
330410
def __checkKey(access_key, secret_key):
331411
if not (access_key and secret_key):

qiniu/config.py

+26-24
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,36 @@
11
# -*- coding: utf-8 -*-
2-
from qiniu import region
3-
42
RS_HOST = 'http://rs.qiniu.com' # 管理操作Host
53
RSF_HOST = 'http://rsf.qbox.me' # 列举操作Host
64
API_HOST = 'http://api.qiniuapi.com' # 数据处理操作Host
7-
UC_HOST = region.UC_HOST # 获取空间信息Host
8-
QUERY_REGION_HOST = 'https://kodo-config.qiniuapi.com'
5+
QUERY_REGION_HOST = 'https://uc.qiniuapi.com'
6+
QUERY_REGION_BACKUP_HOSTS = [
7+
'kodo-config.qiniuapi.com',
8+
'uc.qbox.me'
9+
]
10+
UC_HOST = QUERY_REGION_HOST # 获取空间信息Host
11+
UC_BACKUP_HOSTS = QUERY_REGION_BACKUP_HOSTS
912

1013
_BLOCK_SIZE = 1024 * 1024 * 4 # 断点续传分块大小,该参数为接口规格,暂不支持修改
1114

1215
_config = {
13-
'default_zone': region.Region(),
16+
'default_zone': None,
1417
'default_rs_host': RS_HOST,
1518
'default_rsf_host': RSF_HOST,
1619
'default_api_host': API_HOST,
1720
'default_uc_host': UC_HOST,
21+
'default_uc_backup_hosts': UC_BACKUP_HOSTS,
1822
'default_query_region_host': QUERY_REGION_HOST,
19-
'default_query_region_backup_hosts': [
20-
'uc.qbox.me',
21-
'api.qiniu.com'
22-
],
23-
'default_backup_hosts_retry_times': 2,
23+
'default_query_region_backup_hosts': QUERY_REGION_BACKUP_HOSTS,
24+
'default_backup_hosts_retry_times': 3, # 仅控制旧区域 LegacyRegion 查询 Hosts 的重试次数
2425
'connection_timeout': 30, # 链接超时为时间为30s
2526
'connection_retries': 3, # 链接重试次数为3次
2627
'connection_pool': 10, # 链接池个数为10
2728
'default_upload_threshold': 2 * _BLOCK_SIZE # put_file上传方式的临界默认值
2829
}
2930

3031
_is_customized_default = {
31-
'default_zone': False,
32-
'default_rs_host': False,
33-
'default_rsf_host': False,
34-
'default_api_host': False,
35-
'default_uc_host': False,
36-
'default_query_region_host': False,
37-
'default_query_region_backup_hosts': False,
38-
'default_backup_hosts_retry_times': False,
39-
'connection_timeout': False,
40-
'connection_retries': False,
41-
'connection_pool': False,
42-
'default_upload_threshold': False
32+
k: False
33+
for k in _config.keys()
4334
}
4435

4536

@@ -48,6 +39,10 @@ def is_customized_default(key):
4839

4940

5041
def get_default(key):
42+
if key == 'default_zone' and not _is_customized_default[key]:
43+
# prevent circle import
44+
from .region import LegacyRegion
45+
return LegacyRegion()
5146
return _config[key]
5247

5348

@@ -56,7 +51,7 @@ def set_default(
5651
connection_timeout=None, default_rs_host=None, default_uc_host=None,
5752
default_rsf_host=None, default_api_host=None, default_upload_threshold=None,
5853
default_query_region_host=None, default_query_region_backup_hosts=None,
59-
default_backup_hosts_retry_times=None):
54+
default_backup_hosts_retry_times=None, default_uc_backup_hosts=None):
6055
if default_zone:
6156
_config['default_zone'] = default_zone
6257
_is_customized_default['default_zone'] = True
@@ -72,16 +67,23 @@ def set_default(
7267
if default_uc_host:
7368
_config['default_uc_host'] = default_uc_host
7469
_is_customized_default['default_uc_host'] = True
70+
_config['default_uc_backup_hosts'] = []
71+
_is_customized_default['default_uc_backup_hosts'] = True
7572
_config['default_query_region_host'] = default_uc_host
7673
_is_customized_default['default_query_region_host'] = True
7774
_config['default_query_region_backup_hosts'] = []
7875
_is_customized_default['default_query_region_backup_hosts'] = True
76+
if default_uc_backup_hosts is not None:
77+
_config['default_uc_backup_hosts'] = default_uc_backup_hosts
78+
_is_customized_default['default_uc_backup_hosts'] = True
79+
_config['default_query_region_backup_hosts'] = default_uc_backup_hosts
80+
_is_customized_default['default_query_region_backup_hosts'] = True
7981
if default_query_region_host:
8082
_config['default_query_region_host'] = default_query_region_host
8183
_is_customized_default['default_query_region_host'] = True
8284
_config['default_query_region_backup_hosts'] = []
8385
_is_customized_default['default_query_region_backup_hosts'] = True
84-
if default_query_region_backup_hosts:
86+
if default_query_region_backup_hosts is not None:
8587
_config['default_query_region_backup_hosts'] = default_query_region_backup_hosts
8688
_is_customized_default['default_query_region_backup_hosts'] = True
8789
if default_backup_hosts_retry_times:

0 commit comments

Comments
 (0)