diff --git a/.travis.yml b/.travis.yml
index cee5df18..933f1662 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,7 @@ install:
- pip install nose
- pip install pycodestyle
- pip install importlib_metadata
-- pip install dicttoxml
+- pip install xmltodict
- pip install crcmod
- pip install pycryptodome
notifications:
diff --git a/qcloud_cos/.travis.yml b/qcloud_cos/.travis.yml
index 51d64fed..23e3daad 100644
--- a/qcloud_cos/.travis.yml
+++ b/qcloud_cos/.travis.yml
@@ -9,7 +9,7 @@ install:
- pip install six
- pip install nose
- pip install pycodestyle
- - pip install dicttoxml
+ - pip install xmltodict
- pip install crcmod
notifications:
email:
diff --git a/qcloud_cos/cos_client.py b/qcloud_cos/cos_client.py
index 8e3b7cd9..b6a93e49 100644
--- a/qcloud_cos/cos_client.py
+++ b/qcloud_cos/cos_client.py
@@ -17,7 +17,6 @@
from six.moves.urllib.parse import quote, unquote, urlencode
from six import text_type, binary_type
from hashlib import md5
-from dicttoxml import dicttoxml
from .streambody import StreamBody
from .xml2dict import Xml2Dict
from .cos_auth import CosS3Auth
@@ -747,8 +746,7 @@ def delete_objects(self, Bucket, Delete={}, **kwargs):
Delete=objects
)
"""
- lst = [''] # 类型为list的标签
- xml_config = format_xml(data=Delete, root='Delete', lst=lst)
+ xml_config = format_xml(data=Delete, root='Delete')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -1141,12 +1139,9 @@ def put_object_acl(self, Bucket, Key, AccessControlPolicy={}, **kwargs):
GrantRead='id="qcs::cam::uin/123:uin/456",id="qcs::cam::uin/123:uin/123"'
)
"""
- lst = [ # 类型为list的标签
- '',
- '']
xml_config = ""
if AccessControlPolicy:
- xml_config = format_xml(data=AccessControlPolicy, root='AccessControlPolicy', lst=lst)
+ xml_config = format_xml(data=AccessControlPolicy, root='AccessControlPolicy')
headers = mapped(kwargs)
params = {'acl': ''}
url = self._conf.uri(bucket=Bucket, path=Key)
@@ -1610,12 +1605,9 @@ def put_bucket_acl(self, Bucket, AccessControlPolicy={}, **kwargs):
GrantRead='id="qcs::cam::uin/123:uin/456",id="qcs::cam::uin/123:uin/123"'
)
"""
- lst = [ # 类型为list的标签
- '',
- '']
xml_config = ""
if AccessControlPolicy:
- xml_config = format_xml(data=AccessControlPolicy, root='AccessControlPolicy', lst=lst)
+ xml_config = format_xml(data=AccessControlPolicy, root='AccessControlPolicy')
headers = mapped(kwargs)
params = {'acl': ''}
url = self._conf.uri(bucket=Bucket)
@@ -1699,18 +1691,7 @@ def put_bucket_cors(self, Bucket, CORSConfiguration={}, **kwargs):
CORSConfiguration=cors_config
)
"""
- lst = [ # 类型为list的标签
- '',
- '',
- '',
- '',
- '',
- '',
- '',
- '',
- '',
- '']
- xml_config = format_xml(data=CORSConfiguration, root='CORSConfiguration', lst=lst)
+ xml_config = format_xml(data=CORSConfiguration, root='CORSConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -1833,17 +1814,7 @@ def put_bucket_lifecycle(self, Bucket, LifecycleConfiguration={}, **kwargs):
)
"""
# 类型为list的标签
- lst = [
- '',
- '',
- '',
- '',
- '',
- '',
- '',
- ''
- ]
- xml_config = format_xml(data=LifecycleConfiguration, root='LifecycleConfiguration', lst=lst)
+ xml_config = format_xml(data=LifecycleConfiguration, root='LifecycleConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2068,8 +2039,7 @@ def put_bucket_replication(self, Bucket, ReplicationConfiguration={}, **kwargs):
ReplicationConfiguration=replication_config
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=ReplicationConfiguration, root='ReplicationConfiguration', lst=lst)
+ xml_config = format_xml(data=ReplicationConfiguration, root='ReplicationConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2197,7 +2167,12 @@ def put_bucket_website(self, Bucket, WebsiteConfiguration={}, **kwargs):
WebsiteConfiguration=website_config
)
"""
- xml_config = format_xml(data=WebsiteConfiguration, root='WebsiteConfiguration', parent_child=True)
+ # 重构 WebsiteConfiguration['RoutingRules']
+ WebsiteConfigurationCpy = copy.deepcopy(WebsiteConfiguration)
+ if 'RoutingRules' in WebsiteConfigurationCpy.keys():
+ WebsiteConfigurationCpy['RoutingRules'] = {'RoutingRule': WebsiteConfigurationCpy['RoutingRules']}
+
+ xml_config = format_xml(data=WebsiteConfigurationCpy, root='WebsiteConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2496,8 +2471,7 @@ def put_bucket_domain(self, Bucket, DomainConfiguration={}, **kwargs):
DomainConfiguration=domain_config
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=DomainConfiguration, root='DomainConfiguration', lst=lst)
+ xml_config = format_xml(data=DomainConfiguration, root='DomainConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2600,8 +2574,7 @@ def put_bucket_domain_certificate(self, Bucket, DomainCertificateConfiguration,
DomainCertificateConfiguration=domain_cert_config
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=DomainCertificateConfiguration, root='DomainCertificate', lst=lst)
+ xml_config = format_xml(data=DomainCertificateConfiguration, root='DomainCertificate')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2714,8 +2687,7 @@ def put_bucket_origin(self, Bucket, OriginConfiguration={}, **kwargs):
OriginConfiguration=origin_config
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=OriginConfiguration, root='OriginConfiguration', lst=lst)
+ xml_config = format_xml(data=OriginConfiguration, root='OriginConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2848,9 +2820,8 @@ def put_bucket_inventory(self, Bucket, Id, InventoryConfiguration={}, **kwargs):
InventoryConfiguration=inventory_config
)
"""
- lst = ['', ''] # 类型为list的标签
InventoryConfiguration['Id'] = Id
- xml_config = format_xml(data=InventoryConfiguration, root='InventoryConfiguration', lst=lst)
+ xml_config = format_xml(data=InventoryConfiguration, root='InventoryConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -2965,8 +2936,7 @@ def put_object_tagging(self, Bucket, Key, Tagging={}, **kwargs):
Tagging=tagging_set
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=Tagging, root='Tagging', lst=lst)
+ xml_config = format_xml(data=Tagging, root='Tagging')
headers = mapped(kwargs)
params = {'tagging': ''}
if 'versionId' in headers:
@@ -3089,8 +3059,7 @@ def put_bucket_tagging(self, Bucket, Tagging={}, **kwargs):
Tagging=tagging_set
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=Tagging, root='Tagging', lst=lst)
+ xml_config = format_xml(data=Tagging, root='Tagging')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -3203,8 +3172,7 @@ def put_bucket_referer(self, Bucket, RefererConfiguration={}, **kwargs):
RefererConfiguration=referer_config
)
"""
- lst = ['', ''] # 类型为list的标签
- xml_config = format_xml(data=RefererConfiguration, root='RefererConfiguration', lst=lst)
+ xml_config = format_xml(data=RefererConfiguration, root='RefererConfiguration')
headers = mapped(kwargs)
headers['Content-MD5'] = get_md5(xml_config)
headers['Content-Type'] = 'application/xml'
@@ -4059,13 +4027,7 @@ def put_bucket_encryption(self, Bucket, ServerSideEncryptionConfiguration={}, **
:param kwargs(dict): 设置请求的headers.
:return: None.
"""
- # 类型为list的标签
- lst = [
- '',
- ''
- ]
- xml_config = format_xml(data=ServerSideEncryptionConfiguration, root='ServerSideEncryptionConfiguration',
- lst=lst)
+ xml_config = format_xml(data=ServerSideEncryptionConfiguration, root='ServerSideEncryptionConfiguration')
headers = mapped(kwargs)
params = {'encryption': ''}
url = self._conf.uri(bucket=Bucket)
@@ -6012,21 +5974,7 @@ def ci_auditing_image_batch(self, Bucket, Input, DetectType=None, BizType=None,
'Conf': conf
}
- lst = [ # 类型为list的标签
- '',
- '',
- '',
- '',
- '',
- '']
-
- xml_request = format_xml(data=request, root='Request', lst=lst)
+ xml_request = format_xml(data=request, root='Request')
headers['Content-Type'] = 'application/xml'
path = 'image/auditing'
@@ -6586,7 +6534,7 @@ def ci_create_media_jobs(self, Bucket, Jobs={}, Lst={}, **kwargs):
:param Bucket(string): 存储桶名称.
:param Jobs(dict): 创建任务的配置.
- :param Lst(dict): 创建任务dict转xml时去除Key数组.
+ :param Lst(dict): 创建任务dict转xml时去除Key数组. TODO 替换成 xmltodict 库后可以将 Lst 参数去掉
:param kwargs(dict): 设置请求的headers.
:return(dict): 查询成功返回的结果,dict类型.
@@ -6613,7 +6561,7 @@ def ci_create_media_jobs(self, Bucket, Jobs={}, Lst={}, **kwargs):
headers = final_headers
params = format_values(params)
- xml_config = format_xml(data=Jobs, root='Request', lst=Lst)
+ xml_config = format_xml(data=Jobs, root='Request')
path = "/jobs"
url = self._conf.uri(bucket=Bucket, path=path, endpoint=self._conf._endpoint_ci)
logger.info("create_media_jobs result, url=:{url} ,headers=:{headers}, params=:{params}, xml_config=:{xml_config}".format(
@@ -6640,7 +6588,7 @@ def ci_create_media_pic_jobs(self, Bucket, Jobs={}, Lst={}, **kwargs):
:param Bucket(string): 存储桶名称.
:param Jobs(dict): 创建任务的配置.
- :param Lst(dict): 创建任务dict转xml时去除Key数组.
+ :param Lst(dict): 创建任务dict转xml时去除Key数组. TODO 替换为 xmltodict 库后可以将 Lst 参数去掉
:param kwargs(dict): 设置请求的headers.
:return(dict): 查询成功返回的结果,dict类型.
@@ -6667,7 +6615,7 @@ def ci_create_media_pic_jobs(self, Bucket, Jobs={}, Lst={}, **kwargs):
headers = final_headers
params = format_values(params)
- xml_config = format_xml(data=Jobs, root='Request', lst=Lst)
+ xml_config = format_xml(data=Jobs, root='Request')
path = "/pic_jobs"
url = self._conf.uri(bucket=Bucket, path=path, endpoint=self._conf._endpoint_ci)
logger.info("create_media_pic_jobs result, url=:{url} ,headers=:{headers}, params=:{params}, xml_config=:{xml_config}".format(
diff --git a/qcloud_cos/cos_comm.py b/qcloud_cos/cos_comm.py
index 4d2ad6dc..c758d4bd 100644
--- a/qcloud_cos/cos_comm.py
+++ b/qcloud_cos/cos_comm.py
@@ -12,7 +12,7 @@
import xml.dom.minidom
import xml.etree.ElementTree
from datetime import datetime
-from dicttoxml import dicttoxml
+from xmltodict import unparse
from .xml2dict import Xml2Dict
from .cos_exception import CosClientError
from .cos_exception import CosServiceError
@@ -202,14 +202,21 @@ def mapped(headers):
return _headers
-def format_xml(data, root, lst=list(), parent_child=False):
+# def format_xml(data, root, lst=list(), parent_child=False):
+# """将dict转换为xml, xml_config是一个bytes"""
+# if parent_child:
+# xml_config = dicttoxml(data, item_func=lambda x: x[:-2], custom_root=root, attr_type=False)
+# else:
+# xml_config = dicttoxml(data, item_func=lambda x: x, custom_root=root, attr_type=False)
+# for i in lst:
+# xml_config = xml_config.replace(to_bytes(i + i), to_bytes(i))
+# return xml_config
+
+
+def format_xml(data, root):
"""将dict转换为xml, xml_config是一个bytes"""
- if parent_child:
- xml_config = dicttoxml(data, item_func=lambda x: x[:-1], custom_root=root, attr_type=False)
- else:
- xml_config = dicttoxml(data, item_func=lambda x: x, custom_root=root, attr_type=False)
- for i in lst:
- xml_config = xml_config.replace(to_bytes(i + i), to_bytes(i))
+ input_dict = {root: data}
+ xml_config = unparse(input_dict=input_dict).encode('utf-8')
return xml_config
diff --git a/requirements.txt b/requirements.txt
index 50d0098c..e7cdece1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-dicttoxml
+xmltodict
six
crcmod
pycryptodome
\ No newline at end of file
diff --git a/ut/test.py b/ut/test.py
index ee5b433b..20ff6343 100644
--- a/ut/test.py
+++ b/ut/test.py
@@ -514,7 +514,7 @@ def test_put_get_delete_cors():
)
assert response
# delete cors
- response = client.get_bucket_cors(
+ response = client.delete_bucket_cors(
Bucket=test_bucket
)
@@ -2254,57 +2254,85 @@ def test_sse_c_file():
if __name__ == "__main__":
setUp()
"""
- test_put_object_enable_md5()
- test_upload_with_server_side_encryption()
- test_upload_empty_file()
test_put_get_delete_object_10MB()
+ test_put_object_speacil_names()
+ test_get_object_special_names()
+ test_delete_object_special_names()
+ test_put_object_non_exist_bucket()
+ test_put_object_acl()
+ test_get_object_acl()
+ test_copy_object_diff_bucket()
+ test_create_abort_multipart_upload()
+ test_create_complete_multipart_upload()
+ test_upload_part_copy()
+ test_delete_multiple_objects()
+ test_create_head_delete_bucket()
+ test_create_head_delete_maz_bucket()
+ test_put_bucket_acl_illegal()
+ test_get_bucket_acl_normal()
+ test_list_objects()
+ test_list_objects_versions()
+ test_get_presigned_url()
+ test_get_bucket_location()
+ test_get_service()
+ test_put_get_delete_cors()
+ test_put_get_delete_lifecycle()
test_put_get_versioning()
test_put_get_delete_replication()
- test_upload_part_copy()
+ test_put_get_delete_website()
+ test_list_multipart_uploads()
+ test_upload_file_from_buffer()
test_upload_file_multithreading()
test_upload_file_with_progress_callback()
test_copy_file_automatically()
- test_copy_10G_file_in_same_region()
- test_list_objects()
+ test_upload_empty_file()
test_use_get_auth()
+ test_upload_with_server_side_encryption()
test_put_get_bucket_logging()
- test_put_get_delete_website()
- test_put_get_bucket_policy()
+ test_put_object_enable_md5()
+ test_put_object_from_local_file()
+ test_object_exists()
+ test_bucket_exists()
+ test_put_get_delete_bucket_policy()
test_put_file_like_object()
test_put_chunked_object()
+ test_put_get_gzip_file()
+ test_put_get_delete_bucket_domain()
+ test_put_get_delete_bucket_domain_certificate()
test_put_get_delete_bucket_inventory()
+ test_put_get_delete_bucket_tagging()
+ test_put_get_delete_object_tagging()
+ test_put_get_delete_bucket_referer()
test_put_get_traffic_limit()
- test_put_get_delete_bucket_domain()
test_select_object()
- _test_get_object_sensitive_content_recognition()
- test_live_channel()
test_download_file()
- test_put_get_bucket_intelligenttiering()
+ test_bucket_encryption()
test_aes_client()
test_rsa_client()
- test_qrcode()
+ test_live_channel()
+ test_get_object_url()
+ test_ci_put_image_style()
+ test_ci_get_image_style()
+ test_ci_get_image_info()
+ test_ci_get_image_exif_info()
+ test_ci_get_image_ave_info()
+ test_ci_image_assess_quality()
+ test_ci_qrcode_generate()
+ test_ci_ocr_process()
test_ci_get_media_queue()
+ test_ci_get_media_pic_queue()
test_ci_create_media_transcode_watermark_jobs()
test_ci_create_media_transcode_jobs()
+ test_ci_create_media_pic_jobs()
+ test_ci_list_media_pic_jobs()
test_ci_list_media_transcode_jobs()
- test_ci_create_doc_transcode_jobs()
- test_ci_list_doc_transcode_jobs()
- test_ci_live_video_auditing()
test_get_media_info()
test_get_snapshot()
test_get_pm3u8()
test_ci_get_media_bucket()
+ test_ci_create_doc_transcode_jobs()
+ test_ci_list_doc_transcode_jobs()
+ test_ci_live_video_auditing()
test_sse_c_file()
- test_ci_list_media_pic_jobs()
- test_ci_create_media_pic_jobs()
- test_ci_get_media_pic_queue()
- test_ci_put_image_style()
- test_ci_get_image_style()
- test_ci_get_image_info()
- test_ci_get_image_exif_info()
- test_ci_get_image_ave_info()
- test_ci_qrcode_generate()
- test_ci_image_assess_quality()
- test_ci_ocr_process()
"""
tearDown()