Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial boilerplate setup

  • Loading branch information...
commit b03273db54d05eabfe17c0cfcf45ddf41321b08e 1 parent ef08f17
@kamalgill kamalgill authored
Showing with 28,261 additions and 4 deletions.
  1. +6 −4 README.md
  2. +1 −0  docs/README.rst
  3. +13 −0 src/app.yaml
  4. +8 −0 src/application/__init__.py
  5. +11 −0 src/application/decorators.py
  6. +8 −0 src/application/forms.py
  7. +10 −0 src/application/models.py
  8. +21 −0 src/application/settings.py
  9. +7 −0 src/application/static/css/main.css
  10. +281 −0 src/application/static/css/style.css
  11. BIN  src/application/static/img/favicon.ico
  12. BIN  src/application/static/img/favicon.png
  13. 0  src/application/static/js/main.js
  14. +28 −0 src/application/static/js/modernizr-1.5.min.js
  15. +22 −0 src/application/templates/404.html
  16. +41 −0 src/application/templates/base.html
  17. +26 −0 src/application/templates/list_examples.html
  18. +31 −0 src/application/templates/new_example.html
  19. +42 −0 src/application/views.py
  20. +34 −0 src/flask/__init__.py
  21. +874 −0 src/flask/app.py
  22. +152 −0 src/flask/config.py
  23. +66 −0 src/flask/ctx.py
  24. +20 −0 src/flask/globals.py
  25. +463 −0 src/flask/helpers.py
  26. +42 −0 src/flask/logging.py
  27. +221 −0 src/flask/module.py
  28. +43 −0 src/flask/session.py
  29. +50 −0 src/flask/signals.py
  30. +96 −0 src/flask/templating.py
  31. +45 −0 src/flask/testing.py
  32. +88 −0 src/flask/wrappers.py
  33. +1 −0  src/flaskext/__init__.py
  34. +158 −0 src/flaskext/wtf/__init__.py
  35. +5 −0 src/flaskext/wtf/recaptcha/__init__.py
  36. +16 −0 src/flaskext/wtf/recaptcha/fields.py
  37. +70 −0 src/flaskext/wtf/recaptcha/validators.py
  38. +78 −0 src/flaskext/wtf/recaptcha/widgets.py
  39. +11 −0 src/index.yaml
  40. +73 −0 src/jinja2/__init__.py
  41. +259 −0 src/jinja2/_speedups.c
  42. +130 −0 src/jinja2/_stringdefs.py
  43. +280 −0 src/jinja2/bccache.py
  44. +1,642 −0 src/jinja2/compiler.py
  45. +290 −0 src/jinja2/constants.py
  46. +308 −0 src/jinja2/debug.py
  47. +40 −0 src/jinja2/defaults.py
  48. +1,118 −0 src/jinja2/environment.py
  49. +143 −0 src/jinja2/exceptions.py
  50. +604 −0 src/jinja2/ext.py
  51. +725 −0 src/jinja2/filters.py
  52. +680 −0 src/jinja2/lexer.py
  53. +449 −0 src/jinja2/loaders.py
  54. +102 −0 src/jinja2/meta.py
  55. +901 −0 src/jinja2/nodes.py
  56. +68 −0 src/jinja2/optimizer.py
  57. +882 −0 src/jinja2/parser.py
  58. +535 −0 src/jinja2/runtime.py
  59. +271 −0 src/jinja2/sandbox.py
  60. +146 −0 src/jinja2/tests.py
  61. +95 −0 src/jinja2/testsuite/__init__.py
  62. +232 −0 src/jinja2/testsuite/api.py
  63. +286 −0 src/jinja2/testsuite/core_tags.py
  64. +60 −0 src/jinja2/testsuite/debug.py
  65. +29 −0 src/jinja2/testsuite/doctests.py
  66. +432 −0 src/jinja2/testsuite/ext.py
  67. +285 −0 src/jinja2/testsuite/filters.py
  68. +144 −0 src/jinja2/testsuite/imports.py
  69. +208 −0 src/jinja2/testsuite/inheritance.py
  70. +374 −0 src/jinja2/testsuite/lexnparse.py
  71. +191 −0 src/jinja2/testsuite/loader.py
  72. +258 −0 src/jinja2/testsuite/regression.py
  73. 0  src/jinja2/testsuite/res/__init__.py
  74. +3 −0  src/jinja2/testsuite/res/templates/broken.html
  75. +1 −0  src/jinja2/testsuite/res/templates/foo/test.html
  76. +4 −0 src/jinja2/testsuite/res/templates/syntaxerror.html
  77. +1 −0  src/jinja2/testsuite/res/templates/test.html
  78. +134 −0 src/jinja2/testsuite/security.py
  79. +87 −0 src/jinja2/testsuite/tests.py
  80. +85 −0 src/jinja2/testsuite/utils.py
  81. +819 −0 src/jinja2/utils.py
  82. +87 −0 src/jinja2/visitor.py
  83. +5 −0 src/main.py
  84. +2,677 −0 src/pkg_resources.py
  85. +437 −0 src/simplejson/__init__.py
  86. +2,561 −0 src/simplejson/_speedups.c
  87. +421 −0 src/simplejson/decoder.py
  88. +501 −0 src/simplejson/encoder.py
  89. +119 −0 src/simplejson/ordered_dict.py
  90. +77 −0 src/simplejson/scanner.py
  91. +63 −0 src/simplejson/tests/__init__.py
  92. +30 −0 src/simplejson/tests/test_check_circular.py
  93. +33 −0 src/simplejson/tests/test_decimal.py
  94. +73 −0 src/simplejson/tests/test_decode.py
  95. +9 −0 src/simplejson/tests/test_default.py
  96. +27 −0 src/simplejson/tests/test_dump.py
  97. +41 −0 src/simplejson/tests/test_encode_basestring_ascii.py
  98. +32 −0 src/simplejson/tests/test_encode_for_html.py
  99. +91 −0 src/simplejson/tests/test_fail.py
  100. +19 −0 src/simplejson/tests/test_float.py
  101. +53 −0 src/simplejson/tests/test_indent.py
  102. +76 −0 src/simplejson/tests/test_pass1.py
  103. +14 −0 src/simplejson/tests/test_pass2.py
  104. +20 −0 src/simplejson/tests/test_pass3.py
  105. +67 −0 src/simplejson/tests/test_recursion.py
  106. +117 −0 src/simplejson/tests/test_scanstring.py
  107. +42 −0 src/simplejson/tests/test_separators.py
  108. +21 −0 src/simplejson/tests/test_speedups.py
  109. +99 −0 src/simplejson/tests/test_unicode.py
  110. +39 −0 src/simplejson/tool.py
  111. +157 −0 src/werkzeug/__init__.py
  112. +398 −0 src/werkzeug/_internal.py
  113. +16 −0 src/werkzeug/contrib/__init__.py
  114. +343 −0 src/werkzeug/contrib/atom.py
  115. +511 −0 src/werkzeug/contrib/cache.py
  116. +205 −0 src/werkzeug/contrib/fixers.py
  117. +281 −0 src/werkzeug/contrib/iterio.py
  118. +258 −0 src/werkzeug/contrib/jsrouting.py
  119. +284 −0 src/werkzeug/contrib/kickstart.py
  120. +36 −0 src/werkzeug/contrib/limiter.py
  121. +331 −0 src/werkzeug/contrib/lint.py
  122. +116 −0 src/werkzeug/contrib/profiler.py
  123. +328 −0 src/werkzeug/contrib/securecookie.py
  124. +342 −0 src/werkzeug/contrib/sessions.py
  125. +66 −0 src/werkzeug/contrib/testtools.py
  126. +275 −0 src/werkzeug/contrib/wrappers.py
Sorry, we could not display the entire diff because it was too big.
View
10 README.md
@@ -12,9 +12,11 @@ Setup Instructions
tk
-Notes
------
-This project template was heavily inspired by Francisco Souza's [gaeseries flask template][1]
+Credits
+-------
+Project template layout was heavily inspired by Francisco Souza's [gaeseries flask template][1]
-[1]: http://github.com/franciscosouza/gaeseries/tree/flask
+HTML5-based main template (templates/base.html) and base CSS styles (static/css/style.css) extracted from [HTML5 Boilerplate][2]
+[1]: http://github.com/franciscosouza/gaeseries/tree/flask
+[2]: http://html5boilerplate.com/
View
1  docs/README.rst
@@ -0,0 +1 @@
+Your application's documentation here
View
13 src/app.yaml
@@ -0,0 +1,13 @@
+application: myflaskonappengineapp
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /favicon.ico
+ static_files: application/static/img/favicon.ico
+ upload: application/static/img/favicon.ico
+
+- url: .*
+ script: main.py
+
View
8 src/application/__init__.py
@@ -0,0 +1,8 @@
+
+from flask import Flask
+import settings
+
+app = Flask(__name__)
+app.config.from_object('application.settings')
+
+import views
View
11 src/application/decorators.py
@@ -0,0 +1,11 @@
+from functools import wraps
+from google.appengine.api import users
+from flask import redirect, request
+
+def login_required(func):
+ @wraps(func)
+ def decorated_view(*args, **kwargs):
+ if not users.get_current_user():
+ return redirect(users.create_login_url(request.url))
+ return func(*args, **kwargs)
+ return decorated_view
View
8 src/application/forms.py
@@ -0,0 +1,8 @@
+
+from flaskext import wtf
+from flaskext.wtf import validators
+
+
+class ExampleForm(wtf.Form):
+ example_id = wtf.TextField('Example ID', validators=[validators.Required()])
+ example_title = wtf.TextField('Example Title', validators=[validators.Required()])
View
10 src/application/models.py
@@ -0,0 +1,10 @@
+
+from google.appengine.ext import db
+
+
+class ExampleModel(db.Model):
+ """Example Model"""
+ example_id = db.StringProperty(required = True)
+ example_title = db.StringProperty(required = True)
+ added_by = db.UserProperty()
+ timestamp = db.DateTimeProperty(auto_now_add = True)
View
21 src/application/settings.py
@@ -0,0 +1,21 @@
+'''
+Place your keys in the secret_keys.py module, which will be kept out of version control
+
+'''
+
+
+import os
+from secret_keys import CSRF_SECRET_KEY, SESSION_KEY
+
+
+# Auto-set debug mode based on App Engine dev environ
+DEBUG_MODE = os.environ['SERVER_SOFTWARE'].startswith('Dev')
+
+DEBUG = DEBUG_MODE
+
+# Set secret keys for CSRF protection
+SECRET_KEY = CSRF_SECRET_KEY
+CSRF_SESSION_LKEY = SESSION_KEY
+
+CSRF_ENABLED = True
+
View
7 src/application/static/css/main.css
@@ -0,0 +1,7 @@
+
+/* Main stylesheet */
+
+html {background-color: #333;}
+body {background-color: #fff; margin: 2em auto; width: 960px; padding: 1em; border: 8px solid #ccc;}
+
+/* Your customizations here */
View
281 src/application/static/css/style.css
@@ -0,0 +1,281 @@
+/*
+ style.css contains a reset, font normalization and some base styles.
+
+ credit is left where credit is due.
+ additionally, much inspiration was taken from these projects:
+ yui.yahooapis.com/2.8.1/build/base/base.css
+ camendesign.com/design/
+ praegnanz.de/weblog/htmlcssjs-kickstart
+*/
+
+/*
+ html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
+ v1.4 2009-07-27 | Authors: Eric Meyer & Richard Clark
+ html5doctor.com/html-5-reset-stylesheet/
+*/
+
+html, body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+abbr, address, cite, code,
+del, dfn, em, img, ins, kbd, q, samp,
+small, strong, sub, sup, var,
+b, i,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section, summary,
+time, mark, audio, video {
+ margin:0;
+ padding:0;
+ border:0;
+ outline:0;
+ font-size:100%;
+ vertical-align:baseline;
+ background:transparent;
+}
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display:block;
+}
+
+nav ul { list-style:none; }
+
+blockquote, q { quotes:none; }
+
+blockquote:before, blockquote:after,
+q:before, q:after { content:''; content:none; }
+
+a { margin:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent; }
+
+ins { background-color:#ff9; color:#000; text-decoration:none; }
+
+mark { background-color:#ff9; color:#000; font-style:italic; font-weight:bold; }
+
+del { text-decoration: line-through; }
+
+abbr[title], dfn[title] { border-bottom:1px dotted; cursor:help; }
+
+/* tables still need cellspacing="0" in the markup */
+table { border-collapse:collapse; border-spacing:0; }
+
+hr { display:block; height:1px; border:0; border-top:1px solid #ccc; margin:1em 0; padding:0; }
+
+input, select { vertical-align:middle; }
+/* END RESET CSS */
+
+
+/* fonts.css from the YUI Library: developer.yahoo.com/yui/
+ Please refer to developer.yahoo.com/yui/fonts/ for font sizing percentages
+
+ There are three custom edits:
+ * remove arial, helvetica from explicit font stack
+ * we normalize monospace styles ourselves
+ * table font-size is reset in the HTML5 reset above so there is no need to repeat
+*/
+body { font:13px/1.231 sans-serif; *font-size:small; } /* hack retained to preserve specificity */
+
+select, input, textarea, button { font:99% sans-serif; }
+
+/* normalize monospace sizing
+ * en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome
+ */
+pre, code, kbd, samp { font-family: monospace, sans-serif; }
+
+
+/*
+ * minimal base styles
+ */
+
+
+body, select, input, textarea {
+ /* #444 looks better than black: twitter.com/H_FJ/statuses/11800719859 */
+ color: #444;
+ /* set your base font here, to apply evenly
+ /* font-family: Georgia, serif; */
+}
+
+/* Headers (h1,h2,etc) have no default font-size or margin,
+ you'll want to define those yourself. */
+h1,h2,h3,h4,h5,h6 { font-weight: bold; }
+
+/* always force a scrollbar in non-IE */
+html { overflow-y: scroll; }
+
+
+/* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */
+a:hover, a:active { outline: none; }
+
+a, a:active, a:visited { color: #607890; }
+a:hover { color: #036; }
+
+
+ul, ol { margin-left: 1.8em; }
+ol { list-style-type: decimal; }
+
+small { font-size: 85%; }
+strong, th { font-weight: bold; }
+
+td, td img { vertical-align: top; }
+
+sub { vertical-align: sub; font-size: smaller; }
+sup { vertical-align: super; font-size: smaller; }
+
+pre {
+ padding: 15px;
+
+ /* www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ */
+ white-space: pre; /* CSS2 */
+ white-space: pre-wrap; /* CSS 2.1 */
+ white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
+ word-wrap: break-word; /* IE */
+}
+
+textarea { overflow: auto; } /* thnx ivannikolic! www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ */
+
+.ie6 legend, .ie7 legend { margin-left: -7px; } /* thnx ivannikolic! */
+
+/* align checkboxes, radios, text inputs with their label
+ by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css */
+input[type="radio"] { vertical-align: text-bottom; }
+input[type="checkbox"] { vertical-align: bottom; }
+.ie7 input[type="checkbox"] { vertical-align: baseline; }
+.ie6 input { vertical-align: text-bottom; }
+
+/* hand cursor on clickable input elements */
+label, input[type=button], input[type=submit], button { cursor: pointer; }
+
+/* webkit browsers add a 2px margin outside the chrome of form elements */
+button, input, select, textarea { margin: 0; }
+
+/* colors for form validity */
+input:valid, textarea:valid { }
+input:invalid, textarea:invalid {
+ border-radius: 1px;
+ -moz-box-shadow: 0px 0px 5px red;
+ -webkit-box-shadow: 0px 0px 5px red;
+ box-shadow: 0px 0px 5px red;
+}
+.no-boxshadow input:invalid,
+.no-boxshadow textarea:invalid { background-color: #f0dddd; }
+
+
+/* These selection declarations have to be separate.
+ No text-shadow: twitter.com/miketaylr/status/12228805301
+ Also: hot pink. */
+::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
+::selection { background:#FF5E99; color:#fff; text-shadow: none; }
+
+/* j.mp/webkit-tap-highlight-color */
+a:link { -webkit-tap-highlight-color: #FF5E99; }
+
+/* make buttons play nice in IE:
+ www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */
+button { width: auto; overflow: visible; }
+
+/* bicubic resizing for non-native sized IMG:
+ code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ */
+.ie7 img { -ms-interpolation-mode: bicubic; }
+
+
+
+/*
+ * Non-semantic helper classes
+ */
+
+/* for image replacement */
+.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
+
+/* Hide for both screenreaders and browsers
+ css-discuss.incutio.com/wiki/Screenreader_Visibility */
+.hidden { display: none; visibility: hidden; }
+
+/* Hide only visually, but have it available for screenreaders
+ www.webaim.org/techniques/css/invisiblecontent/
+ Solution from: j.mp/visuallyhidden - Thanks Jonathan Neal! */
+.visuallyhidden { position: absolute !important;
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px); }
+
+/* Hide visually and from screenreaders, but maintain layout */
+.invisible { visibility: hidden; }
+
+/* >> The Magnificent CLEARFIX << j.mp/phayesclearfix */
+.clearfix:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; }
+/* Fix clearfix: blueprintcss.lighthouseapp.com/projects/15318/tickets/5-extra-margin-padding-bottom-of-page */
+.clearfix { zoom: 1; }
+
+
+
+
+
+
+ /* Primary Styles
+ Author:
+ */
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * print styles
+ * inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/
+ */
+@media print {
+ * { background: transparent !important; color: #444 !important; text-shadow: none !important; }
+
+ a, a:visited { color: #444 !important; text-decoration: underline; }
+
+ a:after { content: " (" attr(href) ")"; }
+
+ abbr:after { content: " (" attr(title) ")"; }
+
+ .ir a:after { content: ""; } /* Don't show links for images */
+
+ pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
+
+ thead { display: table-header-group; } /* css-discuss.incutio.com/wiki/Printing_Tables */
+
+ tr, img { page-break-inside: avoid; }
+
+ @page { margin: 0.5cm; }
+
+ p, h2, h3 { orphans: 3; widows: 3; }
+
+ h2, h3{ page-break-after: avoid; }
+}
+
+
+
+/*
+ * Media queries for responsive design
+ */
+
+@media all and (orientation:portrait) {
+ /* Style adjustments for portrait mode goes here */
+
+}
+
+@media all and (orientation:landscape) {
+ /* Style adjustments for landscape mode goes here */
+
+}
+
+/* Grade-A Mobile Browsers (Opera Mobile, iPhone Safari, Android Chrome)
+ Consider this: www.cloudfour.com/css-media-query-for-mobile-is-fools-gold/ */
+@media screen and (max-device-width: 480px) {
+
+
+ /* Uncomment if you don't want iOS and WinMobile to mobile-optimize the text for you
+ j.mp/textsizeadjust
+ html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
+}
+
View
BIN  src/application/static/img/favicon.ico
Binary file not shown
View
BIN  src/application/static/img/favicon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  src/application/static/js/main.js
No changes.
View
28 src/application/static/js/modernizr-1.5.min.js
@@ -0,0 +1,28 @@
+/*!
+ * Modernizr JavaScript library 1.5
+ * http://www.modernizr.com/
+ *
+ * Copyright (c) 2009-2010 Faruk Ates - http://farukat.es/
+ * Dual-licensed under the BSD and MIT licenses.
+ * http://www.modernizr.com/license/
+ *
+ * Featuring major contributions by
+ * Paul Irish - http://paulirish.com
+ */
+ window.Modernizr=function(i,e,I){function C(a,b){for(var c in a)if(m[a[c]]!==I&&(!b||b(a[c],D)))return true}function r(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1);return!!C([a,"Webkit"+c,"Moz"+c,"O"+c,"ms"+c,"Khtml"+c],b)}function P(){j[E]=function(a){for(var b=0,c=a.length;b<c;b++)J[a[b]]=!!(a[b]in n);return J}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" "));j[Q]=function(a){for(var b=0,c,h=a.length;b<h;b++){n.setAttribute("type",a[b]);if(c=n.type!==
+ "text"){n.value=K;/tel|search/.test(n.type)||(c=/url|email/.test(n.type)?n.checkValidity&&n.checkValidity()===false:n.value!=K)}L[a[b]]=!!c}return L}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var j={},s=e.documentElement,D=e.createElement("modernizr"),m=D.style,n=e.createElement("input"),E="input",Q=E+"types",K=":)",M=Object.prototype.toString,y=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),d={},L={},J={},N=[],u=function(){var a={select:"input",
+ change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"},b={};return function(c,h){var t=arguments.length==1;if(t&&b[c])return b[c];h=h||document.createElement(a[c]||"div");c="on"+c;var g=c in h;if(!g&&h.setAttribute){h.setAttribute(c,"return;");g=typeof h[c]=="function"}h=null;return t?(b[c]=g):g}}(),F={}.hasOwnProperty,O;O=typeof F!=="undefined"&&typeof F.call!=="undefined"?function(a,b){return F.call(a,b)}:function(a,b){return b in a&&typeof a.constructor.prototype[b]==="undefined"};
+ d.canvas=function(){return!!e.createElement("canvas").getContext};d.canvastext=function(){return!!(d.canvas()&&typeof e.createElement("canvas").getContext("2d").fillText=="function")};d.geolocation=function(){return!!navigator.geolocation};d.crosswindowmessaging=function(){return!!i.postMessage};d.websqldatabase=function(){var a=!!i.openDatabase;if(a)try{a=!!openDatabase("testdb","1.0","html5 test db",2E5)}catch(b){a=false}return a};d.indexedDB=function(){return!!i.indexedDB};d.hashchange=function(){return u("hashchange",
+ i)&&(document.documentMode===I||document.documentMode>7)};d.historymanagement=function(){return!!(i.history&&history.pushState)};d.draganddrop=function(){return u("drag")&&u("dragstart")&&u("dragenter")&&u("dragover")&&u("dragleave")&&u("dragend")&&u("drop")};d.websockets=function(){return"WebSocket"in i};d.rgba=function(){m.cssText="background-color:rgba(150,255,150,.5)";return(""+m.backgroundColor).indexOf("rgba")!==-1};d.hsla=function(){m.cssText="background-color:hsla(120,40%,100%,.5)";return(""+
+ m.backgroundColor).indexOf("rgba")!==-1};d.multiplebgs=function(){m.cssText="background:url(//:),url(//:),red url(//:)";return/(url\s*\(.*?){3}/.test(m.background)};d.backgroundsize=function(){return r("backgroundSize")};d.borderimage=function(){return r("borderImage")};d.borderradius=function(){return r("borderRadius","",function(a){return(""+a).indexOf("orderRadius")!==-1})};d.boxshadow=function(){return r("boxShadow")};d.opacity=function(){var a=y.join("opacity:.5;")+"";m.cssText=a;return(""+m.opacity).indexOf("0.5")!==
+ -1};d.cssanimations=function(){return r("animationName")};d.csscolumns=function(){return r("columnCount")};d.cssgradients=function(){var a=("background-image:"+y.join("gradient(linear,left top,right bottom,from(#9f9),to(white));background-image:")+y.join("linear-gradient(left top,#9f9, white);background-image:")).slice(0,-17);m.cssText=a;return(""+m.backgroundImage).indexOf("gradient")!==-1};d.cssreflections=function(){return r("boxReflect")};d.csstransforms=function(){return!!C(["transformProperty",
+ "WebkitTransform","MozTransform","OTransform","msTransform"])};d.csstransforms3d=function(){var a=!!C(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);if(a){var b=document.createElement("style"),c=e.createElement("div");b.textContent="@media ("+y.join("transform-3d),(")+"modernizr){#modernizr{height:3px}}";e.getElementsByTagName("head")[0].appendChild(b);c.id="modernizr";s.appendChild(c);a=c.offsetHeight===3;b.parentNode.removeChild(b);c.parentNode.removeChild(c)}return a};
+ d.csstransitions=function(){return r("transitionProperty")};d.fontface=function(){var a;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)a=true;else{var b=e.createElement("style"),c=e.createElement("span"),h,t=false,g=e.body,o,w;b.textContent="@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}";
+ e.getElementsByTagName("head")[0].appendChild(b);c.setAttribute("style","font:99px _,arial,helvetica;position:absolute;visibility:hidden");if(!g){g=s.appendChild(e.createElement("fontface"));t=true}c.innerHTML="........";c.id="fonttest";g.appendChild(c);h=c.offsetWidth*c.offsetHeight;c.style.font="99px testfont,_,arial,helvetica";a=h!==c.offsetWidth*c.offsetHeight;var v=function(){if(g.parentNode){a=j.fontface=h!==c.offsetWidth*c.offsetHeight;s.className=s.className.replace(/(no-)?fontface\b/,"")+
+ (a?" ":" no-")+"fontface"}};setTimeout(v,75);setTimeout(v,150);addEventListener("load",function(){v();(w=true)&&o&&o(a);setTimeout(function(){t||(g=c);g.parentNode.removeChild(g);b.parentNode.removeChild(b)},50)},false)}j._fontfaceready=function(p){w||a?p(a):(o=p)};return a||h!==c.offsetWidth};d.video=function(){var a=e.createElement("video"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('video/ogg; codecs="theora"');b.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"');b.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return b};
+ d.audio=function(){var a=e.createElement("audio"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('audio/ogg; codecs="vorbis"');b.mp3=a.canPlayType("audio/mpeg;");b.wav=a.canPlayType('audio/wav; codecs="1"');b.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")}return b};d.localStorage=function(){return"localStorage"in i&&i.localStorage!==null};d.sessionStorage=function(){try{return"sessionStorage"in i&&i.sessionStorage!==null}catch(a){return false}};d.webworkers=function(){return!!i.Worker};
+ d.applicationCache=function(){var a=i.applicationCache;return!!(a&&typeof a.status!="undefined"&&typeof a.update=="function"&&typeof a.swapCache=="function")};d.svg=function(){return!!e.createElementNS&&!!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect};d.smil=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg","animate")))};d.svgclippaths=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg",
+ "clipPath")))};for(var z in d)if(O(d,z))N.push(((j[z.toLowerCase()]=d[z]())?"":"no-")+z.toLowerCase());j[E]||P();j.addTest=function(a,b){a=a.toLowerCase();if(!j[a]){b=!!b();s.className+=" "+(b?"":"no-")+a;j[a]=b;return j}};m.cssText="";D=n=null;(function(){var a=e.createElement("div");a.innerHTML="<elem></elem>";return a.childNodes.length!==1})()&&function(a,b){function c(f,k){if(o[f])o[f].styleSheet.cssText+=k;else{var l=t[G],q=b[A]("style");q.media=f;l.insertBefore(q,l[G]);o[f]=q;c(f,k)}}function h(f,
+ k){for(var l=new RegExp("\\b("+w+")\\b(?!.*[;}])","gi"),q=function(B){return".iepp_"+B},x=-1;++x<f.length;){k=f[x].media||k;h(f[x].imports,k);c(k,f[x].cssText.replace(l,q))}}for(var t=b.documentElement,g=b.createDocumentFragment(),o={},w="abbr|article|aside|audio|canvas|command|datalist|details|figure|figcaption|footer|header|hgroup|keygen|mark|meter|nav|output|progress|section|source|summary|time|video",v=w.split("|"),p=[],H=-1,G="firstChild",A="createElement";++H<v.length;){b[A](v[H]);g[A](v[H])}g=
+ g.appendChild(b[A]("div"));a.attachEvent("onbeforeprint",function(){for(var f,k=b.getElementsByTagName("*"),l,q,x=new RegExp("^"+w+"$","i"),B=-1;++B<k.length;)if((f=k[B])&&(q=f.nodeName.match(x))){l=new RegExp("^\\s*<"+q+"(.*)\\/"+q+">\\s*$","i");g.innerHTML=f.outerHTML.replace(/\r|\n/g," ").replace(l,f.currentStyle.display=="block"?"<div$1/div>":"<span$1/span>");l=g.childNodes[0];l.className+=" iepp_"+q;l=p[p.length]=[f,l];f.parentNode.replaceChild(l[1],l[0])}h(b.styleSheets,"all")});a.attachEvent("onafterprint",
+ function(){for(var f=-1,k;++f<p.length;)p[f][1].parentNode.replaceChild(p[f][0],p[f][1]);for(k in o)t[G].removeChild(o[k]);o={};p=[]})}(this,e);j._enableHTML5=true;j._version="1.5";s.className=s.className.replace(/\bno-js\b/,"")+" js";s.className+=" "+N.join(" ");return j}(this,this.document);
View
22 src/application/templates/404.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>not found</title>
+
+<style>
+body { text-align: center;}
+h1 { font-size: 50px; }
+body { font: 20px Constantia, 'Hoefler Text', "Adobe Caslon Pro", Baskerville, Georgia, Times, serif; color: #999; text-shadow: 2px 2px 2px rgba(200, 200, 200, 0.5); }
+::-moz-selection{ background:#FF5E99; color:#fff; }
+::selection { background:#FF5E99; color:#fff; }
+details { display:block; }
+a { color: rgb(36, 109, 56); text-decoration:none; }
+a:hover { color: rgb(96, 73, 141) ; text-shadow: 2px 2px 2px rgba(36, 109, 56, 0.5); }
+span[frown] { transform: rotate(90deg); display:inline-block; color: #bbb; }
+</style>
+
+
+
+
+<details>
+ <summary><h1>Not found</h1></summary>
+ <p><span frown>:(</span></p>
+</details>
View
41 src/application/templates/base.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en" class="no-js">
+<head>
+ <meta charset="utf-8" />
+ <title>{% block title %}Examples{% endblock %}</title>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;" />
+ <link rel="shortcut icon" href="/favicon.ico" />
+ <link rel="stylesheet" href="/static/css/style.css?v=1" />
+ <link rel="stylesheet" href="/static/css/main.css?v=1" />
+ <script src="/static/js/modernizr-1.5.min.js"></script>
+</head>
+<!--[if lt IE 7 ]><body class="ie6"><![endif]-->
+<!--[if IE 7 ]><body class="ie7"><![endif]-->
+<!--[if IE 8 ]><body class="ie8"><![endif]-->
+<!--[if IE 9 ]><body class="ie9"><![endif]-->
+<!--[if (gt IE 9)|!(IE)]><!-->
+<body id="">
+<!--<![endif]-->
+ <header>
+ <div id="hd">
+ {% block header %}
+ <!-- header -->
+ {% endblock %}
+ </div>
+ </header>
+
+ <div id="bd">
+ {% block content %}
+ <!-- main content area -->
+ {% endblock %}
+ </div>
+
+ <footer>
+ <div id="ft">
+ {% block footer %}
+ <!-- footer -->
+ {% endblock %}
+ </div>
+ </footer>
+</body>
+</html>
View
26 src/application/templates/list_examples.html
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+
+{% block content %}
+<p>
+ <a href="{{ url_for('list_examples') }}">List examples</a> | <a href="{{ url_for('new_example') }}">New example</a>
+</p>
+{% with messages = get_flashed_messages() %}
+ {% if messages %}
+ <div class="flash">
+ {% for message in messages %}
+ <p>{{ message }}</p>
+ {% endfor %}
+ </div>
+ {% endif %}
+{% endwith %}
+
+<ul>
+ <h1 id="">All Examples</h1>
+ {% for example in examples %}
+ <li>
+ ID: {{ example.example_id }}<br />
+ Title: {{ example.example_title }}<br />
+ </li>
+ {% endfor %}
+</ul>
+{% endblock %}
View
31 src/application/templates/new_example.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+
+{% block content %}
+ <h1 id="">Add a New Example</h1>
+ <form action="{{ url_for('new_example') }}" method="post" accept-charset="utf-8">
+ {{ form.csrf_token }}
+ <p>
+ <label for="example_id">{{ form.example_id.label }}</label><br />
+ {{ form.example_id|safe }}<br />
+ {% if form.example_id.errors %}
+ <ul class="errors">
+ {% for error in form.example_id.errors %}
+ <li>{{ error }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </p>
+ <p>
+ <label for="example_title">{{ form.example_title.label }}</label><br />
+ {{ form.example_title|safe }}<br />
+ {% if form.example_title.errors %}
+ <ul class="errors">
+ {% for error in form.example_title.errors %}
+ <li>{{ error }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </p>
+ <p><input type="submit" value="Add Example"/></p>
+ </form>
+{% endblock %}
View
42 src/application/views.py
@@ -0,0 +1,42 @@
+
+from google.appengine.api import users
+
+from flask import render_template, flash, url_for, redirect
+
+from application import app
+from models import ExampleModel
+from decorators import login_required
+from forms import ExampleForm
+
+
+@app.route('/')
+def redirect_to_home():
+ return redirect(url_for('list_examples'))
+
+
+@app.route('/hello/<username>')
+def say_hello(username):
+ '''Contrived example to demonstrate Flask's url routing capabilities'''
+ return 'Hello %s' % username
+
+
+@app.route('/examples')
+def list_examples():
+ examples = ExampleModel.all()
+ return render_template('list_examples.html', examples=examples)
+
+
+@app.route('/example/new', methods = ['GET', 'POST'])
+@login_required
+def new_example():
+ form = ExampleForm()
+ if form.validate_on_submit():
+ example = ExampleModel(
+ example_id = form.example_id.data,
+ example_title = form.example_title.data,
+ added_by = users.get_current_user()
+ )
+ example.put()
+ flash('Example successfully saved.')
+ return redirect(url_for('list_examples'))
+ return render_template('new_example.html', form=form)
View
34 src/flask/__init__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug import abort, redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, jsonify, json_available, flash, \
+ send_file, send_from_directory, get_flashed_messages, \
+ get_template_attribute, make_response
+from .globals import current_app, g, request, session, _request_ctx_stack
+from .module import Module
+from .templating import render_template, render_template_string
+from .session import Session
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception
+
+# only import json if it's available
+if json_available:
+ from .helpers import json
View
874 src/flask/app.py
@@ -0,0 +1,874 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+from threading import Lock
+from datetime import timedelta, datetime
+from itertools import chain
+
+from jinja2 import Environment
+
+from werkzeug import ImmutableDict
+from werkzeug.routing import Map, Rule
+from werkzeug.exceptions import HTTPException, InternalServerError
+
+from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
+ _tojson_filter, _endpoint_from_view_func
+from .wrappers import Request, Response
+from .config import ConfigAttribute, Config
+from .ctx import _RequestContext
+from .globals import _request_ctx_stack, request
+from .session import Session, _NullSession
+from .module import _ModuleSetupState
+from .templating import _DispatchingJinjaLoader, \
+ _default_template_ctx_processor
+from .signals import request_started, request_finished, got_request_exception
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+
+class Flask(_PackageBoundObject):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an `__init__.py` file inside) or a standard module (just a `.py` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the `__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea what
+ belongs to your application. This name is used to find resources
+ on the file system, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in `yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplicaiton.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.5
+ The `static_path` parameter was added.
+
+ :param import_name: the name of the application package
+ :param static_path: can be used to specify a different path for the
+ static files on the web. Defaults to ``/static``.
+ This does not affect the folder the files are served
+ *from*.
+ """
+
+ #: The class that is used for request objects. See :class:`~flask.Request`
+ #: for more information.
+ request_class = Request
+
+ #: The class that is used for response objects. See
+ #: :class:`~flask.Response` for more information.
+ response_class = Response
+
+ #: Path for the static files. If you don't want to use static files
+ #: you can set this value to `None` in which case no URL rule is added
+ #: and the development server will no longer serve any static files.
+ #:
+ #: This is the default used for application and modules unless a
+ #: different value is passed to the constructor.
+ static_path = '/static'
+
+ #: The debug flag. Set this to `True` to enable debugging of the
+ #: application. In debug mode the debugger will kick in when an unhandled
+ #: exception ocurrs and the integrated server will automatically reload
+ #: the application if changes in the code are detected.
+ #:
+ #: This attribute can also be configured from the config with the `DEBUG`
+ #: configuration key. Defaults to `False`.
+ debug = ConfigAttribute('DEBUG')
+
+ #: The testing flask. Set this to `True` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate unittest helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `TESTING` configuration key. Defaults to `False`.
+ testing = ConfigAttribute('TESTING')
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SECRET_KEY` configuration key. Defaults to `None`.
+ secret_key = ConfigAttribute('SECRET_KEY')
+
+ #: The secure cookie uses this for the name of the session cookie.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
+ session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+ #: A :class:`~datetime.timedelta` which is used to set the expiration
+ #: date of a permanent session. The default is 31 days which makes a
+ #: permanent session survive for roughly one month.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
+
+ #: Enable this if you want to use the X-Sendfile feature. Keep in
+ #: mind that the server has to support this. This only affects files
+ #: sent with the :func:`send_file` method.
+ #:
+ #: .. versionadded:: 0.2
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
+ use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+ #: The name of the logger to use. By default the logger name is the
+ #: package name passed to the constructor.
+ #:
+ #: .. versionadded:: 0.4
+ logger_name = ConfigAttribute('LOGGER_NAME')
+
+ #: The logging format used for the debug logger. This is only used when
+ #: the application is in debug mode, otherwise the attached logging
+ #: handler does the formatting.
+ #:
+ #: .. versionadded:: 0.3
+ debug_log_format = (
+ '-' * 80 + '\n' +
+ '%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
+ '%(message)s\n' +
+ '-' * 80
+ )
+
+ #: Options that are passed directly to the Jinja2 environment.
+ jinja_options = ImmutableDict(
+ extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+ )
+
+ #: Default configuration parameters.
+ default_config = ImmutableDict({
+ 'DEBUG': False,
+ 'TESTING': False,
+ 'SECRET_KEY': None,
+ 'SESSION_COOKIE_NAME': 'session',
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'LOGGER_NAME': None,
+ 'SERVER_NAME': None,
+ 'MAX_CONTENT_LENGTH': None
+ })
+
+ def __init__(self, import_name, static_path=None):
+ _PackageBoundObject.__init__(self, import_name)
+ if static_path is not None:
+ self.static_path = static_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = Config(self.root_path, self.default_config)
+
+ #: Prepare the deferred setup of the logger.
+ self._logger = None
+ self.logger_name = self.import_name
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: to register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ #: A dictionary of all registered error handlers. The key is
+ #: be the error code as integer, the value the function that
+ #: should handle that error.
+ #: To register a error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handlers = {}
+
+ #: A dictionary with lists of functions that should be called at the
+ #: beginning of the request. The key of the dictionary is the name of
+ #: the module this function is active for, `None` for all requests.
+ #: This can for example be used to open database connections or
+ #: getting hold of the currently logged in user. To register a
+ #: function here, use the :meth:`before_request` decorator.
+ self.before_request_funcs = {}
+
+ #: A dictionary with lists of functions that should be called after
+ #: each request. The key of the dictionary is the name of the module
+ #: this function is active for, `None` for all requests. This can for
+ #: example be used to open database connections or getting hold of the
+ #: currently logged in user. To register a function here, use the
+ #: :meth:`before_request` decorator.
+ self.after_request_funcs = {}
+
+ #: A dictionary with list of functions that are called without argument
+ #: to populate the template context. They key of the dictionary is the
+ #: name of the module this function is active for, `None` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: all the loaded modules in a dictionary by name.
+ #:
+ #: .. versionadded:: 0.5
+ self.modules = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(BaseConverter.to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = Map()
+
+ # register the static folder for the application. Do that even
+ # if the folder does not exist. First of all it might be created
+ # while the server is running (usually happens during development)
+ # but also because google appengine stores static files somewhere
+ # else when mapped with the .yml file.
+ self.add_url_rule(self.static_path + '/<path:filename>',
+ endpoint='static',
+ view_func=self.send_static_file)
+
+ #: The Jinja2 environment. It is created from the
+ #: :attr:`jinja_options`.
+ self.jinja_env = self.create_jinja_environment()
+ self.init_jinja_globals()
+
+ @property
+ def logger(self):
+ """A :class:`logging.Logger` object for this application. The
+ default configuration is to log to stderr if the application is
+ in debug mode. This logger can be used to (surprise) log messages.
+ Here some examples::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning ocurred (%d apples)', 42)
+ app.logger.error('An error occoured')
+
+ .. versionadded:: 0.3
+ """
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ with _logger_lock:
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ from flask.logging import create_logger
+ self._logger = rv = create_logger(self)
+ return rv
+
+ def create_jinja_environment(self):
+ """Creates the Jinja2 environment based on :attr:`jinja_options`
+ and :meth:`select_jinja_autoescape`.
+
+ .. versionadded:: 0.5
+ """
+ options = dict(self.jinja_options)
+ if 'autoescape' not in options:
+ options['autoescape'] = self.select_jinja_autoescape
+ return Environment(loader=_DispatchingJinjaLoader(self), **options)
+
+ def init_jinja_globals(self):
+ """Called directly after the environment was created to inject
+ some defaults (like `url_for`, `get_flashed_messages` and the
+ `tojson` filter.
+
+ .. versionadded:: 0.5
+ """
+ self.jinja_env.globals.update(
+ url_for=url_for,
+ get_flashed_messages=get_flashed_messages
+ )
+ self.jinja_env.filters['tojson'] = _tojson_filter
+
+ def select_jinja_autoescape(self, filename):
+ """Returns `True` if autoescaping should be active for the given
+ template name.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return False
+ return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
+
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session, config and g into the template
+ context as well as everything template context processors want
+ to inject. Note that the as of Flask 0.6, the original values
+ in the context will not be overriden if a context processor
+ decides to return a value with the same key.
+
+ :param context: the context as a dictionary that is updated in place
+ to add extra variables.
+ """
+ funcs = self.template_context_processors[None]
+ mod = _request_ctx_stack.top.request.module
+ if mod is not None and mod in self.template_context_processors:
+ funcs = chain(funcs, self.template_context_processors[mod])
+ orig_ctx = context.copy()
+ for func in funcs:
+ context.update(func())
+ # make sure the original values win. This makes it possible to
+ # easier add new variables in context processors without breaking
+ # existing views.
+ context.update(orig_ctx)
+
+ def run(self, host='127.0.0.1', port=5000, **options):
+ """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.
+
+ If you want to run the application in debug mode, but disable the
+ code execution on the interactive debugger, you can pass
+ ``use_evalex=False`` as parameter. This will keep the debugger's
+ traceback screen active, but disable code execution.
+
+ .. admonition:: Keep in Mind
+
+ Flask will suppress any server error with a generic error page
+ unless it is in debug mode. As such to enable just the
+ interactive debugger without the code reloading, you have to
+ invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
+ Setting ``use_debugger`` to `True` without being in debug mode
+ won't catch any exceptions because there won't be any to
+ catch.
+
+ :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:
+ self.debug = options.pop('debug')
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ return run_simple(host, port, self, **options)
+
+ def test_client(self):
+ """Creates a test client for this application. For information
+ about unit testing head over to :ref:`testing`.
+
+ The test client can be used in a `with` block to defer the closing down
+ of the context until the end of the `with` block. This is useful if
+ you want to access the context locals for testing::
+
+ with app.test_client() as c:
+ rv = c.get('/?vodka=42')
+ assert request.args['vodka'] == '42'
+
+ .. versionchanged:: 0.4
+ added support for `with` block usage for the client.
+ """
+ from flask.testing import FlaskClient
+ return FlaskClient(self, self.response_class, use_cookies=True)
+
+ def open_session(self, request):
+ """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:
+ return Session.load_cookie(request, self.session_cookie_name,
+ secret_key=key)
+
+ 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 response: an instance of :attr:`response_class`
+ """
+ expires = domain = None
+ if session.permanent:
+ expires = datetime.utcnow() + self.permanent_session_lifetime
+ if self.config['SERVER_NAME'] is not None:
+ domain = '.' + self.config['SERVER_NAME']
+ session.save_cookie(response, self.session_cookie_name,
+ expires=expires, httponly=True, domain=domain)
+
+ def register_module(self, module, **options):
+ """Registers a module with this application. The keyword argument
+ of this function are the same as the ones for the constructor of the
+ :class:`Module` class and will override the values of the module if
+ provided.
+ """
+ options.setdefault('url_prefix', module.url_prefix)
+ options.setdefault('subdomain', module.subdomain)
+ state = _ModuleSetupState(self, **options)
+ for func in module._register_events:
+ func(state)
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Connects a URL rule. Works exactly like the :meth:`route`
+ decorator. If a view_func is provided it will be registered with the
+ endpoint.
+
+ Basically this example::
+
+ @app.route('/')
+ def index():
+ pass
+
+ Is equivalent to the following::
+
+ def index():
+ pass
+ app.add_url_rule('/', 'index', index)
+
+ If the view_func is not provided you will need to connect the endpoint
+ to a view function like so::
+
+ app.view_functions['index'] = index
+
+ .. versionchanged:: 0.2
+ `view_func` parameter added.
+
+ .. versionchanged:: 0.6
+ `OPTIONS` is added automatically as method.
+
+ :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 view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ """
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ options['endpoint'] = endpoint
+ methods = options.pop('methods', ('GET',))
+ provide_automatic_options = False
+ if 'OPTIONS' not in methods:
+ methods = tuple(methods) + ('OPTIONS',)
+ provide_automatic_options = True
+ rule = Rule(rule, methods=methods, **options)
+ rule.provide_automatic_options = provide_automatic_options
+ self.url_map.add(rule)
+ if view_func is not None:
+ self.view_functions[endpoint] = view_func
+
+ def route(self, rule, **options):
+ """A decorator that is used to register a view function for a
+ given URL rule. Example::
+
+ @app.route('/')
+ def index():
+ return 'Hello World'
+
+ Variables parts in the route can be specified with angular
+ brackets (``/user/<username>``). By default a variable part
+ in the URL accepts any string without a slash however a different
+ converter can be specified as well by using ``<converter:name>``.
+
+ Variable parts are passed to the view function as keyword
+ arguments.
+
+ The following converters are possible:
+
+ =========== ===========================================
+ `int` accepts integers
+ `float` like `int` but for floating point values
+ `path` like the default but also accepts slashes
+ =========== ===========================================
+
+ Here some examples::
+
+ @app.route('/')
+ def index():
+ pass
+
+ @app.route('/<username>')
+ def show_user(username):
+ pass
+
+ @app.route('/post/<int:post_id>')
+ def show_post(post_id):
+ pass
+
+ An important detail to keep in mind is how Flask deals with trailing
+ slashes. The idea is to keep each URL unique so the following rules
+ apply:
+
+ 1. If a rule ends with a slash and is requested without a slash
+ by the user, the user is automatically redirected to the same
+ page with a trailing slash attached.
+ 2. If a rule does not end with a trailing slash and the user request
+ the page with a trailing slash, a 404 not found is raised.
+
+ This is consistent with how web servers deal with static files. This
+ also makes it possible to use relative link targets safely.
+
+ 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`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ :param subdomain: specifies the rule for the subdomain in case
+ 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, None, f, **options)
+ return f
+ return decorator
+
+ def errorhandler(self, code):
+ """A decorator that is used to register a function give a given
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register a function as error handler without using
+ the :meth:`errorhandler` decorator. The following example is
+ equivalent to the one above::
+
+ def page_not_found(error):
+ 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
+ return f
+ return decorator
+
+ def template_filter(self, name=None):
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.jinja_env.filters[name or f.__name__] = f
+ return f
+ return decorator
+
+ def before_request(self, f):
+ """Registers a function to run before each request."""
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def after_request(self, f):
+ """Register a function to be run after each request."""
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def context_processor(self, f):
+ """Registers a template context processor function."""
+ self.template_context_processors[None].append(f)
+ return f
+
+ def handle_http_exception(self, e):
+ """Handles an HTTP exception. By default this will invoke the
+ registered error handlers and fall back to returning the
+ exception as response.
+
+ .. versionadded: 0.3
+ """
+ handler = self.error_handlers.get(e.code)
+ if handler is None:
+ return e
+ return handler(e)
+
+ def handle_exception(self, e):
+ """Default exception handling that kicks in when an exception
+ occours that is not catched. In debug mode the exception will
+ be re-raised immediately, otherwise it is logged and the handler
+ for a 500 internal server error is used. If no such handler
+ exists, a default 500 internal server error message is displayed.
+
+ .. versionadded: 0.3
+ """
+ got_request_exception.send(self, exception=e)
+ handler = self.error_handlers.get(500)
+ if self.debug:
+ raise
+ self.logger.exception('Exception on %s [%s]' % (
+ request.path,
+ request.method
+ ))
+ if handler is None:
+ return InternalServerError()
+ return handler(e)
+
+ def dispatch_request(self):
+ """Does the request dispatching. Matches the URL and returns the
+ return value of the view or error handler. This does not have to
+ be a response object. In order to convert the return value to a
+ proper response object, call :func:`make_response`.
+ """
+ req = _request_ctx_stack.top.request
+ try:
+ if req.routing_exception is not None:
+ raise req.routing_exception
+ rule = req.url_rule
+ # if we provide automatic options for this URL and the
+ # request came with the OPTIONS method, reply automatically
+ if rule.provide_automatic_options and req.method == 'OPTIONS':
+ rv = self.response_class()
+ rv.allow.update(rule.methods)
+ return rv
+ # otherwise dispatch to the handler for that endpoint
+ return self.view_functions[rule.endpoint](**req.view_args)
+ except HTTPException, e:
+ return self.handle_http_exception(e)
+
+ 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 allowed for `rv`:
+
+ .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+ ======================= ===========================================
+ :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 rv is None:
+ raise ValueError('View function did not return a response')
+ if isinstance(rv, self.response_class):
+ return rv
+ if isinstance(rv, basestring):
+ return self.response_class(rv)
+ if isinstance(rv, tuple):
+ return self.response_class(*rv)
+ return self.response_class.force_type(rv, request.environ)
+
+ def create_url_adapter(self, request):
+ """Creates a URL adapter for the given request. The URL adapter
+ is created at a point where the request context is not yet set up
+ so the request is passed explicitly.
+
+ .. versionadded:: 0.6
+ """
+ return self.url_map.bind_to_environ(request.environ,
+ server_name=self.config['SERVER_NAME'])
+
+ def preprocess_request(self):
+ """Called before the actual request dispatching and will
+ call every as :meth:`before_request` decorated function.
+ If any of these function returns a value it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+ """
+ funcs = self.before_request_funcs.get(None, ())
+ mod = request.module
+ if mod and mod in self.before_request_funcs:
+ funcs = chain(funcs, self.before_request_funcs[mod])
+ for func in funcs:
+ rv = func()
+ if rv is not None:
+ return rv
+
+ def process_response(self, response):
+ """Can be overridden in order to modify the response object
+ before it's sent to the WSGI server. By default this will
+ call all the :meth:`after_request` decorated functions.
+
+ .. versionchanged:: 0.5
+ As of Flask 0.5 the functions registered for after request
+ execution are called in reverse order of registration.
+
+ :param response: a :attr:`response_class` object.
+ :return: a new response object or the same, has to be an
+ instance of :attr:`response_class`.
+ """
+ ctx = _request_ctx_stack.top
+ mod = ctx.request.module
+ if not isinstance(ctx.session, _NullSession):
+ self.save_session(ctx.session, response)
+ funcs = ()
+ if mod and mod in self.after_request_funcs:
+ funcs = reversed(self.after_request_funcs[mod])
+ if None in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[None]))
+ for handler in funcs:
+ response = handler(response)
+ return response
+
+ def request_context(self, environ):
+ """Creates a request context from the given environment and binds
+ it to the current context. This must be used in combination with
+ the `with` statement because the request is only bound to the
+ current context for the duration of the `with` block.
+
+ Example usage::
+
+ with app.request_context(environ):
+ do_something_with(request)
+
+ The object returned can also be used without the `with` statement
+ which is useful for working in the shell. The example above is
+ doing exactly the same as this code::
+
+ ctx = app.request_context(environ)
+ ctx.push()
+ try:
+ do_something_with(request)
+ finally:
+ ctx.pop()
+
+ The big advantage of this approach is that you can use it without
+ the try/finally statement in a shell for interactive testing:
+
+ >>> ctx = app.test_request_context()
+ >>> ctx.bind()
+ >>> request.path
+ u'/'
+ >>> ctx.unbind()
+
+ .. versionchanged:: 0.3
+ Added support for non-with statement usage and `with` statement
+ is now passed the ctx object.
+
+ :param environ: a WSGI environment
+ """
+ return _RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :func:`werkzeug.create_environ` for more information, this
+ function accepts the same arguments).
+ """
+ from werkzeug import create_environ
+ return self.request_context(create_environ(*args, **kwargs))
+
+ def wsgi_app(self, environ, start_response):
+ """The actual WSGI application. This is not implemented in
+ `__call__` so that middlewares can be applied without losing a
+ reference to the class. So instead of doing this::
+
+ app = MyMiddleware(app)
+
+ It's a better idea to do this instead::
+
+ app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ .. versionchanged:: 0.4
+ The :meth:`after_request` functions are now called even if an
+ error handler took over request processing. This ensures that
+ even if an exception happens database have the chance to
+ properly close the connection.
+
+ :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):
+ try:
+ request_started.send(self)
+ rv = self.preprocess_request()
+ if rv is None:
+ rv = self.dispatch_request()
+ response = self.make_response(rv)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ try:
+ response = self.process_response(response)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ request_finished.send(self, response=response)
+ return response(environ, start_response)
+
+ def __call__(self, environ, start_response):
+ """Shortcut for :attr:`wsgi_app`."""
+ return self.wsgi_app(environ, start_response)
View
152 src/flask/config.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+
+from werkzeug import import_string
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name):
+ self.__name__ = name
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return obj.config[self.__name__]
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This basically is just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to `True` if you want silent failing for missing
+ files.
+ :return: bool. `True` if able to load config, `False` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ self.from_pyfile(rv)
+ return True
+
+ def from_pyfile(self, filename):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = type(sys)('config')
+ d.__file__ = filename
+ execfile(filename, d.__dict__)
+ self.from_object(d)
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes.
+
+ Just the uppercase variables in that object are stored in the config
+ after lowercasing. Example usage::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, basestring):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
View
66 src/flask/ctx.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack
+from .session import _NullSession
+
+
+class _RequestGlobals(object):
+ pass
+
+
+class _RequestContext(object):
+ """The request context contains all request relevant information. It is
+ created at the beginning of the request and pushed to the
+ `_request_ctx_stack` and removed at the end of it. It will create the
+ URL adapter and request object for the WSGI environment provided.
+ """
+
+ def __init__(self, app, environ):
+ self.app = app
+ self.request = app.request_class(environ)
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.session = app.open_session(self.request)
+ if self.session is None:
+ self.session = _NullSession()
+ self.g = _RequestGlobals()
+ self.flashes = None
+
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException, e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context."""
+ _request_ctx_stack.push(self)
+
+ def pop(self):
+ """Pops the request context."""
+ _request_ctx_stack.pop()
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ # do not pop the request stack if we are in debug mode and an
+ # exception happened. This will allow the debugger to still
+ # access the request object in the interactive shell. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ if not self.request.environ.get('flask._preserve_context') and \
+ (tb is None or not self.app.debug):
+ self.pop()
View
20 src/flask/globals.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import LocalStack, LocalProxy
+
+# context locals
+_request_ctx_stack = LocalStack()
+current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
+request = LocalProxy(lambda: _request_ctx_stack.top.request)
+session = LocalProxy(lambda: _request_ctx_stack.top.session)
+g = LocalProxy(lambda: _request_ctx_stack.top.g)
View
463 src/flask/helpers.py
@@ -0,0 +1,463 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+
+# try to load the best simplejson implementation available. If JSON
+# is not installed, we add a failing class.
+json_available = True
+json = None
+try:
+ import simplejson as json
+except ImportError:
+ try:
+ import json
+ except ImportError:
+ try:
+ # Google Appengine offers simplejson via django
+ from django.utils import simplejson as json
+ except ImportError:
+ json_available = False
+
+
+from werkzeug import Headers, wrap_file, is_resource_modified, cached_property
+from werkzeug.exceptions import NotFound
+
+from jinja2 import FileSystemLoader
+
+from .globals import session, _request_ctx_stack, current_app, request
+
+
+def _assert_have_json():
+ """Helper function that fails if JSON is unavailable."""
+ if not json_available:
+ raise RuntimeError('simplejson not installed')
+
+# figure out if simplejson escapes slashes. This behaviour was changed
+# from one version to another without reason.
+if not json_available or '\\/' not in json.dumps('/'):
+
+ def _tojson_filter(*args, **kwargs):
+ if __debug__:
+ _assert_have_json()
+ return json.dumps(*args, **kwargs).replace('/', '\\/')
+else:
+ _tojson_filter = json.dumps
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def jsonify(*args, **kwargs):
+ """Creates a :class:`~flask.Response` with the JSON representation of
+ the given arguments with an `application/json` mimetype. The arguments
+ to this function are the same as to the :class:`dict` constructor.
+
+ Example usage::
+
+ @app.route('/_get_current_user')
+ def get_current_user():
+ return jsonify(username=g.user.username,
+ email=g.user.email,
+ id=g.user.id)
+
+ This will send a JSON response like this to the browser::
+
+ {
+ "username": "admin",
+ "email": "admin@localhost",
+ "id": 42
+ }
+
+ This requires Python 2.6 or an installed version of simplejson. For
+ security reasons only objects are supported toplevel. For more
+ information about this, have a look at :ref:`json-security`.
+
+ .. versionadded:: 0.2
+ """
+ if __debug__:
+ _assert_have_json()
+ return current_app.response_class(json.dumps(dict(*args, **kwargs),
+ indent=None if request.is_xhr else 2), mimetype='application/json')
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+ The endpoint is relative to the active module if modules are in use.
+
+ Here some examples:
+
+ ==================== ======================= =============================
+ Active Module Target Endpoint Target Function
+ ==================== ======================= =============================
+ `None` ``'index'`` `index` of the application
+ `None` ``'.index'`` `index` of the application
+ ``'admin'`` ``'index'`` `index` of the `admin` module
+ any ``'.index'`` `index` of the application
+ any ``'admin.index'`` `index` of the `admin` module
+ ==================== ======================= =============================
+
+ Variable arguments that are unknown to the target endpoint are appended
+ to the generated URL as query arguments.
+
+ For more information, head over to the :ref:`Quickstart <url-building>`.
+
+ :param endpoint: the endpoint of the URL (name of the function)
+ :param values: the variable arguments of the URL rule
+ :param _external: if set to `True`, an absolute URL is generated.
+ """
+ ctx = _request_ctx_stack.top
+ if '.' not in endpoint:
+ mod = ctx.request.module
+ if mod is not None:
+ endpoint = mod + '.' + endpoint
+ elif endpoint.startswith('.'):
+ endpoint = endpoint[1:]
+ external = values.pop('_external', False)
+ return ctx.url_adapter.build(endpoint, values, force_external=external)
+
+
+def get_template_attribute(template_name, attribute):
+ """Loads a macro (or variable) a template exports. This can be used to
+ invoke a macro from within Python code. If you for example have a
+ template named `_cider.html` with the following contents:
+
+ .. sourcecode:: html+jinja
+
+ {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+ You can access this from Python code like this::
+
+ hello = get_template_attribute('_cider.html', 'hello')
+ return hello('World')
+
+ .. versionadded:: 0.2
+
+ :param template_name: the name of the template
+ :param attribute: the name of the variable of macro to acccess
+ """
+ return getattr(current_app.jinja_env.get_template(template_name).module,
+ attribute)
+
+
+def flash(message, category='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`.
+
+ .. versionchanged: 0.3
+ `category` parameter added.
+
+ :param message: the message to be flashed.
+ :param category: the category for the message. The following values
+ are recommended: ``'message'`` for any kind of message,
+ ``'error'`` for errors, ``'info'`` for information
+ messages and ``'warning'`` for warnings. However any
+ kind of string can be used as category.
+ """
+ session.setdefault('_flashes', []).append((category, message))
+
+
+def get_flashed_messages(with_categories=False):
+ """Pulls all flashed messages from the session and returns them.
+ Further calls in the same request to the function will return
+ the same messages. By default just the messages are returned,
+ but when `with_categories` is set to `True`, the return value will
+ be a list of tuples in the form ``(category, message)`` instead.
+
+ Example usage:
+
+ .. sourcecode:: html+jinja
+
+ {% for category, msg in get_flashed_messages(with_categories=true) %}
+ <p class=flash-{{ category }}>{{ msg }}
+ {% endfor %}
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ :param with_categories: set to `True` to also receive categories.
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=60 * 60 * 12, conditional=False):
+ """Sends the contents of a file to the client. This will use the
+ most efficient method available and configured. By default it will
+ try to use the WSGI server's file_wrapper support. Alternatively
+ you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+ to ``True`` to directly emit an `X-Sendfile` header. This however
+ requires support of the underlying webserver for `X-Sendfile`.
+
+ By default it will try to guess the mimetype for you, but you can
+ also explicitly provide one. For extra security you probably want
+ to sent certain files as attachment (HTML for instance).
+
+ Please never pass filenames to this function from user sources without
+ checking them first. Something like this is usually sufficient to
+ avoid security problems::
+
+ if '..' in filename or filename.startswith('/'):
+ abort(404)
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behaviour is now to attach etags.
+
+ :param filename_or_fp: the filename of the file to send. This is
+ relative to the :attr:`~Flask.root_path` if a
+ relative path is specified.
+ Alternatively a file object might be provided
+ in which case `X-Sendfile` might not work and
+ fall back to the traditional method.
+ :param mimetype: the mimetype of the file if provided, otherwise
+ auto detection happens.
+ :param as_attachment: set to `True` if you want to send this file with
+ a ``Content-Disposition: attachment`` header.
+ :param attachment_filename: the filename for the attachment if it
+ differs from the file's filename.