Permalink
Browse files

Initial version with just DictCache and SqliteCache.

  • Loading branch information...
1 parent 6b2b642 commit 7d53b7027edd2d263816838b626af56bd038ae9a @singerb committed Oct 27, 2012
Showing with 1,182 additions and 4 deletions.
  1. +0 −4 README.md
  2. +160 −0 README.rst
  3. 0 __init__.py
  4. +96 −0 async_dropbox.py
  5. +347 −0 cache.py
  6. +455 −0 mixin.py
  7. +124 −0 sqlite_cache.py
View
@@ -1,4 +0,0 @@
-tornado_dropcache
-=================
-
-Classes to provide high level Dropbox access in the Tornado webserver.
@@ -0,0 +1,160 @@
+========
+mixin.py
+========
+
+Classes to provide high level Dropbox access, built on top of async_dropbox and Cache.
+
+Dependencies
+============
+
+Python (tested on 2.7.1), tornado, and async_dropbox (a copy is provided).
+
+Classes
+=======
+
+DropboxUserHandler
+ Mixin on top of async_dropbox.DropboxMixin to provide nicer user access.
+ Handler to provide nicer user access.
+
+DropboxLoginHandler
+ Handler to handle Dropbox login; redirects to / on success.
+
+DropboxAPIMixin
+ High level Dropbox API access for a single folder, built on top of
+ async_dropbox.DropboxMixin, Cache, and DropboxUserMixin.
+
+See the documentation for each class for more details and specifics on which
+application settings and cookies are used.
+
+Example Usage
+=============
+
+::
+ class MainHandler(DropboxUserHandler, DropboxAPIMixin):
+ @tornado.web.asynchronous
+ @tornado.gen.engine
+ def get(self):
+ if not self.current_user:
+ self.render("welcome.html")
+ else:
+ files = yield tornado.gen.Task(self.get_files)
+ self.render("list.html", title="all files", files=files)
+
+ class LoginHandler(DropboxLoginHandler):
+ def set_application_cookies(self):
+ self.set_secure_cookie("dropbox_folder_path", "<folder path>")
+
+ class ViewHandler(DropboxUserHandler, DropboxAPIMixin):
+ @tornado.web.authenticated
+ @tornado.web.asynchronous
+ @tornado.gen.engine
+ def get(self, file_name):
+ res = yield tornado.gen.Task(self.get_data, file_name)
+ filedata = res[0][1]
+
+ self.render("view.html", title=file_name, contents=filedata)
+
+ logging.basicConfig(level=logging.DEBUG)
+
+ cache = SqliteCache("<folder path>")
+
+ settings = {
+ "cookie_secret": "<COOKIE SECRET>",
+ "static_path": os.path.join(os.path.dirname(__file__), "static"),
+ "login_url": "/login",
+ "template_path": "templates/",
+ "dropbox_consumer_key": "<KEY HERE>",
+ "dropbox_consumer_secret": "<SECRET HERE>",
+ "xsrf_cookies": True,
+ "debug": True,
+ "dropbox_cache": cache,
+ "dropbox_api_type": "dropbox",
+ }
+
+ application = tornado.web.Application([
+ tornado.web.URLSpec(r"/", MainHandler, name="main"),
+ tornado.web.URLSpec(r"/view/([^/]+)", ViewHandler, name="view"),
+ tornado.web.URLSpec(r"/login", LoginHandler, name="login"),
+ ], **settings)
+
+ if __name__ == "__main__":
+ application.listen(8888)
+ tornado.ioloop.IOLoop.instance().start()
+
+Contributing
+============
+
+If you use and like this, please let me know! Patches, pull requests, suggestions etc. are all
+gratefully accepted.
+
+========
+cache.py
+========
+
+Cache system for apps managing files in a Dropbox folder; allows higher level clients to easily
+follow Dropbox best practices.
+
+Dependencies
+============
+
+Python (tested on 2.7.1).
+
+Cache implementations other than the basic ones here may have additional dependencies for their
+specific backend; see each implementation for details.
+
+Classes
+=======
+
+The base class (using the abstract base class facility from abc) and 2 simple implementations
+are contained here; more useful implementations are found seperately.
+
+Cache
+ Cache abstract base class.
+
+EmptyCache
+ Cache implementation that caches nothing; used if no cache is specified.
+
+DictCache
+ A Cache implementation that stores data in an in memory dictionary.
+
+Other Available Implementations
+===============================
+
+Included in this module currently are the following implementations; the Async* implementations
+are designed for use with the Tornado asynchronous I/O facilities.
+
+SqliteCache (sqlite_cache.py)
+ Cache using the sqlite bindings.
+
+AsyncCouchCache
+ *PENDING* Cache using CouchDB and the Corduroy bindings.
+
+AsyncMemcachedCache
+ *PENDING* Cache using memcached and TBD bindings.
+
+Contributing
+============
+
+If you use and like this, please let me know! Patches, pull requests, suggestions etc. are all
+gratefully accepted. Additional Cache implementations would also be most welcome!
+
+=======
+License
+=======
+
+The included copy of async_dropbox.py is not subject to this license, but retains the
+license, if any, applied by its creator.
+
+Copyright 2012 Benedict Singer
+
+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.
View
No changes.
View
@@ -0,0 +1,96 @@
+import tornado.auth
+import urllib
+from tornado.httpclient import AsyncHTTPClient
+import tornado.gen
+
+class DropboxMixin(tornado.auth.OAuthMixin):
+ """Dropbox OAuth authentication.
+
+ Uses the app settings dropbox_consumer_key and dropbox_consumer_secret.
+
+ Usage::
+
+ class DropboxLoginHandler(RequestHandler, DropboxMixin):
+ @asynchronous
+ def get(self):
+ if self.get_argument("oauth_token", None):
+ self.get_authenticated_user(self._on_auth)
+ return
+ self.authorize_redirect()
+
+ def _on_auth(self, user):
+ if not user:
+ raise tornado.web.HTTPError(500, "Dropbox auth failed")
+ # save the user using e.g. set_secure_cookie
+ """
+ _OAUTH_VERSION = "1.0"
+ # note www vs api.dropbox.com in authorize url
+ _OAUTH_REQUEST_TOKEN_URL = "https://api.dropbox.com/1/oauth/request_token"
+ _OAUTH_ACCESS_TOKEN_URL = "https://api.dropbox.com/1/oauth/access_token"
+ _OAUTH_AUTHORIZE_URL = "https://www.dropbox.com/1/oauth/authorize"
+
+ def dropbox_request(self, subdomain, path, callback, access_token,
+ post_args=None, put_body=None, **args):
+ """Fetches the given API operation.
+
+ The request is defined by a combination of subdomain (either
+ "api" or "api-content") and path (such as "/1/metadata/sandbox/").
+ See the Dropbox REST API docs for details:
+ https://www.dropbox.com/developers/reference/api
+
+ For GET requests, arguments should be passed as keyword arguments
+ to dropbox_request. For POSTs, arguments should be passed
+ as a dictionary in `post_args`. For PUT, data should be passed
+ as `put_body`
+
+ Example usage::
+
+ class MainHandler(tornado.web.RequestHandler,
+ async_dropbox.DropboxMixin):
+ @tornado.web.authenticated
+ @tornado.web.asynchronous
+ def get(self):
+ self.dropbox_request(
+ "api", "/1/metadata/sandbox/"
+ access_token=self.current_user["access_token"],
+ callback=self._on_metadata)
+
+ def _on_metadata(self, response):
+ response.rethrow()
+ metadata = json.loads(response.body)
+ self.render("main.html", metadata=metadata)
+ """
+ url = "https://%s.dropbox.com%s" % (subdomain, path)
+ if access_token:
+ all_args = {}
+ all_args.update(args)
+ all_args.update(post_args or {})
+ assert not (put_body and post_args)
+ if put_body is not None:
+ method = "PUT"
+ elif post_args is not None:
+ method = "POST"
+ else:
+ method = "GET"
+ oauth = self._oauth_request_parameters(
+ url, access_token, all_args, method=method)
+ args.update(oauth)
+ if args: url += "?" + urllib.urlencode(args)
+ http = AsyncHTTPClient()
+ if post_args is not None:
+ http.fetch(url, method=method, body=urllib.urlencode(post_args),
+ callback=callback)
+ else:
+ http.fetch(url, method=method, body=put_body, callback=callback)
+
+ def _oauth_consumer_token(self):
+ return dict(
+ key=self.settings["dropbox_consumer_key"],
+ secret=self.settings["dropbox_consumer_secret"],
+ )
+
+ def _oauth_get_user(self, access_token, callback):
+ callback(dict(
+ access_token=access_token,
+ uid=self.get_argument('uid'),
+ ))
Oops, something went wrong.

0 comments on commit 7d53b70

Please sign in to comment.