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()