# HG changeset patch # User Steve Losh # Date 1273028361 14400 # Node ID ca20cf1cb90b723bbf76e69c12bba26bd39c2cd4 # Parent 50444dfe726588de206eeae25040761b6ae5f78f garter: Update documentation. diff -r 50444dfe7265 -r ca20cf1cb90b garter/_list.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/_list.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,114 @@ + + + + + + + + + + + + + Garter » + ls / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /

+ + +

Directories

+ + + + + + + +
+ + media/ + +
+ + + + + + + + + + +
+
+ + + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/csrf.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/csrf.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + Garter » + Csrf + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

CSRF Protection

+ +

The internet is a dangerous place. One common type of attack your site's users can fall victim to is Cross-site Request Forgery attacks.

+

Garter provides a simple way to guard against these attacks, based on this snippet from the Flask snippet site.

+

To activate CSRF protection for your Flask application you need to do two things. First, call Garter's csrf function with your Flask app as a parameter:

+
from garter.csrf import csrf
+csrf(app)
+
+ + +

Once you do that you'll need to add a CSRF token to every form on your site that makes an HTTP POST request:

+
<input type="hidden" value="{{ csrf_token() }}">
+
+ + +

If you have certain views that need to be excluded from this protection (perhaps they receive POST requests from a third-party site) you can use the csrf_exempt decorator to disable protection:

+
from garter.csrf import csrf, csrf_exempt
+
+@csrf_exempt
+@route('/foo/')
+def my_receiving_view():
+    # ...
+
+csrf(app)
+
+ + +

If for some reason you want to know when a CSRF attack happens, you can pass a function to the csrf call and it will be called whenever Garter detects an attack:

+
from garter.csrf import csrf
+
+attacks = 0
+def count_csrf_attacks(endpoint, arguments):
+    attacks += 1
+
+csrf(app, on_csrf=count_csrf_attacks)
+
+ + +

This function must take two parameters:

+ +

You can use this function to do anything you like; counting attacks is just a simple example.

+ + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/index.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + Garter » + Garter + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

Garter

+ + + +

Garter is a collection of small utilities that makes creating webapps with +Flask easier.

+

Flask is a wonderful little web framework, but sometimes you need +a garter to get your webapp working in the big old speakeasy that +is the Internet.

+ +

Installing

+

Use pip to install Garter:

+
pip install -e hg+http://bitbucket.org/sjl/garter/#egg=garter
+
+ + +

Prefer git to Mercurial?

+
pip install -e git+http://github.com/sjl/garter.git#egg=garter
+
+ + +

You can install it without pip, but why would you want to?

+
hg clone http://bitbucket.org/sjl/garter/
+cd garter
+python setup.py install
+
+ + +

Using

+

Garter contains a bunch of useful little functions. Use the ones you need, +ignore the rest.

+

CSRF Protection

+

LessCSS Support

+

URL Convenience Functions

+

Contributing

+

Want to contribute your own useful functions? Fork the +Mercurial repository or git repository and send a +pull request.

+

Make sure you update the documentation to mention your changes!

+ + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/lesscss.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/lesscss.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + Garter » + Lesscss + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

LessCSS Support

+ +

If you use LessCSS to stay sane while writing CSS, you probably know it +can be a pain to run lessc --watch static/style.less every time you want to +work on your styles. It gets even worse when you have more than one .less +file.

+

Garter provides a function that will automatically re-render .less files into CSS before each request if they've changed.

+

You can activate it by calling the lesscss function with your Flask app as a parameter:

+
from garter.lesscss import lesscss
+lesscss(app)
+
+ + +

This will watch your app's static media directory and automatically render .less files into .css files in the same (sub)directory.

+

When you deploy your app you might not want to accept the overhead of checking the modification time of your .less and .css files on each request. A simple way to avoid this is wrapping the lesscss call in an if statement:

+
if app.debug:
+    from garter.lesscss import lesscss
+    lesscss(app)
+
+ + +

If you do this you'll be responsible for rendering the .less files into CSS when you deploy in non-debug mode to your production server.

+ + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/_list.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/_list.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + Garter » + ls /media + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media

+ + +

Directories

+ + + + + + + + + + + + + + + +
+ + css/ + +
+ + images/ + +
+ + sass/ + +
+ + + + + + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/css/_list.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/css/_list.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + Garter » + ls /media/css + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/css

+ + + + + + +

Files

+ + + + + + + + + + + + + + + + + + +
1K + + garter.css + +
4K + + pygments.css + +
4K + + style.css + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/css/garter.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/css/garter.css Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,83 @@ +/* @override http://localhost:8008/media/css/garter.css */ + +html, body { + font-family: "Palatino", "Palatino Linotype", "Times", serif; + font-size: 16px; +} +body { + margin-bottom: 5em; +} + +ol#breadcrumbs { + height: 2em; +} +ol#breadcrumbs li { + font-size: 16px !important; + line-height: 2em; +} + +div.toc ul { + margin: 0; +} +div.toc ul li { + list-style-type: none; +} +div.toc ul ul { + margin-left: 1.5em; +} + +a:link, a:visited, a:active { + color: #e51b52; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +h1 a:link, h1 a:visited, h1 a:active { + color: black; +} +h1 a:hover { + color: #e51b52; + text-decoration: none; +} + +h1 { + font-size: 42px; +} +h2 { + font-size: 26px; +} +h3 { + font-size: 18px; +} + +code, pre { + font-family: "Monaco", "Menlo", "Consolas", "Courier New", monospaced; + font-size: 12px; + line-height: 1.75em; +} +div.codehilite { + margin: 0; + padding: 5px 36px; + margin-left: 0px; + margin-bottom: 1.4em; + width: 560px; + background-color: #f5f5f5; + border: 1px dotted #eee; + border-right: none; +} + +div#footer { + font-size: 14px; + font-style: italic; + text-align: center; + text-transform: none; +} + +h1#splash-logo { + display: none; +} +div#logo { + text-align: center; + margin: 8px 0 16px; +} \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/css/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/css/index.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + Garter » + ls /media/css + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/css

+ + + + + + +

Files

+ + + + + + + + + + + + + + + + + + +
1K + + garter.css + +
4K + + pygments.css + +
4K + + style.css + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/css/pygments.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/css/pygments.css Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,60 @@ +.codehilite { background: #ffffff; } +.codehilite .c { color: #999988; font-style: italic } /* Comment */ +.codehilite .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.codehilite .k { font-weight: bold } /* Keyword */ +.codehilite .o { font-weight: bold } /* Operator */ +.codehilite .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.codehilite .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.codehilite .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.codehilite .ge { font-style: italic } /* Generic.Emph */ +.codehilite .gr { color: #aa0000 } /* Generic.Error */ +.codehilite .gh { color: #999999 } /* Generic.Heading */ +.codehilite .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.codehilite .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.codehilite .go { color: #888888 } /* Generic.Output */ +.codehilite .gp { color: #555555 } /* Generic.Prompt */ +.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .gu { color: #aaaaaa } /* Generic.Subheading */ +.codehilite .gt { color: #aa0000 } /* Generic.Traceback */ +.codehilite .kc { font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { font-weight: bold } /* Keyword.Declaration */ +.codehilite .kp { font-weight: bold } /* Keyword.Pseudo */ +.codehilite .kr { font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.codehilite .m { color: #009999 } /* Literal.Number */ +.codehilite .s { color: #d14 } /* Literal.String */ +.codehilite .na { color: #008080 } /* Name.Attribute */ +.codehilite .nb { color: #0086B3 } /* Name.Builtin */ +.codehilite .nc { color: #445588; font-weight: bold } /* Name.Class */ +.codehilite .no { color: #008080 } /* Name.Constant */ +.codehilite .ni { color: #800080 } /* Name.Entity */ +.codehilite .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #990000; font-weight: bold } /* Name.Function */ +.codehilite .nn { color: #555555 } /* Name.Namespace */ +.codehilite .nt { color: #000080 } /* Name.Tag */ +.codehilite .nv { color: #008080 } /* Name.Variable */ +.codehilite .ow { font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ +.codehilite .mf { color: #009999 } /* Literal.Number.Float */ +.codehilite .mh { color: #009999 } /* Literal.Number.Hex */ +.codehilite .mi { color: #009999 } /* Literal.Number.Integer */ +.codehilite .mo { color: #009999 } /* Literal.Number.Oct */ +.codehilite .sb { color: #d14 } /* Literal.String.Backtick */ +.codehilite .sc { color: #d14 } /* Literal.String.Char */ +.codehilite .sd { color: #d14 } /* Literal.String.Doc */ +.codehilite .s2 { color: #d14 } /* Literal.String.Double */ +.codehilite .se { color: #d14 } /* Literal.String.Escape */ +.codehilite .sh { color: #d14 } /* Literal.String.Heredoc */ +.codehilite .si { color: #d14 } /* Literal.String.Interpol */ +.codehilite .sx { color: #d14 } /* Literal.String.Other */ +.codehilite .sr { color: #009926 } /* Literal.String.Regex */ +.codehilite .s1 { color: #d14 } /* Literal.String.Single */ +.codehilite .ss { color: #990073 } /* Literal.String.Symbol */ +.codehilite .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.codehilite .vc { color: #008080 } /* Name.Variable.Class */ +.codehilite .vg { color: #008080 } /* Name.Variable.Global */ +.codehilite .vi { color: #008080 } /* Name.Variable.Instance */ +.codehilite .il { color: #009999 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/css/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/css/style.css Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,178 @@ +html { + background-color: #f6f6f6; } + +body { + background-color: white; + margin: 0 auto; + width: 650px; } + body #breadcrumbs, body #content, body #footer { + background-color: white; + clear: both; + float: left; + overflow: hidden; + padding: 0 20px; + width: 610px; } + body #breadcrumbs { + border-bottom: 2px solid #f6f6f6; + height: 28px; + margin: 0; + padding: 0; + width: 650px; } + body #breadcrumbs li { + float: left; + list-style: none; + margin: 0; + padding: 0; } + body #breadcrumbs li a { + display: block; + float: left; + padding: 0 8px; } + body #breadcrumbs li.last { + padding: 0 8px; } + body #breadcrumbs li.not-last:after { + content: "»"; + float: right; } + body #footer { + border-top: 8px solid #f6f6f6; + padding-top: 13px; } + body .clear { + clear: both; + border-width: 0; + margin: 0; + visibility: hidden; } + +body.listing table#pages tr, body.listing table#subdirs tr, body.listing table#files tr { + border-bottom: 1px solid #777; + border-top: 1px solid #777; } +body.listing table#pages td, body.listing table#subdirs td, body.listing table#files td { + border: none; } + body.listing table#pages td.size, body.listing table#subdirs td.size, body.listing table#files td.size { + background-color: #f6f6f6; } + body.listing table#pages td.name, body.listing table#subdirs td.name, body.listing table#files td.name { + padding: 0; } + body.listing table#pages td.name a, body.listing table#subdirs td.name a, body.listing table#files td.name a { + display: block; + margin: 0; + padding: 4px 8px; } + +blockquote { + background-color: #f6f6f6; + padding: 13px; + padding-bottom: 1px; } + +hr { + border-style: solid; + border: none; + border-top: 1px solid #777; + margin: 28px 0; } + +dl { + margin-left: 0; } + dl dd { + margin-bottom: 13px; + margin-left: 13px; } + +ul { + margin-top: 0; } + ul li { + list-style: square outside; } + ul ul { + margin-bottom: 0; } + +pre { + border-left: 1px solid gray; + margin-bottom: 13px; + margin-left: 30px; + padding-left: 12px; } + +.codehilite { + border-left: 1px solid gray; + margin-bottom: 13px; + margin-left: 30px; + padding-left: 12px; } + .codehilite pre { + border: none; + margin: 0; + padding: 0; } + +.codehilitetable { + margin-left: 0; + padding-left: 0; } + .codehilitetable tr td { + border: none; + padding: 3px 5px 0 5px; } + .codehilitetable tr td.linenos { + background-color: #f6f6f6; + border-right: 1px solid gray; + margin: 0; + padding-right: 6px; + text-align: right; + width: 19px; } + .codehilitetable tr td.linenos .linenodiv pre { + border: none; + margin: 0; + padding: 0; } + .codehilitetable tr td.code { + margin: 0; + padding-left: 12px; } + .codehilitetable tr td.code .codehilite { + border: none; + margin: 0; + padding: 0; } + +body { + font-family: 'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; + line-height: 21px; } + body #breadcrumbs li { + color: #aaa; + font-size: 13px; + font-weight: bold; + line-height: 28px; } + body #breadcrumbs li a { + text-decoration: none; } + body #breadcrumbs li .list-crumb { + font-weight: normal; } + body #footer { + color: #777; + font-size: 13px; + text-transform: lowercase; } + body.listing table#pages td.size, body.listing table#subdirs td.size { + font-family: Menlo, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', Courier, 'Courier 10 Pitch', 'Courier New', monospace; + text-align: right; } + body.listing table#subdirs td.name { + font-family: Courier, 'Courier 10 Pitch', 'Courier New', monospace; } + +h1, h2, h3, h4, h5, h6 { + line-height: 21px; } + +h1 { + font-size: 21px; } + +h2 { + font-size: 18px; } + +h3 { + font-size: 15px; } + +h4, h5, h6 { + font-size: 13px; } + +a { + color: #990000; + text-decoration: none; } + a:hover { + color: #4c0000; } + a[href^="http:"] { + text-decoration: underline; } + +dl dt { + font-weight: bold; } + +code { + font-family: Courier, 'Courier 10 Pitch', 'Courier New', monospace; + line-height: 18px; } + +pre { + font-family: Menlo, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', Courier, 'Courier 10 Pitch', 'Courier New', monospace; + font-size: 11px; + line-height: 18px; } diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/images/_list.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/images/_list.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + Garter » + ls /media/images + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/images

+ + + + + + +

Files

+ + + + + + + + +
13K + + logo.png + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/images/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/images/index.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + Garter » + ls /media/images + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/images

+ + + + + + +

Files

+ + + + + + + + +
13K + + logo.png + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/images/logo.png Binary file garter/media/images/logo.png has changed diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/index.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + Garter » + ls /media + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media

+ + +

Directories

+ + + + + + + + + + + + + + + +
+ + css/ + +
+ + images/ + +
+ + sass/ + +
+ + + + + + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/sass/_list.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/sass/_list.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + Garter » + ls /media/sass + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/sass

+ + + + + + +

Files

+ + + + + + + + +
46B + + style.sass + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/sass/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/sass/index.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + Garter » + ls /media/sass + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

ls /media/sass

+ + + + + + +

Files

+ + + + + + + + +
46B + + style.sass + +
+ + + + + + +
+
+ + + + + + +
+ + \ No newline at end of file diff -r 50444dfe7265 -r ca20cf1cb90b garter/media/sass/style.sass --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/media/sass/style.sass Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,2 @@ +@import _layout.sass +@import _typography.sass diff -r 50444dfe7265 -r ca20cf1cb90b garter/urls.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garter/urls.html Tue May 04 22:59:21 2010 -0400 @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + Garter » + Urls + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

URL Convenience Functions

+ +

URLs are a pain. Garter tries to help by providing some useful functions to make working with URLs easier.

+
+ +
+ +

The permalink decorator was taken from +this snippet on the Flask site. It's used to wrap functions so they only need to return the arguments to Flask's url_for function, instead of calling the function themselves.

+

For example, say you have several classes that represents items on your site:

+
from flask import url_for
+
+class Event(object):
+    def __init__(self, event_id):
+        self.event_id = event_id
+
+    def link(self):
+        return url_for('event', event_id=self.event_id)
+
+class User(object):
+    def __init__(self, username):
+        self.username = username
+
+    def link(self):
+        return url_for('profile', username=self.username)
+
+ + +

Using the permalink decorator can make the link functions a bit cleaner:

+
from garter.urls import permalink
+
+class Event(object):
+    def __init__(self, event_id):
+        self.event_id = event_id
+
+    @permalink
+    def link(self):
+        return 'event', { 'event_id': self.event_id }
+
+class User(object):
+    def __init__(self, username):
+        self.username = username
+
+    @permalink
+    def link(self):
+        return 'profile', { 'username': self.username }
+
+ + + + +
+
+ + + + + + +
+ + \ No newline at end of file