diff options
author | Marcel Hellkamp <marc@gsites.de> | 2011-05-10 00:57:55 +0200 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2011-05-12 21:29:07 +0200 |
commit | 20f775ebabdcbbae31da68d800a3faf657ff410b (patch) | |
tree | 061327c86eed7ddd1c65da63f657f1e40fee7625 /plugins | |
parent | 0a6224b9e2530527cfd1f8ef70a99d17371ae67e (diff) | |
download | bottle-20f775ebabdcbbae31da68d800a3faf657ff410b.tar.gz |
Added interactive debugger to werkzeug plugin.
docs: Added documentation for werkzeug and sqlite plugin.
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/sqlite/README.rst | 87 | ||||
-rw-r--r-- | plugins/sqlite/setup.py | 2 | ||||
-rw-r--r-- | plugins/werkzeug/README.rst | 80 | ||||
-rw-r--r-- | plugins/werkzeug/bottle_werkzeug.py | 38 |
4 files changed, 197 insertions, 10 deletions
diff --git a/plugins/sqlite/README.rst b/plugins/sqlite/README.rst new file mode 100644 index 0000000..31f6756 --- /dev/null +++ b/plugins/sqlite/README.rst @@ -0,0 +1,87 @@ +===================== +Bottle-SQLite +===================== + +SQLite is a self-contained SQL database engine that runs locally and does not +require any additional server software or setup. The sqlite3 module is part of the +Python standard library and already installed on most systems. It it very useful +for prototyping database-driven applications that are later ported to larger +databases such as PostgreSQL or MySQL. + +This plugin simplifies the use of sqlite databases in your Bottle applications. +Once installed, all you have to do is to add a ``db`` keyword argument +(configurable) to route callbacks that need a database connection. + +Installation +=============== + +Install with one of the following commands:: + + $ pip install bottle-sqlite + $ easy_install bottle-sqlite + +or download the latest version from github:: + + $ git clone git://github.com/defnull/bottle.git + $ cd bottle/plugins/sqlite + $ python setup.py install + +Usage +=============== + +Once installed to an application, the plugin passes an open +:class:`sqlite3.Connection` instance to all routes that require a ``db`` keyword +argument:: + + import bottle + + app = bottle.Bottle() + plugin = bottle.ext.sqlite.Plugin(dbfile='/tmp/test.db') + app.install(plugin) + + @app.route('/show/:item') + def show(item, db): + row = db.execute('SELECT * from items where name=?', item).fetchone() + if row: + return template('showitem', page=row) + return HTTPError(404, "Page not found") + +Routes that do not expect a ``db`` keyword argument are not affected. + +The connection handle is configured so that :class:`sqlite3.Row` objects can be +accessed both by index (like tuples) and case-insensitively by name. At the end of +the request cycle, outstanding transactions are committed and the connection is +closed automatically. If an error occurs, any changes to the database since the +last commit are rolled back to keep the database in a consistent state. + +Configuration +============= + +The following configuration options exist for the plugin class: + +* **dbfile**: Database filename (default: in-memory database). +* **keyword**: The keyword argument name that triggers the plugin (default: 'db'). +* **autocommit**: Whether or not to commit outstanding transactions at the end of the request cycle (default: True). +* **dictrows**: Whether or not to support dict-like access to row objects (default: True). + +You can override each of these values on a per-route basis:: + + @app.route('/cache/:item', sqlite={'dbfile': ':memory:'}) + def cache(item, db): + ... + +or install two plugins with different ``keyword`` settings to the same application:: + + app = bottle.Bottle() + test_db = bottle.ext.sqlite.Plugin(dbfile='/tmp/test.db') + cache_db = bottle.ext.sqlite.Plugin(dbfile=':memory:', keyword='cache') + app.install(test_db) + app.install(cache_db) + + @app.route('/show/:item') + def show(item, db): + ... + + @app.route('/cache/:item') + def cache(item, cache): + ... diff --git a/plugins/sqlite/setup.py b/plugins/sqlite/setup.py index a889c88..6b4af7b 100644 --- a/plugins/sqlite/setup.py +++ b/plugins/sqlite/setup.py @@ -19,7 +19,7 @@ exec(source) setup( name = 'bottle-sqlite', version = __version__, - url = 'http://bottlepy.org/docs/dev/plugin/sqlite/', + url = 'http://bottlepy.org/docs/dev/plugins/sqlite.html', description = 'SQLite3 integration for Bottle.', long_description = __doc__, author = 'Marcel Hellkamp', diff --git a/plugins/werkzeug/README.rst b/plugins/werkzeug/README.rst new file mode 100644 index 0000000..51eb090 --- /dev/null +++ b/plugins/werkzeug/README.rst @@ -0,0 +1,80 @@ +===================== +Bottle-Werkzeug +===================== + +`Werkzeug <http://werkzeug.pocoo.org/>`_ is a powerfull WSGI utility library for +Python. It includes an interactive debugger and feature-packed request and response +objects. + +This plugin integrates :class:`werkzeug.wrappers.Request` and +:class:`werkzeug.wrappers.Response` as an alternative to the built-in +implementations, adds support for :mod:`werkzeug.exceptions` and replaces the +default error page with an interactive debugger. + + + +Installation +=============== + +Install with one of the following commands:: + + $ pip install bottle-werkzeug + $ easy_install bottle-werkzeug + +or download the latest version from github:: + + $ git clone git://github.com/defnull/bottle.git + $ cd bottle/plugins/werkzeug + $ python setup.py install + + + +Usage +=============== + +Once installed to an application, this plugin adds support for +:class:`werkzeug.wrappers.Response`, all kinds of :mod:`werkzeug.exceptions` and +provides a thread-local instance of :class:`werkzeug.wrappers.Request` that is +updated with each request. The plugin instance itself doubles as a werkzeug +module object, so you don't have to import werkzeug in your application. Here +is an example:: + + import bottle + + app = bottle.Bottle() + werkzeug = bottle.ext.werkzeug.Plugin() + app.install(werkzeug) + + req = werkzueg.request # For the lazy. + + @app.route('/hello/:name') + def say_hello(name): + greet = {'en':'Hello', 'de':'Hallo', 'fr':'Bonjour'} + language = req.accept_languages.best_match(greet.keys()) + if language: + return werkzeug.Response('%s %s!' % (greet[language], name)) + else: + raise werkzeug.exceptions.NotAcceptable() + + + +Using the Debugger +==================== + +This plugin replaces the default error page with an advanced debugger. If you +have the `evalex` feature enabled, you will get an interactive console that +allows you to inspect the error context in the browser. Please read +`Debugging Applications with werkzeug <werkzeug:debug>`_ before you enable this +feature. + + + +Configuration +============= + +The following configuration options exist for the plugin class: + +* **evalex**: Enable the exception evaluation feature (interactive debugging). This requires a non-forking server and is a security risk. Please read `Debugging Applications with werkzeug <werkzeug:debug>`_. (default: False) +* **request_class**: Defaults to :class:`werkzeug.wrappers.Request` +* **debugger_class**: Defaults to a subclass of :class:`werkzeug.debug.DebuggedApplication` which obeys the :data:`bottle.DEBUG` setting. + diff --git a/plugins/werkzeug/bottle_werkzeug.py b/plugins/werkzeug/bottle_werkzeug.py index ce97962..f8a3f8d 100644 --- a/plugins/werkzeug/bottle_werkzeug.py +++ b/plugins/werkzeug/bottle_werkzeug.py @@ -1,6 +1,6 @@ """ This plugin adds support for :class:`werkzeug.Response`, all kinds of -:exc:`werkzeug.HTTPException` and provides a thread-local instance of +:exc:`werkzeug.exceptions` and provides a thread-local instance of :class:`werkzeug.Request`. It basically turns Bottle into Flask. The plugin instance doubles as a werkzeug module object, so you don't need to @@ -11,16 +11,17 @@ For werkzeug library documentation, see: http://werkzeug.pocoo.org/ Example:: import bottle - from bottle.ext.werkzeug import WerkzeugPlugin app = bottle.Bottle() - werkzeug = app.install(WerkzeugPlugin()) - wrequest = werkzueg.request # For the lazy. + werkzeug = bottle.ext.werkzeug.Plugin() + app.install(werkzeug) + + req = werkzueg.request # For the lazy. @app.route('/hello/:name') def say_hello(name): greet = {'en':'Hello', 'de':'Hallo', 'fr':'Bonjour'} - language = wrequest.accept_languages.best_match(greet.keys()) + language = req.accept_languages.best_match(greet.keys()) if language: return werkzeug.Response('%s %s!' % (greet[language], name)) else: @@ -39,6 +40,17 @@ import werkzeug from werkzeug import * import bottle + +class WerkzeugDebugger(DebuggedApplication): + """ A subclass of :class:`werkzeug.debug.DebuggedApplication` that obeys the + :data:`bottle.DEBUG` setting. """ + + def __call__(self, environ, start_response): + if bottle.DEBUG: + return DebuggedApplication.__call__(self, environ, start_response) + return self.app(environ, start_response) + + class WerkzeugPlugin(object): """ This plugin adds support for :class:`werkzeug.Response`, all kinds of :module:`werkzeug.exceptions` and provides a thread-local instance of @@ -46,18 +58,23 @@ class WerkzeugPlugin(object): name = 'werkzeug' - def __init__(self, request_class=werkzeug.Request, **config): - self.request_factory = request_class - self.config = config + def __init__(self, evalex=False, request_class=werkzeug.Request, + debugger_class=WerkzeugDebugger): + self.request_class = request_class + self.debugger_class = debugger_class + self.evalex=evalex self.app = None def setup(self, app): self.app = app + if self.debugger_class: + app.wsgi = self.debugger_class(app.wsgi, evalex=self.evalex) + app.catchall = False def apply(self, callback, context): def wrapper(*a, **ka): environ = bottle.request.environ - bottle.local.werkzueg_request = self.request_factory(environ, **self.config) + bottle.local.werkzueg_request = self.request_class(environ) try: rv = callback(*a, **ka) except werkzeug.exceptions.HTTPException, e: @@ -76,3 +93,6 @@ class WerkzeugPlugin(object): def __getattr__(self, name): ''' Convenient access to werkzeug module contents. ''' return getattr(werkzeug, name) + + +Plugin = WerkzeugPlugin
\ No newline at end of file |