Skip to content

Commit 63a05a8

Browse files
committed
1. add random test bucket name 2. add delete test file after finish 3. add tagging function&tests
1 parent 4b1d079 commit 63a05a8

File tree

12 files changed

+717
-8
lines changed

12 files changed

+717
-8
lines changed

oss2/api.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ class Bucket(_Base):
349349
STAT = 'stat'
350350
BUCKET_INFO = 'bucketInfo'
351351
PROCESS = 'x-oss-process'
352+
TAGGING = 'tagging'
352353

353354
def __init__(self, auth, endpoint, bucket_name,
354355
is_cname=False,
@@ -1595,6 +1596,50 @@ def process_object(self, key, process):
15951596
resp = self.__do_object('POST', key, params={Bucket.PROCESS: ''}, data=process_data)
15961597
logger.debug("Process object done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
15971598
return ProcessObjectResult(resp)
1599+
1600+
def put_object_tagging(self, key, input, headers=None):
1601+
"""
1602+
1603+
:param str key: 上传tagging的对象名称,不能为空。
1604+
1605+
:param input: tag 标签内容
1606+
:type input: :class:`ObjectTagging <oss2.models.ObjectTagging>` 对象
1607+
1608+
:return: :class:`RequestResult <oss2.models.RequestResult>`
1609+
"""
1610+
logger.debug("Start to put object tagging, bucket: {0}, key: {1}, tagging: {2}".format(
1611+
self.bucket_name, to_string(key), input))
1612+
1613+
if headers is not None:
1614+
headers = http.CaseInsensitiveDict(headers)
1615+
1616+
data = self.__convert_data(ObjectTagging, xml_utils.to_put_object_tagging, input)
1617+
resp = self.__do_object('PUT', key, data=data, params={Bucket.TAGGING: ''}, headers=headers)
1618+
1619+
return RequestResult(resp)
1620+
1621+
def get_object_tagging(self, key):
1622+
1623+
"""
1624+
:param str key: 要获取tagging的对象名称
1625+
:return: :class:`ObjectTagging <oss2.models.ObjectTagging>`
1626+
"""
1627+
logger.debug("Start to get object tagging, bucket: {0}, key: {1}".format(
1628+
self.bucket_name, to_string(key)))
1629+
resp = self.__do_object('GET', key, params={Bucket.TAGGING: ''})
1630+
1631+
return self._parse_result(resp, xml_utils.parse_get_object_tagging, GetObjectTaggingResult)
1632+
1633+
def delete_object_tagging(self, key):
1634+
"""
1635+
:param str key: 要删除tagging的对象名称
1636+
:return: :class:`ObjectTagging <oss2.models.ObjectTagging>`
1637+
"""
1638+
logger.debug("Start to delete object tagging, bucket: {0}, key: {1}".format(
1639+
self.bucket_name, to_string(key)))
1640+
resp = self.__do_object('DELETE', key, params={Bucket.TAGGING: ''})
1641+
logger.debug("Delete object tagging done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status))
1642+
return RequestResult(resp)
15981643

15991644
def _get_bucket_config(self, config):
16001645
"""获得Bucket某项配置,具体哪种配置由 `config` 指定。该接口直接返回 `RequestResult` 对象。

oss2/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Auth(AuthBase):
7272
'response-expires', 'response-content-disposition', 'cors', 'lifecycle',
7373
'restore', 'qos', 'referer', 'stat', 'bucketInfo', 'append', 'position', 'security-token',
7474
'live', 'comp', 'status', 'vod', 'startTime', 'endTime', 'x-oss-process',
75-
'symlink', 'callback', 'callback-var']
75+
'symlink', 'callback', 'callback-var', 'tagging']
7676
)
7777

7878
def _sign_request(self, req, bucket_name, key):

oss2/headers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
OSS_SERVER_SIDE_ENCRYPTION = "x-oss-server-side-encryption"
3131
OSS_SERVER_SIDE_ENCRYPTION_KEY_ID = "x-oss-server-side-encryption-key-id"
3232

33+
OSS_OBJECT_TAGGING = "x-oss-tagging"
34+
OSS_OBJECT_TAGGING_COPY_DIRECTIVE = "x-oss-tagging-directive"
35+
3336

3437
class RequestHeader(dict):
3538
def __init__(self, *arg, **kw):

oss2/models.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ class LifecycleRule(object):
552552
:param expiration: 过期删除操作。
553553
:type expiration: :class:`LifecycleExpiration`
554554
:param status: 启用还是禁止该规则。可选值为 `LifecycleRule.ENABLED` 或 `LifecycleRule.DISABLED`
555+
:param storage_transitions: 存储类型转换规则
556+
:type storage_transitions: :class:`StorageTransition`
557+
:param tagging: object tagging 规则
558+
:type tagging: :class:`ObjectTagging`
555559
"""
556560

557561
ENABLED = 'Enabled'
@@ -560,13 +564,14 @@ class LifecycleRule(object):
560564
def __init__(self, id, prefix,
561565
status=ENABLED, expiration=None,
562566
abort_multipart_upload=None,
563-
storage_transitions=None):
567+
storage_transitions=None, tagging=None):
564568
self.id = id
565569
self.prefix = prefix
566570
self.status = status
567571
self.expiration = expiration
568572
self.abort_multipart_upload = abort_multipart_upload
569573
self.storage_transitions = storage_transitions
574+
self.tagging = tagging
570575

571576

572577
class BucketLifecycle(object):
@@ -878,3 +883,42 @@ def __init__(self, resp):
878883
self.object = result['object']
879884
if 'status' in result:
880885
self.process_status = result['status']
886+
887+
_MAX_OBJECT_TAGGING_KEY_LENGTH=128
888+
_MAX_OBJECT_TAGGING_VALUE_LENGTH=256
889+
890+
class ObjectTagging(object):
891+
892+
def __init__(self, tagging_rules=None):
893+
894+
self.tag_set = tagging_rules or ObjectTaggingRule()
895+
896+
class ObjectTaggingRule(object):
897+
898+
def __init__(self):
899+
self.tagging_rule = dict()
900+
901+
def add(self, key, value):
902+
903+
if key is None or key == '':
904+
raise ClientError("ObjectTagging key should not be empty")
905+
906+
if len(key) > _MAX_OBJECT_TAGGING_KEY_LENGTH:
907+
raise ClientError("ObjectTagging key is too long")
908+
909+
if len(value) > _MAX_OBJECT_TAGGING_VALUE_LENGTH:
910+
raise ClientError("ObjectTagging value is too long")
911+
912+
self.tagging_rule[key] = value
913+
914+
def delete(self, key):
915+
del self.tagging_rule[key]
916+
917+
def len(self):
918+
return len(self.tagging_rule)
919+
920+
class GetObjectTaggingResult(RequestResult, ObjectTagging):
921+
922+
def __init__(self, resp):
923+
RequestResult.__init__(self, resp)
924+
ObjectTagging.__init__(self)

oss2/xml_utils.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
Owner,
3030
AccessControlList,
3131
AbortMultipartUpload,
32-
StorageTransition)
32+
StorageTransition,
33+
ObjectTagging,
34+
ObjectTaggingRule)
3335

3436
from .compat import urlunquote, to_unicode, to_string
3537
from .utils import iso8601_to_unixtime, date_to_iso8601, iso8601_to_date
@@ -422,21 +424,36 @@ def parse_lifecycle_storage_transitions(storage_transition_nodes):
422424

423425
return storage_transitions
424426

427+
def parse_lifecycle_object_taggings(lifecycle_tagging_nodes, url_encoded):
428+
429+
if lifecycle_tagging_nodes is None:
430+
return ObjectTagging()
431+
432+
tagging_rule = ObjectTaggingRule()
433+
for tag_node in lifecycle_tagging_nodes:
434+
key = _find_object(tag_node, 'Key', url_encoded)
435+
value = _find_object(tag_node, 'Value', url_encoded)
436+
tagging_rule.add(key, value)
437+
438+
return ObjectTagging(tagging_rule)
425439

426440
def parse_get_bucket_lifecycle(result, body):
427441
root = ElementTree.fromstring(body)
442+
url_encoded = _is_url_encoding(root)
428443

429444
for rule_node in root.findall('Rule'):
430445
expiration = parse_lifecycle_expiration(rule_node.find('Expiration'))
431446
abort_multipart_upload = parse_lifecycle_abort_multipart_upload(rule_node.find('AbortMultipartUpload'))
432447
storage_transitions = parse_lifecycle_storage_transitions(rule_node.findall('Transition'))
448+
tagging = parse_lifecycle_object_taggings(rule_node.findall('Tag'), url_encoded)
433449
rule = LifecycleRule(
434450
_find_tag(rule_node, 'ID'),
435451
_find_tag(rule_node, 'Prefix'),
436452
status=_find_tag(rule_node, 'Status'),
437453
expiration=expiration,
438454
abort_multipart_upload=abort_multipart_upload,
439-
storage_transitions=storage_transitions
455+
storage_transitions=storage_transitions,
456+
tagging=tagging
440457
)
441458
result.rules.append(rule)
442459

@@ -567,6 +584,13 @@ def to_put_bucket_lifecycle(bucket_lifecycle):
567584
_add_text_child(storage_transition_node, 'CreatedBeforeDate',
568585
date_to_iso8601(storage_transition.created_before_date))
569586

587+
tagging = rule.tagging
588+
if tagging:
589+
tagging_rule = tagging.tag_set.tagging_rule
590+
for key in tagging.tag_set.tagging_rule:
591+
tag_node = ElementTree.SubElement(rule_node, 'Tag')
592+
_add_text_child(tag_node, 'Key', key)
593+
_add_text_child(tag_node, 'Value', tagging_rule[key])
570594
return _node_to_string(root)
571595

572596

@@ -741,3 +765,32 @@ def to_get_select_json_object_meta(json_meta_param):
741765
raise SelectOperationClientError("The json_meta_param contains unsupported key " + key, "")
742766

743767
return _node_to_string(root)
768+
769+
def to_put_object_tagging(object_tagging):
770+
root = ElementTree.Element("Tagging")
771+
tag_set = ElementTree.SubElement(root, "TagSet")
772+
773+
for item in object_tagging.tag_set.tagging_rule:
774+
tag_xml = ElementTree.SubElement(tag_set, "Tag")
775+
_add_text_child(tag_xml, 'Key', item)
776+
_add_text_child(tag_xml, 'Value', object_tagging.tag_set.tagging_rule[item])
777+
778+
return _node_to_string(root)
779+
780+
def parse_get_object_tagging(result, body):
781+
root = ElementTree.fromstring(body)
782+
url_encoded = _is_url_encoding(root)
783+
tagset_node = root.find('TagSet')
784+
785+
if tagset_node is None:
786+
return result
787+
788+
tagging_rules = ObjectTaggingRule()
789+
for tag_node in tagset_node.findall('Tag'):
790+
key = _find_object(tag_node, 'Key', url_encoded)
791+
value = _find_object(tag_node, 'Value', url_encoded)
792+
tagging_rules.add(key, value)
793+
794+
result.tag_set = tagging_rules
795+
return result
796+

tests/__init__.py

Whitespace-only changes.

tests/common.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
OSS_ID = os.getenv("OSS_TEST_ACCESS_KEY_ID")
1717
OSS_SECRET = os.getenv("OSS_TEST_ACCESS_KEY_SECRET")
1818
OSS_ENDPOINT = os.getenv("OSS_TEST_ENDPOINT")
19-
OSS_BUCKET = os.getenv("OSS_TEST_BUCKET")
19+
OSS_TEST_BUCKET = os.getenv("OSS_TEST_BUCKET")
2020
OSS_CNAME = os.getenv("OSS_TEST_CNAME")
2121
OSS_CMK = os.getenv("OSS_TEST_CMK")
2222
OSS_REGION = os.getenv("OSS_TEST_REGION", "cn-hangzhou")
@@ -30,7 +30,11 @@
3030
def random_string(n):
3131
return ''.join(random.choice(string.ascii_lowercase) for i in range(n))
3232

33-
33+
OSS_BUCKET = ''
34+
if OSS_TEST_BUCKET is None:
35+
OSS_BUCKET = 'aliyun-oss-python-sdk-'+random_string(10)
36+
else:
37+
OSS_BUCKET = OSS_TEST_BUCKET + random_string(10)
3438

3539
def random_bytes(n):
3640
return oss2.to_bytes(random_string(n))
@@ -82,7 +86,7 @@ def setUp(self):
8286

8387
global OSS_AUTH_VERSION
8488
OSS_AUTH_VERSION = os.getenv('OSS_TEST_AUTH_VERSION')
85-
89+
8690
self.bucket = oss2.Bucket(oss2.make_auth(OSS_ID, OSS_SECRET, OSS_AUTH_VERSION), OSS_ENDPOINT, OSS_BUCKET)
8791

8892
try:

0 commit comments

Comments
 (0)