diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index 46e01d5c..06bd737e 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -5,3 +5,8 @@ not stable yet, but if you have some feedback, let me know.

+

Useful Links

+ diff --git a/docs/_themes/flasky/static/flasky.css_t b/docs/_themes/flasky/static/flasky.css_t index 05caf2f7..bd829c0c 100644 --- a/docs/_themes/flasky/static/flasky.css_t +++ b/docs/_themes/flasky/static/flasky.css_t @@ -16,7 +16,7 @@ body { font-family: 'Georgia', serif; font-size: 100%; - background-color: #111; + background-color: #555; color: #555; margin: 0; padding: 0; @@ -47,15 +47,13 @@ div.body { } div.footer { - color: #555; - width: 100%; - padding: 13px 0; - text-align: center; - font-size: 75%; + color: #ccc; + padding: 10px; + font-size: 0.8em; } div.footer a { - color: #444; + color: white; text-decoration: underline; } @@ -138,7 +136,7 @@ div.sphinxsidebar input[type=text]{ /* -- body styles ----------------------------------------------------------- */ a { - color: #003B55; + color: #004B6B; text-decoration: none; } @@ -159,7 +157,7 @@ div.body h6 { color: #212224; margin: 30px 0px 10px 0px; padding: 8px 0 5px 10px; - text-shadow: 0px 1px 0 white + text-shadow: 0px 1px 0 white; } div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } @@ -170,14 +168,14 @@ div.body h5 { font-size: 100%; background-color: #eee; } div.body h6 { font-size: 100%; background-color: #eee; } a.headerlink { - color: #c60f0f; + color: white; padding: 0 4px; text-decoration: none; } a.headerlink:hover { - background-color: #c60f0f; - color: white; + color: #444; + background: #eaeaea; } div.body p, div.body dd, div.body li { @@ -218,22 +216,78 @@ p.admonition-title { p.admonition-title:after { content: ":"; } + +pre, tt { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +tt.descname, tt.descclassname { + font-size: 0.95em; + -webkit-box-shadow: none; + -moz-box-shadow: none; +} + +tt.descname { + padding-right: 0.08em; +} + +table.docutils { + border: 1px solid #888; + -webkit-box-shadow: 2px 2px 1px #d8d8d8; + -moz-box-shadow: 2px 2px 1px #d8d8d8; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list { + border: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} pre { + background: #FDFDFD; padding: 10px; color: #222; - line-height: 1.2em; - border: 1px solid #C6C9CB; - font-size: 1.1em; - margin: 1.5em 0 1.5em 0; - -webkit-box-shadow: 1px 1px 1px #d8d8d8; - -moz-box-shadow: 1px 1px 1px #d8d8d8; + line-height: 1.3em; + border: 1px solid #f9f9f9; + margin: 1.5em 3px 1.5em 0; + -webkit-box-shadow: 2px 2px 1px #d8d8d8; + -moz-box-shadow: 2px 2px 1px #d8d8d8; } tt { background-color: #ecf0f3; color: #222; /* padding: 1px 2px; */ - font-size: 1.1em; - font-family: monospace; + -webkit-box-shadow: 1px 1px 1px #d8d8d8; + -moz-box-shadow: 1px 1px 1px #d8d8d8; +} + +tt.xref, a tt { + background-color: #FBFBFB; +} + +a:hover tt { + background: #EEE; +} + +div.document + div.related { + background: #aaa; +} + +div.document + div.related a { + color: white; } diff --git a/docs/api.rst b/docs/api.rst index eefdf71c..98614ead 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -163,7 +163,13 @@ Useful Functions and Classes .. autofunction:: url_for -.. autofunction:: abort +.. function:: abort(code) + + Raises an :exc:`~werkzeug.exception.HTTPException` for the given + status code. For example to abort request handling with a page not + found exception, you would call ``abort(404)``. + + :param code: the HTTP error code. .. autofunction:: redirect diff --git a/docs/deploying.rst b/docs/deploying.rst new file mode 100644 index 00000000..345d7ee9 --- /dev/null +++ b/docs/deploying.rst @@ -0,0 +1,266 @@ +Deployment Options +================== + +Depending on what you have available there are multiple ways to run Flask +applications. A very common method is to use the builtin server during +development and maybe behind a proxy for simple applications, but there +are more options available. + +If you have a different WSGI server look up the server documentation about +how to use a WSGI app with it. Just remember that your application object +is the actual WSGI application. + + +FastCGI +------- + +A very popular deployment setup on servers like `lighttpd`_ and `nginx`_ +is FastCGI. To use your WSGI application with any of them you will need +a FastCGI server first. + +The most popular one is `flup`_ which we will use for this guide. Make +sure to have it installed. + +Creating a `.fcgi` file +``````````````````````` + +First you need to create the FastCGI server file. Let's call it +`yourapplication.fcgi`:: + + #!/usr/bin/python + from flup.server.fcgi import WSGIServer + from yourapplication import app + + WSGIServer(app).run() + +This is enough for Apache to work, however lighttpd and nginx need a +socket to communicate with the FastCGI server. For that to work you +need to pass the path to the socket to the +:class:`~flup.server.fcgi.WSGIServer`:: + + WSGIServer(application, bindAddress='/path/to/fcgi.sock').run() + +The path has to be the exact same path you define in the server +config. + +Save the `yourapplication.fcgi` file somewhere you will find it again. +It makes sense to have that in `/var/www/yourapplication` or something +similar. + +Make sure to set the executable bit on that file so that the servers +can execute it:: + + # chmod +x /var/www/yourapplication/yourapplication.fcgi + +Configuring lighttpd +```````````````````` + +A basic FastCGI configuration for lighttpd looks like that:: + + fastcgi.server = ("/yourapplication" => + "yourapplication" => ( + "socket" => "/tmp/yourapplication-fcgi.sock", + "bin-path" => "/var/www/yourapplication/yourapplication.fcgi", + "check-local" => "disable" + ) + ) + +This configuration binds the application to `/yourapplication`. If you +want the application to work in the URL root you have to work around a +lighttpd bug with the `~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware. + +Make sure to apply it only if you are mounting the application the URL +root. + +Configuring nginx +````````````````` + +Installing FastCGI applications on nginx is a bit tricky because by default +some FastCGI parameters are not properly forwarded. + +A basic FastCGI configuration for nginx looks like this:: + + location /yourapplication/ { + include fastcgi_params; + if ($uri ~ ^/yourapplication/(.*)?) { + set $path_url $1; + } + fastcgi_param PATH_INFO $path_url; + fastcgi_param SCRIPT_NAME /yourapplication; + fastcgi_pass unix:/tmp/yourapplication-fcgi.sock; + } + +This configuration binds the application to `/yourapplication`. If you want +to have it in the URL root it's a bit easier because you don't have to figure +out how to calculate `PATH_INFO` and `SCRIPT_NAME`:: + + location /yourapplication/ { + include fastcgi_params; + fastcgi_param PATH_INFO $fastcgi_script_name; + fastcgi_param SCRIPT_NAME ""; + fastcgi_pass unix:/tmp/yourapplication-fcgi.sock; + } + +Since Nginx doesn't load FastCGI apps, you have to do it by yourself. You +can either write an `init.d` script for that or execute it inside a screen +session:: + + $ screen + $ /var/www/yourapplication/yourapplication.fcgi + +Debugging +````````` + +FastCGI deployments tend to be hard to debug on most webservers. Very often the +only thing the server log tells you is something along the lines of "premature +end of headers". In order to debug the application the only thing that can +really give you ideas why it breaks is switching to the correct user and +executing the application by hand. + +This example assumes your application is called `application.fcgi` and that your +webserver user is `www-data`:: + + $ su www-data + $ cd /var/www/yourapplication + $ python application.fcgi + Traceback (most recent call last): + File "yourapplication.fcg", line 4, in + ImportError: No module named yourapplication + +In this case the error seems to be "yourapplication" not being on the python +path. Common problems are: + +- relative paths being used. Don't rely on the current working directory +- the code depending on environment variables that are not set by the + web server. +- different python interpreters being used. + +.. _lighttpd: http://www.lighttpd.net/ +.. _nginx: http://nginx.net/ +.. _flup: http://trac.saddi.com/flup + + +mod_wsgi (Apache) +----------------- + +If you are using the `Apache`_ webserver you should consider using `mod_wsgi`_. + +.. _Apache: http://httpd.apache.org/ + +Installing `mod_wsgi` +````````````````````` + +If you don't have `mod_wsgi` installed yet you have to either install it using +a package manager or compile it yourself. + +The mod_wsgi `installation instructions`_ cover installation instructions for +source installations on UNIX systems. + +If you are using ubuntu / debian you can apt-get it and activate it as follows:: + + # apt-get install libapache2-mod-wsgi + +On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using +pkg_add:: + + # pkg_add -r mod_wsgi + +If you are using pkgsrc you can install `mod_wsgi` by compiling the +`www/ap2-wsgi` package. + +If you encounter segfaulting child processes after the first apache reload you +can safely ignore them. Just restart the server. + +Creating a `.wsgi` file +``````````````````````` + +To run your application you need a `yourapplication.wsgi` file. This file +contains the code `mod_wsgi` is executing on startup to get the application +object. The object called `application` in that file is then used as +application. + +For most applications the following file should be sufficient:: + + from yourapplication import app as application + +If you don't have a factory function for application creation but a singleton +instance you can directly import that one as `application`. + +Store that file somewhere where you will find it again (eg: +`/var/www/yourapplication`) and make sure that `yourapplication` and all +the libraries that are in use are on the python load path. If you don't +want to install it system wide consider using a `virtual python`_ instance. + +Configuring Apache +`````````````````` + +The last thing you have to do is to create an Apache configuration file for +your application. In this example we are telling `mod_wsgi` to execute the +application under a different user for security reasons: + +.. sourcecode:: apache + + + ServerName example.com + + WSGIDaemonProcess yourapplication user=user1 group=group1 processes=1 threads=5 + WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi + + + WSGIProcessGroup yourapplication + WSGIApplicationGroup %{GLOBAL} + Order deny,allow + Allow from all + + + +For more information consult the `mod_wsgi wiki`_. + +.. _mod_wsgi: http://code.google.com/p/modwsgi/ +.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide +.. _virtual python: http://pypi.python.org/pypi/virtualenv +.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ + + +CGI +--- + +If all other deployment methods do not work, CGI will work for sure. CGI +is supported by all major browsers but usually has a less-than-optimal +performance. + +This is also the way you can use a Flask application on Google's +`AppEngine`_, there however the execution does happen in a CGI-like +environment. The application's performance is unaffected because of that. + +.. _AppEngine: http://code.google.com/appengine/ + +Creating a `.cgi` file +`````````````````````` + +First you need to create the CGI application file. Let's call it +`yourapplication.cgi`:: + + #!/usr/bin/python + from wsgiref.handlers import CGIHandler + from yourapplication import app + + CGIHandler().run(app) + +If you're running Python 2.4 you will need the :mod:`wsgiref` package. Python +2.5 and higher ship this as part of the standard library. + +Server Setup +```````````` + +Usually there are two ways to configure the server. Either just copy the +`.cgi` into a `cgi-bin` (and use `mod_rerwite` or something similar to +rewrite the URL) or let the server point to the file directly. + +In Apache for example you can put a like like this into the config: + +.. sourcecode:: apache + + ScriptName /app /path/to/the/application.cgi + +For more information consult the documentation of your webserver. diff --git a/docs/flaskext.py b/docs/flaskext.py index 85331eff..33f47449 100644 --- a/docs/flaskext.py +++ b/docs/flaskext.py @@ -16,10 +16,7 @@ class FlaskyStyle(Style): Other: "#000000", # class 'x' Comment: "italic #8f5902", # class: 'c' - Comment.Multiline: "italic #8f5902", # class: 'cm' - Comment.Preproc: "italic #8f5902", # class: 'cp' - Comment.Single: "italic #8f5902", # class: 'c1' - Comment.Special: "italic #8f5902", # class: 'cs' + Comment.Preproc: "noitalic", # class: 'cp' Keyword: "bold #004461", # class: 'k' Keyword.Constant: "bold #004461", # class: 'kc' @@ -43,7 +40,7 @@ class FlaskyStyle(Style): Name.Builtin.Pseudo: "#3465a4", # class: 'bp' Name.Class: "#000000", # class: 'nc' - to be revised Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#999", # class: 'nd' - to be revised + Name.Decorator: "#888", # class: 'nd' - to be revised Name.Entity: "#ce5c00", # class: 'ni' Name.Exception: "bold #cc0000", # class: 'ne' Name.Function: "#000000", # class: 'nf' @@ -57,14 +54,7 @@ class FlaskyStyle(Style): Name.Variable.Global: "#000000", # class: 'vg' - to be revised Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - # since the tango light blue does not show up well in text, we choose - # a pure blue instead. - Number: "bold #0000cf", # class: 'm' - Number.Float: "bold #0000cf", # class: 'mf' - Number.Hex: "bold #0000cf", # class: 'mh' - Number.Integer: "bold #0000cf", # class: 'mi' - Number.Integer.Long: "bold #0000cf", # class: 'il' - Number.Oct: "bold #0000cf", # class: 'mo' + Number: "#990000", # class: 'm' Literal: "#000000", # class: 'l' Literal.Date: "#000000", # class: 'ld' @@ -88,8 +78,8 @@ class FlaskyStyle(Style): Generic.Error: "#ef2929", # class: 'gr' Generic.Heading: "bold #000080", # class: 'gh' Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "italic #000000", # class: 'go' - Generic.Prompt: "#8f5902", # class: 'gp' + Generic.Output: "#888", # class: 'go' + Generic.Prompt: "#745334", # class: 'gp' Generic.Strong: "bold #000000", # class: 'gs' Generic.Subheading: "bold #800080", # class: 'gu' Generic.Traceback: "bold #a40000", # class: 'gt' diff --git a/docs/foreword.rst b/docs/foreword.rst index 79243d3b..580cf37d 100644 --- a/docs/foreword.rst +++ b/docs/foreword.rst @@ -36,6 +36,15 @@ something the framework just cannot do for you without modification. If you are ever in that situation, check out the :ref:`becomingbig` chapter. +A Framework and An Example +-------------------------- + +Flask is not only a microframework, it is also an example. Based on +Flask, there will be a series of blog posts that explain how to create a +framework. Flask itself is just one way to implement a framework on top +of existing libraries. Unlike many other microframeworks Flask does not +try to implement anything on its own, it reuses existing code. + Target Audience --------------- diff --git a/docs/index.rst b/docs/index.rst index 290656ea..81528203 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,4 +20,5 @@ you want to dive into all the internal parts of Flask, check out the quickstart patterns api + deploying becomingbig diff --git a/docs/installation.rst b/docs/installation.rst index b8375cd7..89e5a680 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -41,18 +41,19 @@ So let's see how that works! If you are on OS X or Linux chances are that one of the following two commands will for for you:: - sudo easy_install virtualenv + $ sudo easy_install virtualenv or even better:: - sudo pip install virtualenv + $ sudo pip install virtualenv Changes are you have virtualenv installed on your system then. Maybe it's even in your package manager (on ubuntu try ``sudo apt-get install python-virtualenv``). -On windows, just installed virtualenv from the `Python Package Index -`_. +If you are on Windows and missing the `easy_install` command you have to +install it first. Check the :ref:`windows-easy-install` section for more +information about how to do that. So now that you have virtualenv running just fire up a shell and create your own environment. I usually create a folder and a `env` folder @@ -101,3 +102,37 @@ The Drop into Place Version Now I really don't recommend this way on using Flask, but you can do that of course as well. Download the `dip` zipfile from the website and unzip it next to your application. + +.. _windows-easy-install: + +`easy_install` on Windows +------------------------- + +On Windows installation of `easy_install` is a little bit tricker because +on Windows slightly different rules apply, but it's not a biggy. The +easiest way to accomplish that is downloading the `ez_setup.py`_ file and +running it. (Double clicking should do the trick) + +Once you have done that it's important to add the `easy_install` command +and other Python scripts to the path. To do that you have to add the +Python installation's Script folder to the `PATH` variable. + +To do that, click right on your "Computer" desktop icon and click +"Properties". On Windows Vista and Windows 7 then click on "Advanced System +settings", on Windows XP click on the "Advanced" tab instead. Then click +on the "Environment variables" button and double click on the "Path" +variable in the "System variables" section. + +There append the path of your Python interpreter's Script folder to the +end of the last (make sure you delimit it from existing values with a +semicolon). Assuming you are using Python 2.6 on the default path, add +the following value:: + + ;C:\Python26\Scripts + +Then you are done. To check if it worked, open the cmd and execute +"easy_install". If you have UAC enabled it should prompt you for admin +privileges. + + +.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 45a3f0f6..429d7ab3 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -23,9 +23,14 @@ A minimal Flask application looks something like that:: if __name__ == '__main__': app.run() -If you now start that application with your Python interpreter and head -over to `http://localhost:5000/ `_, you should see -your hello world application. +Just save it as `hello.py` or something similar and run it with your +Python interpreter:: + + $ python hello.py + * Running on http://localhost:5000/ + +Head over to `http://localhost:5000/ `_, you should +see your hello world greeting. So what did that code do? diff --git a/flask.py b/flask.py index 344cae3b..1ea239f8 100644 --- a/flask.py +++ b/flask.py @@ -79,6 +79,8 @@ def flash(message): """Flashes a message to the next request. In order to remove the flashed message from the session and to display it to the user, the template has to call :func:`get_flashed_messages`. + + :param message: the message to be flashed. """ session['_flashes'] = (session.get('_flashes', [])) + [message] @@ -98,6 +100,10 @@ def get_flashed_messages(): def render_template(template_name, **context): """Renders a template from the template folder with the given context. + + :param template_name: the name of the template to be rendered + :param context: the variables that should be available in the + context of the template. """ current_app.update_template_context(context) return current_app.jinja_env.get_template(template_name).render(context) @@ -106,6 +112,11 @@ def render_template(template_name, **context): def render_template_string(source, **context): """Renders a template from the given template source string with the given context. + + :param template_name: the sourcecode of the template to be + rendered + :param context: the variables that should be available in the + context of the template. """ current_app.update_template_context(context) return current_app.jinja_env.from_string(source).render(context) @@ -222,6 +233,9 @@ class Flask(object): def update_template_context(self, context): """Update the template context with some commonly used variables. This injects request, session and g into the template context. + + :param context: the context as a dictionary that is updated in place + to add extra variables. """ reqctx = _request_ctx_stack.top context['request'] = reqctx.request @@ -232,6 +246,13 @@ class Flask(object): """Runs the application on a local development server. If the :attr:`debug` flag is set the server will automatically reload for code changes and show a debugger in case an exception happened. + + :param host: the hostname to listen on. set this to ``'0.0.0.0'`` + to have the server available externally as well. + :param port: the port of the webserver + :param options: the options to be forwarded to the underlying + Werkzeug server. See :func:`werkzeug.run_simple` + for more information. """ from werkzeug import run_simple if 'debug' in options: @@ -268,6 +289,9 @@ class Flask(object): with app.open_resource('schema.sql') as f: contents = f.read() do_something_with(contents) + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. """ return pkg_resources.resource_stream(self.package_name, resource) @@ -275,6 +299,8 @@ class Flask(object): """Creates or opens a new session. Default implementation stores all session data in a signed cookie. This requires that the :attr:`secret_key` is set. + + :param request: an instance of :attr:`request_class`. """ key = self.secret_key if key is not None: @@ -284,6 +310,11 @@ class Flask(object): def save_session(self, session, response): """Saves the session if it needs updates. For the default implementation, check :meth:`open_session`. + + :param session: the session to be saved (a + :class:`~werkzeug.contrib.securecookie.SecureCookie` + object) + :param request: an instance of :attr:`response_class` """ if session is not None: session.save_cookie(response, self.session_cookie_name) @@ -304,6 +335,13 @@ class Flask(object): pass app.add_url_rule('index', '/') app.view_functions['index'] = index + + :param rule: the URL rule as string + :param endpoint: the endpoint for the registered URL rule. Flask + itself assumes the name of the view function as + endpoint + :param options: the options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object """ options['endpoint'] = endpoint options.setdefault('methods', ('GET',)) @@ -363,6 +401,7 @@ class Flask(object): The :meth:`route` decorator accepts a couple of other arguments as well: + :param rule: the URL rule as string :param methods: a list of methods this rule should be limited to (``GET``, ``POST`` etc.). By default a rule just listens for ``GET`` (and implicitly ``HEAD``). @@ -370,6 +409,8 @@ class Flask(object): subdomain matching is in use. :param strict_slashes: can be used to disable the strict slashes setting for this rule. See above. + :param options: other options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object. """ def decorator(f): self.add_url_rule(rule, f.__name__, **options) @@ -392,6 +433,8 @@ class Flask(object): def page_not_found(): return 'This page does not exist', 404 app.error_handlers[404] = page_not_found + + :param code: the code as integer for the handler """ def decorator(f): self.error_handlers[code] = f @@ -440,6 +483,22 @@ class Flask(object): def make_response(self, rv): """Converts the return value from a view function to a real response object that is an instance of :attr:`response_class`. + + The following types are allowd for `rv`: + + ======================= =========================================== + :attr:`response_class` the object is returned unchanged + :class:`str` a response object is created with the + string as body + :class:`unicode` a response object is created with the + string encoded to utf-8 as body + :class:`tuple` the response object is created with the + contents of the tuple as arguments + a WSGI function the function is called as WSGI application + and buffered as response object + ======================= =========================================== + + :param rv: the return value from the view function """ if isinstance(rv, self.response_class): return rv @@ -464,6 +523,10 @@ class Flask(object): def process_response(self, response): """Can be overridden in order to modify the response object before it's sent to the WSGI server. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. """ session = _request_ctx_stack.top.session if session is not None: @@ -477,6 +540,11 @@ class Flask(object): `__call__` so that middlewares can be applied: app.wsgi_app = MyMiddleware(app.wsgi_app) + + :param environ: a WSGI environment + :param start_response: a callable accepting a status code, + a list of headers and an optional + exception context to start the response """ with self.request_context(environ): rv = self.preprocess_request() @@ -497,6 +565,8 @@ class Flask(object): with app.request_context(environ): do_something_with(request) + + :params environ: a WSGI environment """ _request_ctx_stack.push(_RequestContext(self, environ)) try: @@ -506,7 +576,8 @@ class Flask(object): def test_request_context(self, *args, **kwargs): """Creates a WSGI environment from the given values (see - :func:`werkzeug.create_environ` for more information). + :func:`werkzeug.create_environ` for more information, this + function accepts the same arguments). """ return self.request_context(create_environ(*args, **kwargs))