Skip to content

Commit

Permalink
Handle response XML inside _create_multipart_upload() API (#1009)
Browse files Browse the repository at this point in the history
  • Loading branch information
balamurugana authored Nov 10, 2020
1 parent 366c906 commit 1adadb8
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 129 deletions.
6 changes: 3 additions & 3 deletions minio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@
from .lifecycleconfig import LifecycleConfig
from .notificationconfig import NotificationConfig
from .objectlockconfig import ObjectLockConfig
from .parsers import (parse_error_response,
parse_new_multipart_upload)
from .parsers import parse_error_response
from .replicationconfig import ReplicationConfig
from .retention import Retention
from .select import SelectObjectReader
Expand Down Expand Up @@ -1213,7 +1212,8 @@ def _create_multipart_upload(self, bucket_name, object_name, headers):
headers=headers,
query_params={"uploads": ""},
)
return parse_new_multipart_upload(response.data)
element = ET.fromstring(response.data.decode())
return findtext(element, "UploadId")

def _put_object(self, bucket_name, object_name, data, headers,
query_params=None):
Expand Down
126 changes: 0 additions & 126 deletions minio/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,124 +25,9 @@
"""

from datetime import timezone
from urllib.parse import unquote
from xml.etree import ElementTree
from xml.etree.ElementTree import ParseError

from .error import S3Error
from .helpers import strptime_rfc3339

# dependencies.


_XML_NS = {
's3': 'http://s3.amazonaws.com/doc/2006-03-01/',
}


class S3Element:
"""S3 aware XML parsing class. Wraps a root element name and
ElementTree.Element instance. Provides S3 namespace aware parsing
functions.
"""

def __init__(self, root_name, element):
self.root_name = root_name
self.element = element

@classmethod
def fromstring(cls, root_name, data):
"""Initialize S3Element from name and XML string data.
:param name: Name for XML data. Used in XML errors.
:param data: string data to be parsed.
:return: Returns an S3Element.
"""
try:
return cls(root_name, ElementTree.fromstring(data.strip()))
except (ParseError, AttributeError, ValueError, TypeError) as exc:
raise ValueError(
'"{}" XML is not parsable.'.format(root_name),
) from exc

def findall(self, name):
"""Similar to ElementTree.Element.findall()
"""
return [
S3Element(self.root_name, elem)
for elem in self.element.findall('s3:{}'.format(name), _XML_NS)
]

def find(self, name):
"""Similar to ElementTree.Element.find()
"""
elt = self.element.find('s3:{}'.format(name), _XML_NS)
return S3Element(self.root_name, elt) if elt else None

def get_child_text(self, name, strict=True):
"""Extract text of a child element. If strict, and child element is
not present, raises ValueError and otherwise returns
None.
"""
if strict:
try:
return self.element.find('s3:{}'.format(name), _XML_NS).text
except (ParseError, AttributeError, ValueError, TypeError) as exc:
raise ValueError(
(
'Invalid XML provided for "{}" - erroring tag <{}>'
).format(self.root_name, name),
) from exc
else:
return self.element.findtext('s3:{}'.format(name), None, _XML_NS)

def get_urldecoded_elem_text(self, name, strict=True):
"""Like self.get_child_text(), but also performs urldecode() on the
result.
"""
text = self.get_child_text(name, strict)
# strictness is already enforced above.
return unquote(text) if text is not None else None

def get_etag_elem(self, strict=True):
"""Fetches an 'ETag' child element suitably processed.
"""
return self.get_child_text('ETag', strict).replace('"', '')

def get_int_elem(self, name):
"""Fetches an integer type XML child element by name.
"""
return int(self.get_child_text(name))

def get_time_elem(self, name):
"""Parse a time XML child element.
"""
return strptime_rfc3339(
self.get_child_text(name),
).replace(tzinfo=timezone.utc)

def text(self):
"""Fetch the current node's text
"""
return self.element.text

def is_dir(self):
"""Returns True if the object is a dir
ie, if an object name has `/` suffixed.
"""
text = self.get_child_text('Key')
return text.endswith("/")


def parse_error_response(response):
Expand All @@ -164,14 +49,3 @@ def _get_text(name):
object_name=_get_text("Key"),
response=response,
)


def parse_new_multipart_upload(data):
"""
Parser for new multipart upload response.
:param data: Response data for new multipart upload.
:return: Returns a upload id.
"""
root = S3Element.fromstring('InitiateMultipartUploadResult', data)
return root.get_child_text('UploadId')

0 comments on commit 1adadb8

Please sign in to comment.