summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMarcel Hellkamp <marc@gsites.de>2011-05-10 00:57:55 +0200
committerMarcel Hellkamp <marc@gsites.de>2011-05-12 21:29:07 +0200
commit20f775ebabdcbbae31da68d800a3faf657ff410b (patch)
tree061327c86eed7ddd1c65da63f657f1e40fee7625 /plugins
parent0a6224b9e2530527cfd1f8ef70a99d17371ae67e (diff)
downloadbottle-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.rst87
-rw-r--r--plugins/sqlite/setup.py2
-rw-r--r--plugins/werkzeug/README.rst80
-rw-r--r--plugins/werkzeug/bottle_werkzeug.py38
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