def test_resumable_multipart_media_good_upload(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) media_upload = MediaFileUpload(datafile('small.png'), resumable=True) request = zoo.animals().insert(media_body=media_upload, body={}) self.assertTrue( request.headers['content-type'].startswith('application/json')) self.assertEquals('{"data": {}}', request.body) self.assertEquals(media_upload, request.resumable) # TODO: Google API does not recognize the PNG content type #self.assertEquals('image/png', request.resumable.mimetype()) #self.assertNotEquals(request.body, None) #self.assertEquals(request.resumable_uri, None) http = HttpMockSequence([ ({ 'status': '200', 'location': 'http://upload.example.com' }, ''), ({ 'status': '308', 'location': 'http://upload.example.com/2', 'range': '0-12' }, ''), ({ 'status': '308', 'location': 'http://upload.example.com/3', 'range': '0-%d' % (media_upload.size() - 2) }, ''), ({ 'status': '200' }, '{"foo": "bar"}'), ]) status, body = request.next_chunk(http=http) self.assertEquals(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) self.assertEquals(13, status.resumable_progress) # Two requests should have been made and the resumable_uri should have been # updated for each one. self.assertEquals(request.resumable_uri, 'http://upload.example.com/2') self.assertEquals(media_upload, request.resumable) self.assertEquals(13, request.resumable_progress) status, body = request.next_chunk(http=http) self.assertEquals(request.resumable_uri, 'http://upload.example.com/3') self.assertEquals(media_upload.size() - 1, request.resumable_progress) self.assertEquals('{"data": {}}', request.body) # Final call to next_chunk should complete the upload. status, body = request.next_chunk(http=http) self.assertEquals(body, {"foo": "bar"}) self.assertEquals(status, None)
def test_resumable_media_good_upload(self): """Not a multipart upload.""" self.http = HttpMock(datafile("zoo.json"), {"status": "200"}) zoo = build("zoo", "v1", http=self.http) media_upload = MediaFileUpload(datafile("small.png"), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) self.assertEqual(media_upload, request.resumable) # TODO: Google API does not recognize the PNG content type # self.assertEqual('image/png', request.resumable.mimetype()) # self.assertEqual(request.body, None) # self.assertEqual(request.resumable_uri, None) http = HttpMockSequence( [ ({"status": "200", "location": "http://upload.example.com"}, ""), ({"status": "308", "location": "http://upload.example.com/2", "range": "0-12"}, ""), ( { "status": "308", "location": "http://upload.example.com/3", "range": "0-%d" % (media_upload.size() - 2), }, "", ), ({"status": "200"}, '{"foo": "bar"}'), ] ) status, body = request.next_chunk(http=http) self.assertEqual(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) self.assertEqual(13, status.resumable_progress) # Two requests should have been made and the resumable_uri should have been # updated for each one. self.assertEqual(request.resumable_uri, "http://upload.example.com/2") self.assertEqual(media_upload, request.resumable) self.assertEqual(13, request.resumable_progress) status, body = request.next_chunk(http=http) self.assertEqual(request.resumable_uri, "http://upload.example.com/3") self.assertEqual(media_upload.size() - 1, request.resumable_progress) self.assertEqual(request.body, None) # Final call to next_chunk should complete the upload. status, body = request.next_chunk(http=http) self.assertEqual(body, {"foo": "bar"}) self.assertEqual(status, None)
def test_resumable_multipart_media_good_upload(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) media_upload = MediaFileUpload(datafile('small.png'), resumable=True) request = zoo.animals().insert(media_body=media_upload, body={}) self.assertTrue(request.headers['content-type'].startswith( 'application/json')) self.assertEquals('{"data": {}}', request.body) self.assertEquals(media_upload, request.resumable) # TODO: Google API does not recognize the PNG content type #self.assertEquals('image/png', request.resumable.mimetype()) #self.assertNotEquals(request.body, None) #self.assertEquals(request.resumable_uri, None) http = HttpMockSequence([ ({'status': '200', 'location': 'http://upload.example.com'}, ''), ({'status': '308', 'location': 'http://upload.example.com/2', 'range': '0-12'}, ''), ({'status': '308', 'location': 'http://upload.example.com/3', 'range': '0-%d' % (media_upload.size() - 2)}, ''), ({'status': '200'}, '{"foo": "bar"}'), ]) status, body = request.next_chunk(http=http) self.assertEquals(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) self.assertEquals(13, status.resumable_progress) # Two requests should have been made and the resumable_uri should have been # updated for each one. self.assertEquals(request.resumable_uri, 'http://upload.example.com/2') self.assertEquals(media_upload, request.resumable) self.assertEquals(13, request.resumable_progress) status, body = request.next_chunk(http=http) self.assertEquals(request.resumable_uri, 'http://upload.example.com/3') self.assertEquals(media_upload.size()-1, request.resumable_progress) self.assertEquals('{"data": {}}', request.body) # Final call to next_chunk should complete the upload. status, body = request.next_chunk(http=http) self.assertEquals(body, {"foo": "bar"}) self.assertEquals(status, None)
def test_resumable_media_good_upload_from_execute(self): """Not a multipart upload.""" self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) media_upload = MediaFileUpload(datafile('small.png'), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) assertUrisEqual( self, 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json', request.uri) http = HttpMockSequence([ ({ 'status': '200', 'location': 'http://upload.example.com' }, ''), ({ 'status': '308', 'location': 'http://upload.example.com/2', 'range': '0-12' }, ''), ({ 'status': '308', 'location': 'http://upload.example.com/3', 'range': '0-%d' % media_upload.size() }, ''), ({ 'status': '200' }, '{"foo": "bar"}'), ]) body = request.execute(http=http) self.assertEquals(body, {"foo": "bar"})
def test_resumable_media_good_upload_from_execute(self): """Not a multipart upload.""" self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) media_upload = MediaFileUpload(datafile('small.png'), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) assertUrisEqual(self, 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json', request.uri) http = HttpMockSequence([ ({'status': '200', 'location': 'http://upload.example.com'}, ''), ({'status': '308', 'location': 'http://upload.example.com/2', 'range': '0-12'}, ''), ({'status': '308', 'location': 'http://upload.example.com/3', 'range': '0-%d' % media_upload.size()}, ''), ({'status': '200'}, '{"foo": "bar"}'), ]) body = request.execute(http=http) self.assertEquals(body, {"foo": "bar"})
def updateFile(self, localFilePath, remoteFile, timestamp): body = { 'parents': [{ 'id': self.id }], 'title': remoteFile.name, 'modifiedDate': datetime.utcfromtimestamp(timestamp).isoformat() + '.000Z' } media_body = MediaFileUpload(localFilePath, mimetype='*/*', resumable=True) if media_body != None and media_body.size() > 0: request = self.service.files().update(fileId=remoteFile.id, body=body, media_body=media_body, setModifiedDate=True) response = None while response is None: status, response = request.next_chunk() item = response return self.getFileFromItem(item) else: request = self.service.files().update(fileId=remoteFile.id, body=body, setModifiedDate=True) response = request.execute() return self.getFileFromItem(response)
def test_media_file_upload_to_from_json(self): upload = MediaFileUpload(datafile("small.png"), chunksize=500, resumable=True) self.assertEqual("image/png", upload.mimetype()) self.assertEqual(190, upload.size()) self.assertEqual(True, upload.resumable()) self.assertEqual(500, upload.chunksize()) self.assertEqual("PNG", upload.getbytes(1, 3)) json = upload.to_json() new_upload = MediaUpload.new_from_json(json) self.assertEqual("image/png", new_upload.mimetype()) self.assertEqual(190, new_upload.size()) self.assertEqual(True, new_upload.resumable()) self.assertEqual(500, new_upload.chunksize()) self.assertEqual("PNG", new_upload.getbytes(1, 3))
def test_media_file_upload_to_from_json(self): upload = MediaFileUpload( datafile('small.png'), chunksize=500, resumable=True) self.assertEqual('image/png', upload.mimetype()) self.assertEqual(190, upload.size()) self.assertEqual(True, upload.resumable()) self.assertEqual(500, upload.chunksize()) self.assertEqual('PNG', upload.getbytes(1, 3)) json = upload.to_json() new_upload = MediaUpload.new_from_json(json) self.assertEqual('image/png', new_upload.mimetype()) self.assertEqual(190, new_upload.size()) self.assertEqual(True, new_upload.resumable()) self.assertEqual(500, new_upload.chunksize()) self.assertEqual('PNG', new_upload.getbytes(1, 3))
def update(self, new_path=None, parent_id='root'): try: if not hasattr(self, 'id'): return self.create(parent_id) existing_file = self.get_file(self.id) if new_path is not None: self.path = new_path mime_type = defaul_mime_type media_body = None if not os.path.isdir(self.path): media_body = MediaFileUpload(self.path, resumable=True) if media_body.size() == 0: logger.error('cannot update no content file %s', self.path) return None if media_body.mimetype() is not None: mime_type = media_body.mimetype() else: media_body._mimetype = mime_type else: mime_type = folder_mime_type existing_file['title'] = os.path.basename(self.path) existing_file['parents'] = [{'id': parent_id}] existing_file['mimeType'] = mime_type logger.info('updated %s', self.path) with drive.lock: metadata = drive.service.files().update( fileId=self.id, body=existing_file, media_body=media_body).execute() self.id = metadata['id'] if metadata.has_key('downloadUrl'): self.download_url = metadata['downloadUrl'] if metadata.has_key('md5Checksum'): self.md5Checksum = metadata['md5Checksum'] return metadata except errors.HttpError, error: logger.error('an error occurred: %s', error) return None
def updateFile(self, localFilePath, remoteFile, timestamp): body = { "parents": [{"id": self.id}], "title": remoteFile.name, "modifiedDate": datetime.utcfromtimestamp(timestamp).isoformat() + ".000Z", } media_body = MediaFileUpload(localFilePath, mimetype="*/*", resumable=True) if media_body != None and media_body.size() > 0: request = self.service.files().update( fileId=remoteFile.id, body=body, media_body=media_body, setModifiedDate=True ) response = None while response is None: status, response = request.next_chunk() item = response return self.getFileFromItem(item) else: request = self.service.files().update(fileId=remoteFile.id, body=body, setModifiedDate=True) response = request.execute() return self.getFileFromItem(response)
def create(self, parent_id='root'): mime_type = defaul_mime_type media_body = None if not os.path.isdir(self.path): media_body = MediaFileUpload(self.path, resumable=True) if media_body.size() == 0: logger.error('cannot create no content file %s', self.path) return None if media_body.mimetype() is not None: mime_type = media_body.mimetype() else: media_body._mimetype = mime_type else: mime_type = folder_mime_type body = { 'title': os.path.basename(self.path), 'mimeType': mime_type, 'parents': [{'id': parent_id}] } try: with drive.lock: metadata = drive.service.files().insert( body=body, media_body=media_body).execute() logger.info('created %s, %s', self.path, body['mimeType']) self.id = metadata['id'] if metadata.has_key('downloadUrl'): self.download_url = metadata['downloadUrl'] if metadata.has_key('md5Checksum'): self.md5Checksum = metadata['md5Checksum'] return metadata except errors.HttpError, error: logger.error('an error occurred: %s', error) return None
def method(self, **kwargs): # Don't bother with doc string, it will be over-written by createMethod. for name in kwargs.iterkeys(): if name not in parameters.argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) # Remove args that have a value of None. keys = kwargs.keys() for name in keys: if kwargs[name] is None: del kwargs[name] for name in parameters.required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in parameters.pattern_params.iteritems(): if name in kwargs: if isinstance(kwargs[name], basestring): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex)) for name, enums in parameters.enum_params.iteritems(): if name in kwargs: # We need to handle the case of a repeated enum # name differently, since we want to handle both # arg='value' and arg=['value1', 'value2'] if (name in parameters.repeated_params and not isinstance(kwargs[name], basestring)): values = kwargs[name] else: values = [kwargs[name]] for value in values: if value not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, value, str(enums))) actual_query_params = {} actual_path_params = {} for key, value in kwargs.iteritems(): to_type = parameters.param_types.get(key, 'string') # For repeated parameters we cast each member of the list. if key in parameters.repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in parameters.query_params: actual_query_params[parameters.argmap[key]] = cast_value if key in parameters.path_params: actual_path_params[parameters.argmap[key]] = cast_value body_value = kwargs.get('body', None) media_filename = kwargs.get('media_body', None) if self._developerKey: actual_query_params['key'] = self._developerKey model = self._model if methodName.endswith('_media'): model = MediaModel() elif 'response' not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request(headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = _urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = '' if media_filename: # Ensure we end up with a valid MediaUpload object. if isinstance(media_filename, basestring): (media_mime_type, encoding) = 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)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, mimetype=media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError('media_filename must be str or MediaUpload.') # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads expanded_url = uritemplate.expand(mediaPathUrl, params) url = _urljoin(self._baseUrl, expanded_url + query) if media_upload.resumable(): url = _add_query_parameter(url, 'uploadType', 'resumable') if media_upload.resumable(): # This is all we need to do for resumable, if the body exists it gets # sent in the first request, otherwise an empty body is sent. resumable = media_upload else: # A non-resumable upload if body is None: # This is a simple media upload headers['content-type'] = media_upload.mimetype() body = media_upload.getbytes(0, media_upload.size()) url = _add_query_parameter(url, 'uploadType', 'media') else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') # msgRoot should not write out it's own headers setattr(msgRoot, '_write_headers', lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers['content-type'].split('/')) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split('/')) msg['Content-Transfer-Encoding'] = 'binary' payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) # encode the body: note that we can't use `as_string`, because # it plays games with `From ` lines. fp = StringIO.StringIO() g = Generator(fp, mangle_from_=False) g.flatten(msgRoot, unixfrom=False) body = fp.getvalue() multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ( 'multipart/related; ' 'boundary="%s"') % multipart_boundary url = _add_query_parameter(url, 'uploadType', 'multipart') logger.info('URL being requested: %s %s' % (httpMethod, url)) return self._requestBuilder(self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable)
def method(self, **kwargs): for name in six.iterkeys(kwargs): if name not in argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) for name in required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in six.iteritems(pattern_params): if name in kwargs: if isinstance(kwargs[name], six.string_types): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex)) for name, enums in six.iteritems(enum_params): if name in kwargs: if kwargs[name] not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, kwargs[name], str(enums))) actual_query_params = {} actual_path_params = {} for key, value in six.iteritems(kwargs): to_type = param_type.get(key, 'string') # For repeated parameters we cast each member of the list. if key in repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in query_params: actual_query_params[argmap[key]] = cast_value if key in path_params: actual_path_params[argmap[key]] = cast_value body_value = kwargs.get('body', None) media_filename = kwargs.get('media_body', None) if self._developerKey: actual_query_params['key'] = self._developerKey model = self._model # If there is no schema for the response then presume a binary blob. if 'response' not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request(headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = six.moves.urllib.parse.urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = '' 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: raise UnknownFileType(media_filename) if not mimeparse.best_match([media_mime_type], ','.join(accept)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError('media_filename must be str or MediaUpload.') # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads if media_upload.resumable(): expanded_url = uritemplate.expand(mediaResumablePathUrl, params) else: expanded_url = uritemplate.expand(mediaPathUrl, params) url = six.moves.urllib.parse.urljoin(self._baseUrl, expanded_url + query) if media_upload.resumable(): # This is all we need to do for resumable, if the body exists it gets # sent in the first request, otherwise an empty body is sent. resumable = media_upload else: # A non-resumable upload if body is None: # This is a simple media upload headers['content-type'] = media_upload.mimetype() body = media_upload.getbytes(0, media_upload.size()) else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') # msgRoot should not write out it's own headers setattr(msgRoot, '_write_headers', lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers['content-type'].split('/')) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split('/')) msg['Content-Transfer-Encoding'] = 'binary' payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ('multipart/related; ' 'boundary="%s"') % multipart_boundary logging.info('URL being requested: %s' % url) return self._requestBuilder(self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable)
def method(self, **kwargs): for name in kwargs.iterkeys(): if name not in argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) for name in required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in pattern_params.iteritems(): if name in kwargs: if isinstance(kwargs[name], basestring): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex) ) for name, enums in enum_params.iteritems(): if name in kwargs: if kwargs[name] not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, kwargs[name], str(enums)) ) actual_query_params = {} actual_path_params = {} for key, value in kwargs.iteritems(): to_type = param_type.get(key, "string") # For repeated parameters we cast each member of the list. if key in repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in query_params: actual_query_params[argmap[key]] = cast_value if key in path_params: actual_path_params[argmap[key]] = cast_value body_value = kwargs.get("body", None) media_filename = kwargs.get("media_body", None) if self._developerKey: actual_query_params["key"] = self._developerKey model = self._model # If there is no schema for the response then presume a binary blob. if "response" not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request(headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = "" if media_filename: # Ensure we end up with a valid MediaUpload object. if isinstance(media_filename, basestring): (media_mime_type, encoding) = 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)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError("media_filename must be str or MediaUpload.") # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads if media_upload.resumable(): expanded_url = uritemplate.expand(mediaResumablePathUrl, params) else: expanded_url = uritemplate.expand(mediaPathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) if media_upload.resumable(): # This is all we need to do for resumable, if the body exists it gets # sent in the first request, otherwise an empty body is sent. resumable = media_upload else: # A non-resumable upload if body is None: # This is a simple media upload headers["content-type"] = media_upload.mimetype() body = media_upload.getbytes(0, media_upload.size()) else: # This is a multipart/related upload. msgRoot = MIMEMultipart("related") # msgRoot should not write out it's own headers setattr(msgRoot, "_write_headers", lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers["content-type"].split("/")) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split("/")) msg["Content-Transfer-Encoding"] = "binary" payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() multipart_boundary = msgRoot.get_boundary() headers["content-type"] = ("multipart/related; " 'boundary="%s"') % multipart_boundary logging.info("URL being requested: %s" % url) return self._requestBuilder( self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable, )
def test_resumable_media_good_upload_from_execute(self): """Not a multipart upload.""" self.http = HttpMock(datafile("zoo.json"), {"status": "200"}) zoo = build("zoo", "v1", http=self.http) media_upload = MediaFileUpload(datafile("small.png"), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) assertUrisEqual( self, "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json", request.uri ) http = HttpMockSequence( [ ({"status": "200", "location": "http://upload.example.com"}, ""), ({"status": "308", "location": "http://upload.example.com/2", "range": "0-12"}, ""), ( {"status": "308", "location": "http://upload.example.com/3", "range": "0-%d" % media_upload.size()}, "", ), ({"status": "200"}, '{"foo": "bar"}'), ] ) body = request.execute(http=http) self.assertEqual(body, {"foo": "bar"})
def method(self, **kwargs): # Don't bother with doc string, it will be over-written by createMethod. for name in kwargs.iterkeys(): if name not in parameters.argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) # Remove args that have a value of None. keys = kwargs.keys() for name in keys: if kwargs[name] is None: del kwargs[name] for name in parameters.required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in parameters.pattern_params.iteritems(): if name in kwargs: if isinstance(kwargs[name], basestring): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex)) for name, enums in parameters.enum_params.iteritems(): if name in kwargs: # We need to handle the case of a repeated enum # name differently, since we want to handle both # arg='value' and arg=['value1', 'value2'] if (name in parameters.repeated_params and not isinstance(kwargs[name], basestring)): values = kwargs[name] else: values = [kwargs[name]] for value in values: if value not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, value, str(enums))) actual_query_params = {} actual_path_params = {} for key, value in kwargs.iteritems(): to_type = parameters.param_types.get(key, 'string') # For repeated parameters we cast each member of the list. if key in parameters.repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in parameters.query_params: actual_query_params[parameters.argmap[key]] = cast_value if key in parameters.path_params: actual_path_params[parameters.argmap[key]] = cast_value body_value = kwargs.get('body', None) media_filename = kwargs.get('media_body', None) if self._developerKey: actual_query_params['key'] = self._developerKey model = self._model if methodName.endswith('_media'): model = MediaModel() elif 'response' not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request(headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = '' if media_filename: # Ensure we end up with a valid MediaUpload object. if isinstance(media_filename, basestring): (media_mime_type, encoding) = 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)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, mimetype=media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError('media_filename must be str or MediaUpload.') # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads expanded_url = uritemplate.expand(mediaPathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) if media_upload.resumable(): url = _add_query_parameter(url, 'uploadType', 'resumable') if media_upload.resumable(): # This is all we need to do for resumable, if the body exists it gets # sent in the first request, otherwise an empty body is sent. resumable = media_upload else: # A non-resumable upload if body is None: # This is a simple media upload headers['content-type'] = media_upload.mimetype() body = media_upload.getbytes(0, media_upload.size()) url = _add_query_parameter(url, 'uploadType', 'media') else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') # msgRoot should not write out it's own headers setattr(msgRoot, '_write_headers', lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers['content-type'].split('/')) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split('/')) msg['Content-Transfer-Encoding'] = 'binary' payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ('multipart/related; ' 'boundary="%s"') % multipart_boundary url = _add_query_parameter(url, 'uploadType', 'multipart') logger.info('URL being requested: %s' % url) return self._requestBuilder(self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable)
def method(self, **kwargs): for name in kwargs.iterkeys(): if name not in argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) for name in required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in pattern_params.iteritems(): if name in kwargs: if isinstance(kwargs[name], basestring): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex)) for name, enums in enum_params.iteritems(): if name in kwargs: if kwargs[name] not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, kwargs[name], str(enums))) actual_query_params = {} actual_path_params = {} for key, value in kwargs.iteritems(): to_type = param_type.get(key, 'string') # For repeated parameters we cast each member of the list. if key in repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in query_params: actual_query_params[argmap[key]] = cast_value if key in path_params: actual_path_params[argmap[key]] = cast_value body_value = kwargs.get('body', None) media_filename = kwargs.get('media_body', None) if self._developerKey: actual_query_params['key'] = self._developerKey model = self._model # If there is no schema for the response then presume a binary blob. if 'response' not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request(headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = '' if media_filename: # Convert a simple filename into a MediaUpload object. if isinstance(media_filename, basestring): (media_mime_type, encoding) = 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)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError('media_filename must be str or MediaUpload.') if media_upload.resumable(): resumable = media_upload # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads if media_upload.resumable(): expanded_url = uritemplate.expand(mediaResumablePathUrl, params) else: expanded_url = uritemplate.expand(mediaPathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) if body is None: # This is a simple media upload headers['content-type'] = media_upload.mimetype() expanded_url = uritemplate.expand(mediaResumablePathUrl, params) if not media_upload.resumable(): body = media_upload.getbytes(0, media_upload.size()) else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') # msgRoot should not write out it's own headers setattr(msgRoot, '_write_headers', lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers['content-type'].split('/')) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split('/')) msg['Content-Transfer-Encoding'] = 'binary' if media_upload.resumable(): # This is a multipart resumable upload, where a multipart payload # looks like this: # # --===============1678050750164843052== # Content-Type: application/json # MIME-Version: 1.0 # # {'foo': 'bar'} # --===============1678050750164843052== # Content-Type: image/png # MIME-Version: 1.0 # Content-Transfer-Encoding: binary # # <BINARY STUFF> # --===============1678050750164843052==-- # # In the case of resumable multipart media uploads, the <BINARY # STUFF> is large and will be spread across multiple PUTs. What we # do here is compose the multipart message with a random payload in # place of <BINARY STUFF> and then split the resulting content into # two pieces, text before <BINARY STUFF> and text after <BINARY # STUFF>. The text after <BINARY STUFF> is the multipart boundary. # In apiclient.http the HttpRequest will send the text before # <BINARY STUFF>, then send the actual binary media in chunks, and # then will send the multipart delimeter. payload = hex(random.getrandbits(300)) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() body, _ = body.split(payload) resumable = media_upload else: payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ('multipart/related; ' 'boundary="%s"') % multipart_boundary logging.info('URL being requested: %s' % url) return self._requestBuilder(self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable)
def method(self, **kwargs): for name in kwargs.iterkeys(): if name not in argmap: raise TypeError('Got an unexpected keyword argument "%s"' % name) for name in required_params: if name not in kwargs: raise TypeError('Missing required parameter "%s"' % name) for name, regex in pattern_params.iteritems(): if name in kwargs: if isinstance(kwargs[name], basestring): pvalues = [kwargs[name]] else: pvalues = kwargs[name] for pvalue in pvalues: if re.match(regex, pvalue) is None: raise TypeError( 'Parameter "%s" value "%s" does not match the pattern "%s"' % (name, pvalue, regex)) for name, enums in enum_params.iteritems(): if name in kwargs: if kwargs[name] not in enums: raise TypeError( 'Parameter "%s" value "%s" is not an allowed value in "%s"' % (name, kwargs[name], str(enums))) actual_query_params = {} actual_path_params = {} for key, value in kwargs.iteritems(): to_type = param_type.get(key, 'string') # For repeated parameters we cast each member of the list. if key in repeated_params and type(value) == type([]): cast_value = [_cast(x, to_type) for x in value] else: cast_value = _cast(value, to_type) if key in query_params: actual_query_params[argmap[key]] = cast_value if key in path_params: actual_path_params[argmap[key]] = cast_value body_value = kwargs.get('body', None) media_filename = kwargs.get('media_body', None) if self._developerKey: actual_query_params['key'] = self._developerKey model = self._model # If there is no schema for the response then presume a binary blob. if 'response' not in methodDesc: model = RawModel() headers = {} headers, params, query, body = model.request( headers, actual_path_params, actual_query_params, body_value) expanded_url = uritemplate.expand(pathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) resumable = None multipart_boundary = '' if media_filename: # Convert a simple filename into a MediaUpload object. if isinstance(media_filename, basestring): (media_mime_type, encoding) = 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)): raise UnacceptableMimeTypeError(media_mime_type) media_upload = MediaFileUpload(media_filename, media_mime_type) elif isinstance(media_filename, MediaUpload): media_upload = media_filename else: raise TypeError( 'media_filename must be str or MediaUpload.') if media_upload.resumable(): resumable = media_upload # Check the maxSize if maxSize > 0 and media_upload.size() > maxSize: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads if media_upload.resumable(): expanded_url = uritemplate.expand(mediaResumablePathUrl, params) else: expanded_url = uritemplate.expand(mediaPathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) if body is None: # This is a simple media upload headers['content-type'] = media_upload.mimetype() expanded_url = uritemplate.expand(mediaResumablePathUrl, params) if not media_upload.resumable(): body = media_upload.getbytes(0, media_upload.size()) else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') # msgRoot should not write out it's own headers setattr(msgRoot, '_write_headers', lambda self: None) # attach the body as one part msg = MIMENonMultipart(*headers['content-type'].split('/')) msg.set_payload(body) msgRoot.attach(msg) # attach the media as the second part msg = MIMENonMultipart(*media_upload.mimetype().split('/')) msg['Content-Transfer-Encoding'] = 'binary' if media_upload.resumable(): # This is a multipart resumable upload, where a multipart payload # looks like this: # # --===============1678050750164843052== # Content-Type: application/json # MIME-Version: 1.0 # # {'foo': 'bar'} # --===============1678050750164843052== # Content-Type: image/png # MIME-Version: 1.0 # Content-Transfer-Encoding: binary # # <BINARY STUFF> # --===============1678050750164843052==-- # # In the case of resumable multipart media uploads, the <BINARY # STUFF> is large and will be spread across multiple PUTs. What we # do here is compose the multipart message with a random payload in # place of <BINARY STUFF> and then split the resulting content into # two pieces, text before <BINARY STUFF> and text after <BINARY # STUFF>. The text after <BINARY STUFF> is the multipart boundary. # In apiclient.http the HttpRequest will send the text before # <BINARY STUFF>, then send the actual binary media in chunks, and # then will send the multipart delimeter. payload = hex(random.getrandbits(300)) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() body, _ = body.split(payload) resumable = media_upload else: payload = media_upload.getbytes(0, media_upload.size()) msg.set_payload(payload) msgRoot.attach(msg) body = msgRoot.as_string() multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ( 'multipart/related; ' 'boundary="%s"') % multipart_boundary logging.info('URL being requested: %s' % url) return self._requestBuilder(self._http, model.response, url, method=httpMethod, body=body, headers=headers, methodId=methodId, resumable=resumable)