Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Unified Diff: third_party/cloud_storage/cloudstorage/rest_api.py

Issue 1031663002: Increase maximum file upload to 100MB, use cloudstorage python library (Closed) Base URL: https://github.com/dart-lang/pub-dartlang.git@master
Patch Set: Add deprecation comment to old cloud_storage.py:open() function Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/cloud_storage/cloudstorage/rest_api.py
diff --git a/third_party/cloud_storage/cloudstorage/rest_api.py b/third_party/cloud_storage/cloudstorage/rest_api.py
new file mode 100644
index 0000000000000000000000000000000000000000..437c09d78845c7f3efcf41f03483a35795a7588f
--- /dev/null
+++ b/third_party/cloud_storage/cloudstorage/rest_api.py
@@ -0,0 +1,258 @@
+# Copyright 2012 Google Inc. 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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License 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.
+
+"""Base and helper classes for Google RESTful APIs."""
+
+
+
+
+
+__all__ = ['add_sync_methods']
+
+import random
+import time
+
+from . import api_utils
+
+try:
+ from google.appengine.api import app_identity
+ from google.appengine.ext import ndb
+except ImportError:
+ from google.appengine.api import app_identity
+ from google.appengine.ext import ndb
+
+
+
+def _make_sync_method(name):
+ """Helper to synthesize a synchronous method from an async method name.
+
+ Used by the @add_sync_methods class decorator below.
+
+ Args:
+ name: The name of the synchronous method.
+
+ Returns:
+ A method (with first argument 'self') that retrieves and calls
+ self.<name>, passing its own arguments, expects it to return a
+ Future, and then waits for and returns that Future's result.
+ """
+
+ def sync_wrapper(self, *args, **kwds):
+ method = getattr(self, name)
+ future = method(*args, **kwds)
+ return future.get_result()
+
+ return sync_wrapper
+
+
+def add_sync_methods(cls):
+ """Class decorator to add synchronous methods corresponding to async methods.
+
+ This modifies the class in place, adding additional methods to it.
+ If a synchronous method of a given name already exists it is not
+ replaced.
+
+ Args:
+ cls: A class.
+
+ Returns:
+ The same class, modified in place.
+ """
+ for name in cls.__dict__.keys():
+ if name.endswith('_async'):
+ sync_name = name[:-6]
+ if not hasattr(cls, sync_name):
+ setattr(cls, sync_name, _make_sync_method(name))
+ return cls
+
+
+class _AE_TokenStorage_(ndb.Model):
+ """Entity to store app_identity tokens in memcache."""
+
+ token = ndb.StringProperty()
+ expires = ndb.FloatProperty()
+
+
+@ndb.tasklet
+def _make_token_async(scopes, service_account_id):
+ """Get a fresh authentication token.
+
+ Args:
+ scopes: A list of scopes.
+ service_account_id: Internal-use only.
+
+ Raises:
+ An ndb.Return with a tuple (token, expiration_time) where expiration_time is
+ seconds since the epoch.
+ """
+ rpc = app_identity.create_rpc()
+ app_identity.make_get_access_token_call(rpc, scopes, service_account_id)
+ token, expires_at = yield rpc
+ raise ndb.Return((token, expires_at))
+
+
+class _RestApi(object):
+ """Base class for REST-based API wrapper classes.
+
+ This class manages authentication tokens and request retries. All
+ APIs are available as synchronous and async methods; synchronous
+ methods are synthesized from async ones by the add_sync_methods()
+ function in this module.
+
+ WARNING: Do NOT directly use this api. It's an implementation detail
+ and is subject to change at any release.
+ """
+
+ def __init__(self, scopes, service_account_id=None, token_maker=None,
+ retry_params=None):
+ """Constructor.
+
+ Args:
+ scopes: A scope or a list of scopes.
+ service_account_id: Internal use only.
+ token_maker: An asynchronous function of the form
+ (scopes, service_account_id) -> (token, expires).
+ retry_params: An instance of api_utils.RetryParams. If None, the
+ default for current thread will be used.
+ """
+
+ if isinstance(scopes, basestring):
+ scopes = [scopes]
+ self.scopes = scopes
+ self.service_account_id = service_account_id
+ self.make_token_async = token_maker or _make_token_async
+ if not retry_params:
+ retry_params = api_utils._get_default_retry_params()
+ self.retry_params = retry_params
+ self.user_agent = {'User-Agent': retry_params._user_agent}
+ self.expiration_headroom = random.randint(60, 240)
+
+ def __getstate__(self):
+ """Store state as part of serialization/pickling."""
+ return {'scopes': self.scopes,
+ 'id': self.service_account_id,
+ 'a_maker': (None if self.make_token_async == _make_token_async
+ else self.make_token_async),
+ 'retry_params': self.retry_params,
+ 'expiration_headroom': self.expiration_headroom}
+
+ def __setstate__(self, state):
+ """Restore state as part of deserialization/unpickling."""
+ self.__init__(state['scopes'],
+ service_account_id=state['id'],
+ token_maker=state['a_maker'],
+ retry_params=state['retry_params'])
+ self.expiration_headroom = state['expiration_headroom']
+
+ @ndb.tasklet
+ def do_request_async(self, url, method='GET', headers=None, payload=None,
+ deadline=None, callback=None):
+ """Issue one HTTP request.
+
+ It performs async retries using tasklets.
+
+ Args:
+ url: the url to fetch.
+ method: the method in which to fetch.
+ headers: the http headers.
+ payload: the data to submit in the fetch.
+ deadline: the deadline in which to make the call.
+ callback: the call to make once completed.
+
+ Yields:
+ The async fetch of the url.
+ """
+ retry_wrapper = api_utils._RetryWrapper(
+ self.retry_params,
+ retriable_exceptions=api_utils._RETRIABLE_EXCEPTIONS,
+ should_retry=api_utils._should_retry)
+ resp = yield retry_wrapper.run(
+ self.urlfetch_async,
+ url=url,
+ method=method,
+ headers=headers,
+ payload=payload,
+ deadline=deadline,
+ callback=callback,
+ follow_redirects=False)
+ raise ndb.Return((resp.status_code, resp.headers, resp.content))
+
+ @ndb.tasklet
+ def get_token_async(self, refresh=False):
+ """Get an authentication token.
+
+ The token is cached in memcache, keyed by the scopes argument.
+ Uses a random token expiration headroom value generated in the constructor
+ to eliminate a burst of GET_ACCESS_TOKEN API requests.
+
+ Args:
+ refresh: If True, ignore a cached token; default False.
+
+ Yields:
+ An authentication token. This token is guaranteed to be non-expired.
+ """
+ key = '%s,%s' % (self.service_account_id, ','.join(self.scopes))
+ ts = yield _AE_TokenStorage_.get_by_id_async(
+ key, use_cache=True, use_memcache=True,
+ use_datastore=self.retry_params.save_access_token)
+ if refresh or ts is None or ts.expires < (
+ time.time() + self.expiration_headroom):
+ token, expires_at = yield self.make_token_async(
+ self.scopes, self.service_account_id)
+ timeout = int(expires_at - time.time())
+ ts = _AE_TokenStorage_(id=key, token=token, expires=expires_at)
+ if timeout > 0:
+ yield ts.put_async(memcache_timeout=timeout,
+ use_datastore=self.retry_params.save_access_token,
+ use_cache=True, use_memcache=True)
+ raise ndb.Return(ts.token)
+
+ @ndb.tasklet
+ def urlfetch_async(self, url, method='GET', headers=None,
+ payload=None, deadline=None, callback=None,
+ follow_redirects=False):
+ """Make an async urlfetch() call.
+
+ This is an async wrapper around urlfetch(). It adds an authentication
+ header.
+
+ Args:
+ url: the url to fetch.
+ method: the method in which to fetch.
+ headers: the http headers.
+ payload: the data to submit in the fetch.
+ deadline: the deadline in which to make the call.
+ callback: the call to make once completed.
+ follow_redirects: whether or not to follow redirects.
+
+ Yields:
+ This returns a Future despite not being decorated with @ndb.tasklet!
+ """
+ headers = {} if headers is None else dict(headers)
+ headers.update(self.user_agent)
+ self.token = yield self.get_token_async()
+ if self.token:
+ headers['authorization'] = 'OAuth ' + self.token
+
+ deadline = deadline or self.retry_params.urlfetch_timeout
+
+ ctx = ndb.get_context()
+ resp = yield ctx.urlfetch(
+ url, payload=payload, method=method,
+ headers=headers, follow_redirects=follow_redirects,
+ deadline=deadline, callback=callback)
+ raise ndb.Return(resp)
+
+
+_RestApi = add_sync_methods(_RestApi)
« no previous file with comments | « third_party/cloud_storage/cloudstorage/errors.py ('k') | third_party/cloud_storage/cloudstorage/storage_api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698