Skip to content

Commit

Permalink
Add media_mime_type keyword argument
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Brian J. Watson authored and nathanielmanistaatgoogle committed Oct 31, 2016
1 parent 5f00cad commit 38051ac
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 6 deletions.
16 changes: 14 additions & 2 deletions googleapiclient/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
Expand Down Expand Up @@ -481,7 +487,7 @@ def _fix_up_parameters(method_desc, root_desc, http_method):


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
Expand Down Expand Up @@ -518,6 +524,7 @@ def _fix_up_media_upload(method_desc, root_desc, path_url, parameters):
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

Expand Down Expand Up @@ -751,6 +758,7 @@ def method(self, **kwargs):
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
Expand All @@ -774,7 +782,11 @@ def method(self, **kwargs):
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)):
Expand Down
Binary file added tests/data/small-png
Binary file not shown.
32 changes: 28 additions & 4 deletions tests/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -212,14 +214,16 @@ def test_fix_up_media_upload_no_initial_invalid(self):

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, ['*/*'],
Expand All @@ -236,7 +240,8 @@ def test_fix_up_media_upload_with_initial_valid_minimal(self):
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/')
Expand All @@ -245,7 +250,8 @@ def test_fix_up_media_upload_with_initial_valid_full(self):
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, ['*/*'],
Expand Down Expand Up @@ -775,6 +781,24 @@ def test_simple_media_good_upload(self):
'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)
Expand Down

0 comments on commit 38051ac

Please sign in to comment.