Skip to content

Commit a185839

Browse files
update 3.23.9
1 parent 650fc1b commit a185839

File tree

11 files changed

+106
-54
lines changed

11 files changed

+106
-54
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
Version 3.23.9
2+
3+
New Features:
4+
5+
1. The setBucketLifecycle interface supports setting the expiration time of fragments in the bucket.
6+
7+
Resolved Issues:
8+
9+
1. Fix the bug that read of closed file may be reported when uploading retrying
10+
11+
-------------------------------------------------------------------------------------------------
112
Version 3.23.5
213

314
New Features:

README_CN.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
Version 3.23.5
1+
Version 3.23.9
2+
新特性:
3+
1. setBucketLifecycle接口支持设置桶碎片的过期时间。
4+
5+
修复问题
6+
7+
1. 修复上传重试时可能报read of closed file问题。
8+
9+
-------------------------------------------------------------------------------------------------
10+
Version 3.23.5
211
新特性:
312
1. getBucketStorageInfo接口增加查询标准、归档、低频三种类型的容量统计
413
2. 用户可以在发送请求时添加任意自定义头域

src/obs/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from obs.model import NoncurrentVersionExpiration, GetObjectHeader, HeadPermission, Lifecycle, Notification
2121
from obs.model import TopicConfiguration, FunctionGraphConfiguration, FilterRule, Replication, ReplicationRule
2222
from obs.model import Options, PutObjectHeader, AppendObjectHeader, AppendObjectContent, RedirectAllRequestTo
23-
from obs.model import Redirect, RoutingRule, Tag, TagInfo, Transition, NoncurrentVersionTransition, Rule, Versions
23+
from obs.model import Redirect, RoutingRule, Tag, TagInfo, Transition, NoncurrentVersionTransition, Rule, Versions, AbortIncompleteMultipartUpload
2424
from obs.model import Object, WebsiteConfiguration, Logging, CompleteMultipartUploadRequest, DeleteObjectsRequest
2525
from obs.model import ListMultipartUploadsRequest, GetObjectRequest, UploadFileHeader, Payer
2626
from obs.model import ExtensionHeader, FetchStatus, BucketAliasModel, ListBucketAliasModel
@@ -56,6 +56,7 @@
5656
'IndexDocument',
5757
'Expiration',
5858
'NoncurrentVersionExpiration',
59+
'AbortIncompleteMultipartUpload',
5960
'GetObjectHeader',
6061
'HeadPermission',
6162
'Lifecycle',

src/obs/client.py

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ def _rename_request_headers(self, headers, extension_headers, method):
621621
k = util.encode_item(k, ' ;/?:@&=+$,')
622622

623623
new_headers = self._rename_request_headers_handle(k, v, new_headers)
624+
if isinstance(extension_headers, dict):
624625
for k, v in extension_headers.items():
625626
new_headers = self._rename_request_headers_handle(k, v, new_headers)
626627
return new_headers
@@ -1278,7 +1279,7 @@ def _getApiVersion(self, bucketName=''):
12781279
return const.V2_SIGNATURE, res
12791280

12801281
@funcCache
1281-
def listBuckets(self, isQueryLocation=True, extensionHeaders=None, bucketType=None, maxKeys=100, marker=None):
1282+
def listBuckets(self, isQueryLocation=True, extensionHeaders=None, bucketType=None, maxKeys=None, marker=None):
12821283
"""
12831284
Obtain a bucket list.
12841285
:param isQueryLocation: Whether to query the bucket location.
@@ -1289,7 +1290,9 @@ def listBuckets(self, isQueryLocation=True, extensionHeaders=None, bucketType=No
12891290
If this parameter is left blank, both buckets and parallel file systems will be listed.
12901291
:return: A bucket list
12911292
"""
1292-
pathArgs = {'marker':marker, 'max-keys':maxKeys}
1293+
pathArgs = None
1294+
if maxKeys or marker:
1295+
pathArgs = {'marker': marker, 'max-keys': maxKeys}
12931296
if self.is_cname:
12941297
raise Exception('listBuckets is not allowed in custom domain mode')
12951298
return self._make_get_request(methodName='listBuckets', pathArgs=pathArgs, extensionHeaders=extensionHeaders,
@@ -1613,18 +1616,15 @@ def _prepare_file_notifier_and_entity(self, offset, file_size, headers, progress
16131616
notifier = progress.ProgressNotifier(progressCallback, totalCount)
16141617
else:
16151618
notifier = progress.NONE_NOTIFIER
1616-
readable_object = self.gen_readable_object_from_file(file_path)
1617-
readable_object.seek(offset)
1618-
entity = util.get_entity_for_send_with_total_count(readable_object, totalCount, self.chunk_size, notifier)
1619+
entity = util.get_entity_for_send_with_total_count(file_path, totalCount, offset, self.chunk_size, notifier)
16191620
else:
16201621
totalCount = headers['contentLength']
16211622
if totalCount > 0 and progressCallback is not None:
16221623
readable = True
16231624
notifier = progress.ProgressNotifier(progressCallback, totalCount)
16241625
else:
16251626
notifier = progress.NONE_NOTIFIER
1626-
readable_object = self.gen_readable_object_from_file(file_path)
1627-
entity = util.get_entity_for_send_with_total_count(readable_object, totalCount, self.chunk_size, notifier)
1627+
entity = util.get_entity_for_send_with_total_count(file_path, totalCount, None, self.chunk_size, notifier)
16281628

16291629
return headers, readable, notifier, entity
16301630

@@ -1644,8 +1644,8 @@ def _prepare_content_notifier_and_entity(self, entity, headers, progressCallback
16441644
notifier = progress.ProgressNotifier(progressCallback,
16451645
totalCount) if totalCount > 0 and progressCallback is not None \
16461646
else progress.NONE_NOTIFIER
1647-
entity = util.get_entity_for_send_with_total_count(entity, totalCount, self.chunk_size, notifier,
1648-
autoClose)
1647+
entity = util.get_entity_for_send_with_total_count(read_able=entity, totalCount=totalCount, chunk_size=self.chunk_size, notifier=notifier,
1648+
auto_close=autoClose)
16491649

16501650
return entity, readable, chunkedMode, notifier
16511651

@@ -1728,8 +1728,9 @@ def putContent(self, bucketName, objectKey, content=None, metadata=None, headers
17281728
notifier = progress.ProgressNotifier(progressCallback,
17291729
totalCount) if totalCount > 0 and progressCallback \
17301730
is not None else progress.NONE_NOTIFIER
1731-
entity = util.get_entity_for_send_with_total_count(entity, totalCount, self.chunk_size, notifier,
1732-
autoClose)
1731+
entity = util.get_entity_for_send_with_total_count(read_able=entity, totalCount=totalCount,
1732+
chunk_size=self.chunk_size, notifier=notifier,
1733+
auto_close=autoClose)
17331734

17341735
notifier.start()
17351736
ret = self._make_put_request(bucketName, objectKey, headers=_headers, entity=entity,
@@ -1789,8 +1790,6 @@ def putFile(self, bucketName, objectKey, file_path, metadata=None, headers=None,
17891790

17901791
headers = self._putFileHandleHeader(headers, size, objectKey, file_path)
17911792

1792-
readable_object = self.gen_readable_object_from_file(file_path)
1793-
metadata = self.add_metadata_from_content(metadata, headers, readable_object)
17941793
_headers = self.convertor.trans_put_object(metadata=metadata, headers=headers)
17951794
if const.CONTENT_LENGTH_HEADER not in _headers:
17961795
_headers[const.CONTENT_LENGTH_HEADER] = util.to_string(size)
@@ -1805,7 +1804,7 @@ def putFile(self, bucketName, objectKey, file_path, metadata=None, headers=None,
18051804
notifier = progress.NONE_NOTIFIER
18061805
readable = False
18071806

1808-
entity = util.get_entity_for_send_with_total_count(readable_object, totalCount, self.chunk_size, notifier)
1807+
entity = util.get_entity_for_send_with_total_count(file_path, totalCount, None, self.chunk_size, notifier)
18091808
try:
18101809
notifier.start()
18111810
ret = self._make_put_request(bucketName, objectKey, headers=_headers, entity=entity,
@@ -1815,13 +1814,6 @@ def putFile(self, bucketName, objectKey, file_path, metadata=None, headers=None,
18151814
self._generate_object_url(ret, bucketName, objectKey)
18161815
return ret
18171816

1818-
@staticmethod
1819-
def add_metadata_from_content(metadata, headers, content):
1820-
return metadata
1821-
1822-
def gen_readable_object_from_file(self, file_path):
1823-
return open(file_path, "rb")
1824-
18251817
@staticmethod
18261818
def _putFileHandleHeader(headers, size, objectKey, file_path):
18271819
headers['contentLength'] = util.to_long(headers.get('contentLength'))
@@ -1920,9 +1912,7 @@ def uploadPart(self, bucketName, objectKey, partNumber, uploadId, object=None, i
19201912

19211913
readable, notifier = self._prepare_upload_part_notifier(checked_file_part_info["partSize"],
19221914
progressCallback, readable)
1923-
readable_object = open(checked_file_part_info["file_path"], "rb")
1924-
readable_object.seek(checked_file_part_info["offset"])
1925-
entity = util.get_entity_for_send_with_total_count(readable_object, checked_file_part_info["partSize"],
1915+
entity = util.get_entity_for_send_with_total_count(checked_file_part_info["file_path"], checked_file_part_info["partSize"], checked_file_part_info["offset"],
19261916
self.chunk_size, notifier)
19271917
else:
19281918
headers = {}
@@ -1939,8 +1929,9 @@ def uploadPart(self, bucketName, objectKey, partNumber, uploadId, object=None, i
19391929
headers[const.CONTENT_LENGTH_HEADER] = util.to_string(partSize)
19401930
totalCount = util.to_long(partSize)
19411931
notifier = self._get_notifier_with_size(progressCallback, totalCount)
1942-
entity = util.get_entity_for_send_with_total_count(content, totalCount, self.chunk_size, notifier,
1943-
autoClose)
1932+
entity = util.get_entity_for_send_with_total_count(read_able=content, totalCount=totalCount,
1933+
chunk_size=self.chunk_size, notifier=notifier,
1934+
auto_close=autoClose)
19441935
else:
19451936
entity = content
19461937
if entity is None:
@@ -1989,9 +1980,8 @@ def _uploadPartWithNotifier(self, bucketName, objectKey, partNumber, uploadId, c
19891980

19901981
if notifier is not None and not isinstance(notifier, progress.NoneNotifier):
19911982
readable = True
1992-
readable_object = open(checked_file_part_info["file_path"], "rb")
1993-
readable_object.seek(checked_file_part_info["offset"])
1994-
entity = util.get_entity_for_send_with_total_count(readable_object, partSize, self.chunk_size, notifier)
1983+
entity = util.get_entity_for_send_with_total_count(checked_file_part_info["file_path"], partSize, checked_file_part_info["offset"],
1984+
self.chunk_size, notifier)
19951985
else:
19961986
if content is not None and hasattr(content, 'read') and callable(content.read):
19971987
readable = True
@@ -2002,8 +1992,8 @@ def _uploadPartWithNotifier(self, bucketName, objectKey, partNumber, uploadId, c
20021992
entity = util.get_readable_entity(content, self.chunk_size, notifier)
20031993
else:
20041994
headers[const.CONTENT_LENGTH_HEADER] = util.to_string(partSize)
2005-
entity = util.get_entity_for_send_with_total_count(content, util.to_long(partSize), self.chunk_size,
2006-
notifier)
1995+
entity = util.get_entity_for_send_with_total_count(read_able=content, totalCount=util.to_long(partSize),
1996+
chunk_size=self.chunk_size, notifier=notifier)
20071997
else:
20081998
entity = content
20091999
if entity is None:

src/obs/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
DEFAULT_TASK_NUM = 8
9797
DEFAULT_TASK_QUEUE_SIZE = 20000
9898

99-
OBS_SDK_VERSION = '3.22.2'
99+
OBS_SDK_VERSION = '3.23.9'
100100

101101
V2_META_HEADER_PREFIX = 'x-amz-meta-'
102102
V2_HEADER_PREFIX = 'x-amz-'

src/obs/convertor.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from obs.model import DateTime, ListObjectsResponse, Content, CorsRule, ObjectVersionHead, ObjectVersion, \
3838
ObjectDeleteMarker, DeleteObjectResult, NoncurrentVersionExpiration, NoncurrentVersionTransition, Rule, Condition, \
3939
Redirect, FilterRule, FunctionGraphConfiguration, Upload, CompleteMultipartUploadResponse, ListPartsResponse, \
40-
Grant, ReplicationRule, Transition, Grantee, BucketAliasModel, ListBucketAliasModel
40+
Grant, ReplicationRule, Transition, Grantee, BucketAliasModel, ListBucketAliasModel, AbortIncompleteMultipartUpload
4141

4242
if const.IS_PYTHON2:
4343
from urllib import unquote_plus, quote_plus
@@ -558,6 +558,11 @@ def trans_lifecycle(self, lifecycle):
558558
ET.SubElement(noncurrentVersionExpirationEle, 'NoncurrentDays').text = util.to_string(
559559
item['noncurrentVersionExpiration']['noncurrentDays'])
560560

561+
if item.get('abortIncompleteMultipartUpload') is not None and item['abortIncompleteMultipartUpload'].get(
562+
'daysAfterInitiation') is not None:
563+
abortIncompleteMultipartUploadEle = ET.SubElement(ruleEle, 'AbortIncompleteMultipartUpload')
564+
ET.SubElement(abortIncompleteMultipartUploadEle, 'DaysAfterInitiation').text = util.to_string(
565+
item['abortIncompleteMultipartUpload']['daysAfterInitiation'])
561566
return ET.tostring(root, 'UTF-8')
562567

563568
def _trans_lifecycle_transition_expiration(self, item, ruleEle):
@@ -1047,7 +1052,9 @@ def _set_sse_header(self, sseHeader, headers=None, onlySseCHeader=False):
10471052
headers = {}
10481053
if isinstance(sseHeader, SseCHeader):
10491054
self._put_key_value(headers, self.ha.sse_c_header(), sseHeader.get('encryption'))
1050-
key = util.to_string(sseHeader.get('key'))
1055+
key = sseHeader.get('key')
1056+
if not isinstance(key, bytes):
1057+
key = util.to_string(sseHeader.get('key'))
10511058
self._put_key_value(headers, self.ha.sse_c_key_header(), util.base64_encode(key))
10521059
self._put_key_value(headers, self.ha.sse_c_key_md5_header(), util.base64_encode(util.md5_encode(key)))
10531060
elif isinstance(sseHeader, SseKmsHeader) and not onlySseCHeader:
@@ -1520,8 +1527,13 @@ def parseGetBucketLifecycle(self, xml, headers=None):
15201527
noncurrentDays=noncurrentDays)
15211528
noncurrentVersionTransitions.append(noncurrentVersionTransition)
15221529

1530+
abort_parts = rule.find('AbortIncompleteMultipartUpload')
1531+
abortIncompleteMultipartUpload = AbortIncompleteMultipartUpload(daysAfterInitiation=util.to_int(
1532+
abort_parts.find('DaysAfterInitiation').text)) if abort_parts is not None else None
1533+
15231534
rule = Rule(id=_id, prefix=prefix, status=status, expiration=expiration,
1524-
noncurrentVersionExpiration=noncurrentVersionExpiration)
1535+
noncurrentVersionExpiration=noncurrentVersionExpiration,
1536+
abortIncompleteMultipartUpload=abortIncompleteMultipartUpload)
15251537
rule.transition = transitions
15261538
rule.noncurrentVersionTransition = noncurrentVersionTransitions
15271539
entries.append(rule)

src/obs/model.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
'IndexDocument',
4949
'Expiration',
5050
'NoncurrentVersionExpiration',
51+
'AbortIncompleteMultipartUpload',
5152
'GetObjectHeader',
5253
'HeadPermission',
5354
'Lifecycle',
@@ -831,20 +832,30 @@ def __init__(self, storageClass=None, noncurrentDays=None):
831832
self.storageClass = storageClass
832833

833834

835+
class AbortIncompleteMultipartUpload(BaseModel):
836+
allowedAttr = {'daysAfterInitiation': int}
837+
838+
def __init__(self, daysAfterInitiation=None):
839+
super(AbortIncompleteMultipartUpload, self).__init__()
840+
self.daysAfterInitiation = daysAfterInitiation
841+
842+
834843
class Rule(BaseModel):
835844
allowedAttr = {'id': BASESTRING, 'prefix': BASESTRING, 'status': BASESTRING, 'expiration': Expiration,
836845
'noncurrentVersionExpiration': NoncurrentVersionExpiration,
837-
'transition': [Transition, list], 'noncurrentVersionTransition': [NoncurrentVersionTransition, list]}
846+
'transition': [Transition, list], 'noncurrentVersionTransition': [NoncurrentVersionTransition, list],
847+
'abortIncompleteMultipartUpload': AbortIncompleteMultipartUpload}
838848

839849
def __init__(self, id=None, prefix=None, status=None, expiration=None, noncurrentVersionExpiration=None,
840-
transition=None, noncurrentVersionTransition=None):
850+
transition=None, noncurrentVersionTransition=None, abortIncompleteMultipartUpload=None):
841851
self.id = id
842852
self.prefix = prefix
843853
self.status = status
844854
self.expiration = expiration
845855
self.noncurrentVersionExpiration = noncurrentVersionExpiration
846856
self.transition = transition
847857
self.noncurrentVersionTransition = noncurrentVersionTransition
858+
self.abortIncompleteMultipartUpload = abortIncompleteMultipartUpload
848859

849860

850861
class Upload(BaseModel):

src/obs/util.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,21 +199,25 @@ def entity(conn):
199199

200200
def get_readable_entity_by_total_count(readable, totalCount, chunk_size=const.READ_ONCE_LENGTH, notifier=None,
201201
auto_close=True):
202-
return get_entity_for_send_with_total_count(readable, totalCount, chunk_size, notifier, auto_close)
202+
return get_entity_for_send_with_total_count(totalCount=totalCount, chunk_size=chunk_size, notifier=notifier, auto_close=auto_close, read_able=readable)
203203

204204

205205
def get_file_entity_by_total_count(file_path, totalCount, chunk_size=const.READ_ONCE_LENGTH, notifier=None):
206-
f = open(file_path, "rb")
207-
return get_entity_for_send_with_total_count(f, totalCount, chunk_size, notifier)
206+
return get_entity_for_send_with_total_count(file_path, totalCount, None, chunk_size, notifier)
208207

209208

210-
def get_entity_for_send_with_total_count(readable, totalCount=None, chunk_size=const.READ_ONCE_LENGTH, notifier=None,
211-
auto_close=True):
209+
def get_entity_for_send_with_total_count(file_path=None, totalCount=None, offset=None, chunk_size=const.READ_ONCE_LENGTH,
210+
notifier=None, auto_close=True, read_able=None):
212211
if notifier is None:
213212
notifier = progress.NONE_NOTIFIER
214-
215213
def entity(conn):
216214
readCount = 0
215+
if file_path:
216+
readable = open(file_path, "rb")
217+
if offset:
218+
readable.seek(offset)
219+
else:
220+
readable = read_able
217221
try:
218222
while True:
219223
if totalCount is None or totalCount - readCount >= chunk_size:
@@ -237,9 +241,7 @@ def entity(conn):
237241

238242

239243
def get_file_entity_by_offset_partsize(file_path, offset, totalCount, chunk_size=const.READ_ONCE_LENGTH, notifier=None):
240-
f = open(file_path, "rb")
241-
f.seek(offset)
242-
return get_entity_for_send_with_total_count(f, totalCount, chunk_size, notifier)
244+
return get_entity_for_send_with_total_count(file_path, totalCount, offset, chunk_size, notifier)
243245

244246

245247
def is_ipaddress(item):

src/setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from setuptools import setup, find_packages
2121

2222
setup(
23-
name='sdk-obs-python',
24-
version='3.23.5',
23+
name='esdk-obs-python',
24+
version='3.23.9',
2525
packages=find_packages(exclude=['tests']),
2626
zip_safe=False,
2727
description='OBS Python SDK',

0 commit comments

Comments
 (0)