Add media_mime_type keyword argument
Sometimes the Python mimetypes module cannot automatically detect the
MIME type of a media upload file, and the user would want to explicitly
specify it. An example is audio/x-raw.
This commit adds a media_mime_type keyword argument to media upload
methods. If the caller does not specify this argument, a warning is
logged to teach the user about it in case they need to explicitly
specify a MIME type.
diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py
index c451b47..4e7b736 100644
--- a/googleapiclient/discovery.py
+++ b/googleapiclient/discovery.py
@@ -108,6 +108,12 @@
'type': 'string',
'required': False,
}
+MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE = {
+ 'description': ('The MIME type of the media request body, or an instance '
+ 'of a MediaUpload object.'),
+ 'type': 'string',
+ 'required': False,
+}
# Parameters accepted by the stack, but not visible via discovery.
# TODO(dhermes): Remove 'userip' in 'v2'.
@@ -481,7 +487,7 @@
def _fix_up_media_upload(method_desc, root_desc, path_url, parameters):
- """Updates parameters of API by adding 'media_body' if supported by method.
+ """Adds 'media_body' and 'media_mime_type' parameters if supported by method.
SIDE EFFECTS: If the method supports media upload and has a required body,
sets body to be optional (required=False) instead. Also, if there is a
@@ -518,6 +524,7 @@
if media_upload:
media_path_url = _media_path_url_from_info(root_desc, path_url)
parameters['media_body'] = MEDIA_BODY_PARAMETER_DEFAULT_VALUE.copy()
+ parameters['media_mime_type'] = MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE.copy()
if 'body' in parameters:
parameters['body']['required'] = False
@@ -751,6 +758,7 @@
actual_path_params[parameters.argmap[key]] = cast_value
body_value = kwargs.get('body', None)
media_filename = kwargs.get('media_body', None)
+ media_mime_type = kwargs.get('media_mime_type', None)
if self._developerKey:
actual_query_params['key'] = self._developerKey
@@ -774,7 +782,11 @@
if media_filename:
# Ensure we end up with a valid MediaUpload object.
if isinstance(media_filename, six.string_types):
- (media_mime_type, encoding) = mimetypes.guess_type(media_filename)
+ if media_mime_type is None:
+ logger.warning(
+ 'media_mime_type argument not specified: trying to auto-detect for %s',
+ media_filename)
+ media_mime_type, _ = mimetypes.guess_type(media_filename)
if media_mime_type is None:
raise UnknownFileType(media_filename)
if not mimeparse.best_match([media_mime_type], ','.join(accept)):
diff --git a/tests/data/small-png b/tests/data/small-png
new file mode 100644
index 0000000..5446bc2
--- /dev/null
+++ b/tests/data/small-png
Binary files differ
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index c43d9e4..210717d 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -50,6 +50,7 @@
from googleapiclient.discovery import DISCOVERY_URI
from googleapiclient.discovery import key2param
from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
+from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE
from googleapiclient.discovery import ResourceMethodParameters
from googleapiclient.discovery import STACK_QUERY_PARAMETERS
from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
@@ -61,6 +62,7 @@
from googleapiclient.errors import ResumableUploadError
from googleapiclient.errors import UnacceptableMimeTypeError
from googleapiclient.errors import UnknownApiNameOrVersion
+from googleapiclient.errors import UnknownFileType
from googleapiclient.http import BatchHttpRequest
from googleapiclient.http import HttpMock
from googleapiclient.http import HttpMockSequence
@@ -212,14 +214,16 @@
def test_fix_up_media_upload_no_initial_valid_minimal(self):
valid_method_desc = {'mediaUpload': {'accept': []}}
- final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
+ final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
+ 'media_mime_type': MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE}
self._base_fix_up_method_description_test(
valid_method_desc, {}, final_parameters, [], 0,
'http://root/upload/fake/fake-path/')
def test_fix_up_media_upload_no_initial_valid_full(self):
valid_method_desc = {'mediaUpload': {'accept': ['*/*'], 'maxSize': '10GB'}}
- final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
+ final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
+ 'media_mime_type': MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE}
ten_gb = 10 * 2**30
self._base_fix_up_method_description_test(
valid_method_desc, {}, final_parameters, ['*/*'],
@@ -236,7 +240,8 @@
valid_method_desc = {'mediaUpload': {'accept': []}}
initial_parameters = {'body': {}}
final_parameters = {'body': {'required': False},
- 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
+ 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
+ 'media_mime_type': MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE}
self._base_fix_up_method_description_test(
valid_method_desc, initial_parameters, final_parameters, [], 0,
'http://root/upload/fake/fake-path/')
@@ -245,7 +250,8 @@
valid_method_desc = {'mediaUpload': {'accept': ['*/*'], 'maxSize': '10GB'}}
initial_parameters = {'body': {}}
final_parameters = {'body': {'required': False},
- 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
+ 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
+ 'media_mime_type': MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE}
ten_gb = 10 * 2**30
self._base_fix_up_method_description_test(
valid_method_desc, initial_parameters, final_parameters, ['*/*'],
@@ -775,6 +781,24 @@
'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
request.uri)
+ def test_simple_media_unknown_mimetype(self):
+ self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
+ zoo = build('zoo', 'v1', http=self.http)
+
+ try:
+ zoo.animals().insert(media_body=datafile('small-png'))
+ self.fail("should throw exception if mimetype is unknown.")
+ except UnknownFileType:
+ pass
+
+ request = zoo.animals().insert(media_body=datafile('small-png'),
+ media_mime_type='image/png')
+ self.assertEquals('image/png', request.headers['content-type'])
+ self.assertEquals(b'PNG', request.body[1:4])
+ assertUrisEqual(self,
+ 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
+ request.uri)
+
def test_multipart_media_raise_correct_exceptions(self):
self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
zoo = build('zoo', 'v1', http=self.http)