python-botocore/botocore/payload.py
2015-10-08 11:15:31 -07:00

155 lines
4.9 KiB
Python

# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""
Payloads
--------
These payload objects are used to manage the payload (e.g. body) of
PUT and POST requests. These bodies are either:
* JSON documents that accumulate the values of 1 or more parameters.
In this situation, the values are accumulated in a Python dict and
then marshalled to a JSON string.
* XML documents that accumulate the values of 1 or more parameters.
In this case, the xml fragments are accumulated and a complete
XML document is assembled as a string.
* Literal values such as raw strings or file-like objects that need
to be copied into the request body.
"""
import logging
from botocore.compat import json
logger = logging.getLogger(__name__)
class Payload(object):
def __init__(self):
self._literal_value = None
@property
def literal_value(self):
return self._literal_value
@literal_value.setter
def literal_value(self, literal_value):
self._literal_value = literal_value
def add_param(self, param, value, label=None):
"""
Add a parameter to this JSON payload.
"""
self._literal_value = value
def getvalue(self):
return self._literal_value
class JSONPayload(Payload):
"""
A JSON payload.
The parameters are added to the payload one at a time and the
complete JSON body is returned as a string by the ``getvalue``
method.
"""
def __init__(self):
super(JSONPayload, self).__init__()
self._value = {}
def add_param(self, param, value, label=None):
"""
Add a parameter to this JSON payload.
"""
param.store_value_json(value, self._value, label)
def getvalue(self):
"""
Return the value of the payload as a JSON string.
"""
value = self._literal_value
if self._value:
value = json.dumps(self._value)
return value
class XMLPayload(Payload):
"""
XML Payload.
One or more parameters may be added to this payload and the
complete XML body is constructed and returned as a string by
the ``getvalue`` method.
There are two types of XML payloads encountered.
In the case of S3 and CloudFront requests, one (and only one)
parameter of an operation can have a ``payload=true`` attribute.
In this case, the value of that single parameter is the complete
body of the XML payload.
In the case of Route53 requests, the entire input is treated as
the XML payload. It will have one or more members whose values
must be added to the final XML document. In addition, the input
may have other parameters that need to be added to the URI or to
a header and these parameters are not added to the payload.
To distinquish between these two types, we use two factors:
* ``root_element_name`` attribute. Route53 payloads have a
``root_element_name`` attribute but S3 payloads do not.
* A ``payload=True`` attribute. Route53 payloads do not have
this attribute but S3 and CloudFront do.
I'm not sure if this is the best way to discriminate between the
two types but it seems effective.
Alternatively, the ``literal_value`` property can be set and this
value will be returned as-is by the ``getvalue`` method.
"""
def __init__(self, root_element_name, namespace=None):
super(XMLPayload, self).__init__()
self.root_element_name = root_element_name
self.namespace = namespace
self._elements = []
self._payload = False
def add_param(self, param, value, label=None):
if hasattr(param, 'payload') and param.payload:
self._payload = True
self._elements.append(param.to_xml(value, label))
def _assemble_xml(self):
s = '<%s' % self.root_element_name
if self.namespace:
s += ' xmlns="%s"' % self.namespace
s += '>'
for element in self._elements:
s += element
s += '</%s>' % self.root_element_name
logger.debug('assembled XML: %s', s)
return s
def getvalue(self):
value = self._literal_value
if len(self._elements) > 0:
if self.root_element_name and not self._payload:
value = self._assemble_xml()
else:
value = self._elements[0]
return value