First Commit
This commit is contained in:
commit
25ba685516
567 changed files with 73834 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.idea/
|
23
GithubAPI/GithubAPI.py
Normal file
23
GithubAPI/GithubAPI.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
__author__ = 'sagi'
|
||||
from google.appengine.ext import db
|
||||
|
||||
class GitHubAPI_Keys():
|
||||
|
||||
def __init__(self):
|
||||
self.__clientID = None
|
||||
self.__clientSecret = None
|
||||
q = KEYS.all()
|
||||
for k in q.run(limit=5):
|
||||
self.__clientID = k.client_id
|
||||
self.__clientSecret = k.client_secret
|
||||
break
|
||||
|
||||
def getId(self):
|
||||
return self.__clientID
|
||||
|
||||
def getSecret(self):
|
||||
return self.__clientSecret
|
||||
|
||||
class KEYS(db.Model):
|
||||
client_id = db.StringProperty()
|
||||
client_secret = db.StringProperty()
|
BIN
GithubAPI/GithubAPI.pyc
Normal file
BIN
GithubAPI/GithubAPI.pyc
Normal file
Binary file not shown.
1
GithubAPI/__init__.py
Normal file
1
GithubAPI/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__author__ = 'sagi'
|
BIN
GithubAPI/__init__.pyc
Normal file
BIN
GithubAPI/__init__.pyc
Normal file
Binary file not shown.
69
README.md
Normal file
69
README.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
![SE-Hub][Logo]
|
||||
----
|
||||
|
||||
[![Gitter][gitterBadge]][gitterChatRoomLink]
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
- [Introduction](#introduction)
|
||||
- [Website](#website)
|
||||
- [Documentation](#documentation)
|
||||
- [License](#license)
|
||||
- [The MIT License](#the-mit-license-mit)
|
||||
|
||||
|
||||
# Introduction
|
||||
|
||||
Our vision in creating SE Hub wasn't to re-invent the wheel, but rather unify all the resources needed to solve these problems under one roof - SE Hub. Bringing to the table features like:
|
||||
- Lecturer exercise tracking of individual tasks.
|
||||
- Personal team messages.
|
||||
- Communications made easy and accessible for both lecturers and students.
|
||||
- Lecturer and teacher grading, follow up scores and etc.
|
||||
- Peer assessment and activity on git-hub.
|
||||
- Scheduling and event logs.
|
||||
|
||||
# Website
|
||||
|
||||
- [SE Hub](http://se-hub.appspot.com/) on GAE
|
||||
|
||||
# Documentation
|
||||
|
||||
[PowerPoint Presentation (Hebrew)](https://drive.google.com/file/d/0BwBOu8RSEI0fLVE4UFRTWERqVjQ/view?usp=sharing) |
|
||||
[SOW Document (Hebrew)](https://drive.google.com/file/d/0BwBOu8RSEI0fTkxtZE43b3E1SEk/view?usp=sharing)
|
||||
|
||||
|
||||
the first thing you should do for you to understand how our platform works is to visit our [wiki][wikiLink].
|
||||
there you will find the user manual (both for Students and Lecturers). and for the Developers how wants ti chip in and code, all the technical details.
|
||||
|
||||
# License
|
||||
|
||||
SE-Hub project is part of Software Engineering course at JCE (2015)
|
||||
The Project is under the [MIT](http://opensource.org/licenses/MIT) License.
|
||||
|
||||
## The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 SE-Hub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
[wikiLink]: https://github.com/sagidayan/SE-Hub/wiki
|
||||
[Logo]: https://cloud.githubusercontent.com/assets/2984053/6825467/7c9d0402-d303-11e4-9827-62a6d66f937a.png
|
||||
[gitterBadge]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitterChatRoomLink]: https://gitter.im/sagidayan/SE-Hub
|
4
User/User.py
Normal file
4
User/User.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from google.appengine.ext import db
|
||||
|
||||
|
||||
|
1
User/__init__.py
Normal file
1
User/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__author__ = 'sagi'
|
BIN
User/__init__.pyc
Normal file
BIN
User/__init__.pyc
Normal file
Binary file not shown.
38
app.yaml
Normal file
38
app.yaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
# This file specifies your Python application's runtime configuration
|
||||
# including URL routing, versions, static file uploads, etc. See
|
||||
# https://developers.google.com/appengine/docs/python/config/appconfig
|
||||
# for details.
|
||||
|
||||
# TODO: Enter your application id below. If you have signed up
|
||||
# using cloud.google.com/console use the "project id" for your application
|
||||
# id.
|
||||
application: se-hub
|
||||
version: 1
|
||||
runtime: python27
|
||||
api_version: 1
|
||||
threadsafe: yes
|
||||
|
||||
# Handlers define how to route requests to your application.
|
||||
handlers:
|
||||
|
||||
# App Engine serves and caches static files contained in the listed directories
|
||||
# (and subdirectories). Uncomment and set the directory as needed.
|
||||
#- url: /client
|
||||
# static_dir: client
|
||||
|
||||
# This handler tells app engine how to route requests to a WSGI application.
|
||||
# The script value is in the format <path.to.module>.<wsgi_application>
|
||||
# where <wsgi_application> is a WSGI application object.
|
||||
- url: .* # This regex directs all routes to main.app
|
||||
script: main.app
|
||||
|
||||
# Third party libraries that are included in the App Engine SDK must be listed
|
||||
# here if you want to use them. See
|
||||
# https://developers.google.com/appengine/docs/python/tools/libraries27 for
|
||||
# a list of libraries included in the SDK. Third party libs that are *not* part
|
||||
# of the App Engine SDK don't need to be listed here, instead add them to your
|
||||
# project directory, either as a git submodule or as a plain subdirectory.
|
||||
# TODO: List any other App Engine SDK libs you may need here.
|
||||
libraries:
|
||||
- name: ssl
|
||||
version: latest
|
6
appengine_config.py
Normal file
6
appengine_config.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
"""`appengine_config` gets loaded when starting a new application instance."""
|
||||
import sys
|
||||
import os.path
|
||||
# add `lib` subdirectory to `sys.path`, so our `main` module can load
|
||||
# third-party libraries.
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib'))
|
BIN
appengine_config.pyc
Normal file
BIN
appengine_config.pyc
Normal file
Binary file not shown.
58
lib/Flask-0.10.1-py2.7.egg-info/PKG-INFO
Normal file
58
lib/Flask-0.10.1-py2.7.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,58 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: Flask
|
||||
Version: 0.10.1
|
||||
Summary: A microframework based on Werkzeug, Jinja2 and good intentions
|
||||
Home-page: http://github.com/mitsuhiko/flask/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description:
|
||||
Flask
|
||||
-----
|
||||
|
||||
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good
|
||||
intentions. And before you ask: It's BSD licensed!
|
||||
|
||||
Flask is Fun
|
||||
````````````
|
||||
|
||||
.. code:: python
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return "Hello World!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
And Easy to Setup
|
||||
`````````````````
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ pip install Flask
|
||||
$ python hello.py
|
||||
* Running on http://localhost:5000/
|
||||
|
||||
Links
|
||||
`````
|
||||
|
||||
* `website <http://flask.pocoo.org/>`_
|
||||
* `documentation <http://flask.pocoo.org/docs/>`_
|
||||
* `development version
|
||||
<http://github.com/mitsuhiko/flask/zipball/master#egg=Flask-dev>`_
|
||||
|
||||
|
||||
Platform: any
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
238
lib/Flask-0.10.1-py2.7.egg-info/SOURCES.txt
Normal file
238
lib/Flask-0.10.1-py2.7.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,238 @@
|
|||
AUTHORS
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
Makefile
|
||||
README
|
||||
run-tests.py
|
||||
setup.cfg
|
||||
setup.py
|
||||
Flask.egg-info/PKG-INFO
|
||||
Flask.egg-info/SOURCES.txt
|
||||
Flask.egg-info/dependency_links.txt
|
||||
Flask.egg-info/not-zip-safe
|
||||
Flask.egg-info/requires.txt
|
||||
Flask.egg-info/top_level.txt
|
||||
artwork/.DS_Store
|
||||
artwork/LICENSE
|
||||
artwork/logo-full.svg
|
||||
docs/.gitignore
|
||||
docs/Makefile
|
||||
docs/advanced_foreword.rst
|
||||
docs/api.rst
|
||||
docs/appcontext.rst
|
||||
docs/becomingbig.rst
|
||||
docs/blueprints.rst
|
||||
docs/changelog.rst
|
||||
docs/conf.py
|
||||
docs/config.rst
|
||||
docs/contents.rst.inc
|
||||
docs/design.rst
|
||||
docs/errorhandling.rst
|
||||
docs/extensiondev.rst
|
||||
docs/extensions.rst
|
||||
docs/flaskdocext.py
|
||||
docs/flaskext.py
|
||||
docs/flaskstyle.sty
|
||||
docs/foreword.rst
|
||||
docs/htmlfaq.rst
|
||||
docs/index.rst
|
||||
docs/installation.rst
|
||||
docs/latexindex.rst
|
||||
docs/license.rst
|
||||
docs/logo.pdf
|
||||
docs/make.bat
|
||||
docs/python3.rst
|
||||
docs/quickstart.rst
|
||||
docs/reqcontext.rst
|
||||
docs/security.rst
|
||||
docs/shell.rst
|
||||
docs/signals.rst
|
||||
docs/styleguide.rst
|
||||
docs/templating.rst
|
||||
docs/testing.rst
|
||||
docs/unicode.rst
|
||||
docs/upgrading.rst
|
||||
docs/views.rst
|
||||
docs/_static/debugger.png
|
||||
docs/_static/flask.png
|
||||
docs/_static/flaskr.png
|
||||
docs/_static/logo-full.png
|
||||
docs/_static/no.png
|
||||
docs/_static/touch-icon.png
|
||||
docs/_static/yes.png
|
||||
docs/_templates/sidebarintro.html
|
||||
docs/_templates/sidebarlogo.html
|
||||
docs/_themes/.git
|
||||
docs/_themes/.gitignore
|
||||
docs/_themes/LICENSE
|
||||
docs/_themes/README
|
||||
docs/_themes/flask_theme_support.py
|
||||
docs/_themes/flask/layout.html
|
||||
docs/_themes/flask/relations.html
|
||||
docs/_themes/flask/theme.conf
|
||||
docs/_themes/flask/static/flasky.css_t
|
||||
docs/_themes/flask/static/small_flask.css
|
||||
docs/_themes/flask_small/layout.html
|
||||
docs/_themes/flask_small/theme.conf
|
||||
docs/_themes/flask_small/static/flasky.css_t
|
||||
docs/deploying/cgi.rst
|
||||
docs/deploying/fastcgi.rst
|
||||
docs/deploying/index.rst
|
||||
docs/deploying/mod_wsgi.rst
|
||||
docs/deploying/uwsgi.rst
|
||||
docs/deploying/wsgi-standalone.rst
|
||||
docs/patterns/apierrors.rst
|
||||
docs/patterns/appdispatch.rst
|
||||
docs/patterns/appfactories.rst
|
||||
docs/patterns/caching.rst
|
||||
docs/patterns/celery.rst
|
||||
docs/patterns/deferredcallbacks.rst
|
||||
docs/patterns/distribute.rst
|
||||
docs/patterns/errorpages.rst
|
||||
docs/patterns/fabric.rst
|
||||
docs/patterns/favicon.rst
|
||||
docs/patterns/fileuploads.rst
|
||||
docs/patterns/flashing.rst
|
||||
docs/patterns/index.rst
|
||||
docs/patterns/jquery.rst
|
||||
docs/patterns/lazyloading.rst
|
||||
docs/patterns/methodoverrides.rst
|
||||
docs/patterns/mongokit.rst
|
||||
docs/patterns/packages.rst
|
||||
docs/patterns/requestchecksum.rst
|
||||
docs/patterns/sqlalchemy.rst
|
||||
docs/patterns/sqlite3.rst
|
||||
docs/patterns/streaming.rst
|
||||
docs/patterns/templateinheritance.rst
|
||||
docs/patterns/urlprocessors.rst
|
||||
docs/patterns/viewdecorators.rst
|
||||
docs/patterns/wtforms.rst
|
||||
docs/tutorial/css.rst
|
||||
docs/tutorial/dbcon.rst
|
||||
docs/tutorial/dbinit.rst
|
||||
docs/tutorial/folders.rst
|
||||
docs/tutorial/index.rst
|
||||
docs/tutorial/introduction.rst
|
||||
docs/tutorial/schema.rst
|
||||
docs/tutorial/setup.rst
|
||||
docs/tutorial/templates.rst
|
||||
docs/tutorial/testing.rst
|
||||
docs/tutorial/views.rst
|
||||
examples/.DS_Store
|
||||
examples/blueprintexample/blueprintexample.py
|
||||
examples/blueprintexample/blueprintexample_test.py
|
||||
examples/blueprintexample/simple_page/__init__.py
|
||||
examples/blueprintexample/simple_page/simple_page.py
|
||||
examples/blueprintexample/simple_page/templates/pages/hello.html
|
||||
examples/blueprintexample/simple_page/templates/pages/index.html
|
||||
examples/blueprintexample/simple_page/templates/pages/layout.html
|
||||
examples/blueprintexample/simple_page/templates/pages/world.html
|
||||
examples/flaskr/README
|
||||
examples/flaskr/flaskr.py
|
||||
examples/flaskr/flaskr_tests.py
|
||||
examples/flaskr/schema.sql
|
||||
examples/flaskr/static/style.css
|
||||
examples/flaskr/templates/layout.html
|
||||
examples/flaskr/templates/login.html
|
||||
examples/flaskr/templates/show_entries.html
|
||||
examples/jqueryexample/jqueryexample.py
|
||||
examples/jqueryexample/templates/index.html
|
||||
examples/jqueryexample/templates/layout.html
|
||||
examples/minitwit/README
|
||||
examples/minitwit/minitwit.py
|
||||
examples/minitwit/minitwit_tests.py
|
||||
examples/minitwit/schema.sql
|
||||
examples/minitwit/static/style.css
|
||||
examples/minitwit/templates/layout.html
|
||||
examples/minitwit/templates/login.html
|
||||
examples/minitwit/templates/register.html
|
||||
examples/minitwit/templates/timeline.html
|
||||
examples/persona/.DS_Store
|
||||
examples/persona/persona.py
|
||||
examples/persona/static/.DS_Store
|
||||
examples/persona/static/persona.js
|
||||
examples/persona/static/spinner.png
|
||||
examples/persona/static/style.css
|
||||
examples/persona/templates/index.html
|
||||
examples/persona/templates/layout.html
|
||||
flask/__init__.py
|
||||
flask/_compat.py
|
||||
flask/app.py
|
||||
flask/blueprints.py
|
||||
flask/config.py
|
||||
flask/ctx.py
|
||||
flask/debughelpers.py
|
||||
flask/exthook.py
|
||||
flask/globals.py
|
||||
flask/helpers.py
|
||||
flask/json.py
|
||||
flask/logging.py
|
||||
flask/module.py
|
||||
flask/sessions.py
|
||||
flask/signals.py
|
||||
flask/templating.py
|
||||
flask/testing.py
|
||||
flask/views.py
|
||||
flask/wrappers.py
|
||||
flask/ext/__init__.py
|
||||
flask/testsuite/__init__.py
|
||||
flask/testsuite/appctx.py
|
||||
flask/testsuite/basic.py
|
||||
flask/testsuite/blueprints.py
|
||||
flask/testsuite/config.py
|
||||
flask/testsuite/deprecations.py
|
||||
flask/testsuite/examples.py
|
||||
flask/testsuite/ext.py
|
||||
flask/testsuite/helpers.py
|
||||
flask/testsuite/regression.py
|
||||
flask/testsuite/reqctx.py
|
||||
flask/testsuite/signals.py
|
||||
flask/testsuite/subclassing.py
|
||||
flask/testsuite/templating.py
|
||||
flask/testsuite/testing.py
|
||||
flask/testsuite/views.py
|
||||
flask/testsuite/static/index.html
|
||||
flask/testsuite/templates/_macro.html
|
||||
flask/testsuite/templates/context_template.html
|
||||
flask/testsuite/templates/escaping_template.html
|
||||
flask/testsuite/templates/mail.txt
|
||||
flask/testsuite/templates/simple_template.html
|
||||
flask/testsuite/templates/template_filter.html
|
||||
flask/testsuite/templates/template_test.html
|
||||
flask/testsuite/templates/nested/nested.txt
|
||||
flask/testsuite/test_apps/config_module_app.py
|
||||
flask/testsuite/test_apps/flask_newext_simple.py
|
||||
flask/testsuite/test_apps/importerror.py
|
||||
flask/testsuite/test_apps/main_app.py
|
||||
flask/testsuite/test_apps/blueprintapp/__init__.py
|
||||
flask/testsuite/test_apps/blueprintapp/apps/__init__.py
|
||||
flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
|
||||
flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
|
||||
flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
|
||||
flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
|
||||
flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
|
||||
flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
|
||||
flask/testsuite/test_apps/config_package_app/__init__.py
|
||||
flask/testsuite/test_apps/flask_broken/__init__.py
|
||||
flask/testsuite/test_apps/flask_broken/b.py
|
||||
flask/testsuite/test_apps/flask_newext_package/__init__.py
|
||||
flask/testsuite/test_apps/flask_newext_package/submodule.py
|
||||
flask/testsuite/test_apps/flaskext/__init__.py
|
||||
flask/testsuite/test_apps/flaskext/oldext_simple.py
|
||||
flask/testsuite/test_apps/flaskext/oldext_package/__init__.py
|
||||
flask/testsuite/test_apps/flaskext/oldext_package/submodule.py
|
||||
flask/testsuite/test_apps/lib/python2.5/site-packages/SiteEgg.egg
|
||||
flask/testsuite/test_apps/lib/python2.5/site-packages/site_app.py
|
||||
flask/testsuite/test_apps/lib/python2.5/site-packages/site_package/__init__.py
|
||||
flask/testsuite/test_apps/moduleapp/__init__.py
|
||||
flask/testsuite/test_apps/moduleapp/apps/__init__.py
|
||||
flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
|
||||
flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
|
||||
flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
|
||||
flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
|
||||
flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
|
||||
flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
|
||||
flask/testsuite/test_apps/path/installed_package/__init__.py
|
||||
flask/testsuite/test_apps/subdomaintestmodule/__init__.py
|
||||
flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
|
1
lib/Flask-0.10.1-py2.7.egg-info/dependency_links.txt
Normal file
1
lib/Flask-0.10.1-py2.7.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
148
lib/Flask-0.10.1-py2.7.egg-info/installed-files.txt
Normal file
148
lib/Flask-0.10.1-py2.7.egg-info/installed-files.txt
Normal file
|
@ -0,0 +1,148 @@
|
|||
../flask/wrappers.py
|
||||
../flask/views.py
|
||||
../flask/testing.py
|
||||
../flask/templating.py
|
||||
../flask/signals.py
|
||||
../flask/sessions.py
|
||||
../flask/module.py
|
||||
../flask/logging.py
|
||||
../flask/json.py
|
||||
../flask/helpers.py
|
||||
../flask/globals.py
|
||||
../flask/exthook.py
|
||||
../flask/debughelpers.py
|
||||
../flask/ctx.py
|
||||
../flask/config.py
|
||||
../flask/blueprints.py
|
||||
../flask/app.py
|
||||
../flask/_compat.py
|
||||
../flask/__init__.py
|
||||
../flask/ext/__init__.py
|
||||
../flask/testsuite/views.py
|
||||
../flask/testsuite/testing.py
|
||||
../flask/testsuite/templating.py
|
||||
../flask/testsuite/subclassing.py
|
||||
../flask/testsuite/signals.py
|
||||
../flask/testsuite/reqctx.py
|
||||
../flask/testsuite/regression.py
|
||||
../flask/testsuite/helpers.py
|
||||
../flask/testsuite/ext.py
|
||||
../flask/testsuite/examples.py
|
||||
../flask/testsuite/deprecations.py
|
||||
../flask/testsuite/config.py
|
||||
../flask/testsuite/blueprints.py
|
||||
../flask/testsuite/basic.py
|
||||
../flask/testsuite/appctx.py
|
||||
../flask/testsuite/__init__.py
|
||||
../flask/testsuite/static/index.html
|
||||
../flask/testsuite/templates/_macro.html
|
||||
../flask/testsuite/templates/context_template.html
|
||||
../flask/testsuite/templates/escaping_template.html
|
||||
../flask/testsuite/templates/mail.txt
|
||||
../flask/testsuite/templates/simple_template.html
|
||||
../flask/testsuite/templates/template_filter.html
|
||||
../flask/testsuite/templates/template_test.html
|
||||
../flask/testsuite/templates/nested/nested.txt
|
||||
../flask/testsuite/test_apps/config_module_app.py
|
||||
../flask/testsuite/test_apps/flask_newext_simple.py
|
||||
../flask/testsuite/test_apps/importerror.py
|
||||
../flask/testsuite/test_apps/main_app.py
|
||||
../flask/testsuite/test_apps/blueprintapp/__init__.py
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/__init__.py
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
|
||||
../flask/testsuite/test_apps/config_package_app/__init__.py
|
||||
../flask/testsuite/test_apps/flask_broken/__init__.py
|
||||
../flask/testsuite/test_apps/flask_broken/b.py
|
||||
../flask/testsuite/test_apps/flask_newext_package/__init__.py
|
||||
../flask/testsuite/test_apps/flask_newext_package/submodule.py
|
||||
../flask/testsuite/test_apps/flaskext/__init__.py
|
||||
../flask/testsuite/test_apps/flaskext/oldext_simple.py
|
||||
../flask/testsuite/test_apps/flaskext/oldext_package/__init__.py
|
||||
../flask/testsuite/test_apps/flaskext/oldext_package/submodule.py
|
||||
../flask/testsuite/test_apps/lib/python2.5/site-packages/SiteEgg.egg
|
||||
../flask/testsuite/test_apps/lib/python2.5/site-packages/site_app.py
|
||||
../flask/testsuite/test_apps/lib/python2.5/site-packages/site_package/__init__.py
|
||||
../flask/testsuite/test_apps/moduleapp/__init__.py
|
||||
../flask/testsuite/test_apps/moduleapp/apps/__init__.py
|
||||
../flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
|
||||
../flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
|
||||
../flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
|
||||
../flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
|
||||
../flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
|
||||
../flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
|
||||
../flask/testsuite/test_apps/path/installed_package/__init__.py
|
||||
../flask/testsuite/test_apps/subdomaintestmodule/__init__.py
|
||||
../flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
|
||||
../flask/wrappers.pyc
|
||||
../flask/views.pyc
|
||||
../flask/testing.pyc
|
||||
../flask/templating.pyc
|
||||
../flask/signals.pyc
|
||||
../flask/sessions.pyc
|
||||
../flask/module.pyc
|
||||
../flask/logging.pyc
|
||||
../flask/json.pyc
|
||||
../flask/helpers.pyc
|
||||
../flask/globals.pyc
|
||||
../flask/exthook.pyc
|
||||
../flask/debughelpers.pyc
|
||||
../flask/ctx.pyc
|
||||
../flask/config.pyc
|
||||
../flask/blueprints.pyc
|
||||
../flask/app.pyc
|
||||
../flask/_compat.pyc
|
||||
../flask/__init__.pyc
|
||||
../flask/ext/__init__.pyc
|
||||
../flask/testsuite/views.pyc
|
||||
../flask/testsuite/testing.pyc
|
||||
../flask/testsuite/templating.pyc
|
||||
../flask/testsuite/subclassing.pyc
|
||||
../flask/testsuite/signals.pyc
|
||||
../flask/testsuite/reqctx.pyc
|
||||
../flask/testsuite/regression.pyc
|
||||
../flask/testsuite/helpers.pyc
|
||||
../flask/testsuite/ext.pyc
|
||||
../flask/testsuite/examples.pyc
|
||||
../flask/testsuite/deprecations.pyc
|
||||
../flask/testsuite/config.pyc
|
||||
../flask/testsuite/blueprints.pyc
|
||||
../flask/testsuite/basic.pyc
|
||||
../flask/testsuite/appctx.pyc
|
||||
../flask/testsuite/__init__.pyc
|
||||
../flask/testsuite/test_apps/config_module_app.pyc
|
||||
../flask/testsuite/test_apps/flask_newext_simple.pyc
|
||||
../flask/testsuite/test_apps/importerror.pyc
|
||||
../flask/testsuite/test_apps/main_app.pyc
|
||||
../flask/testsuite/test_apps/blueprintapp/__init__.pyc
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/__init__.pyc
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.pyc
|
||||
../flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.pyc
|
||||
../flask/testsuite/test_apps/config_package_app/__init__.pyc
|
||||
../flask/testsuite/test_apps/flask_broken/__init__.pyc
|
||||
../flask/testsuite/test_apps/flask_broken/b.pyc
|
||||
../flask/testsuite/test_apps/flask_newext_package/__init__.pyc
|
||||
../flask/testsuite/test_apps/flask_newext_package/submodule.pyc
|
||||
../flask/testsuite/test_apps/flaskext/__init__.pyc
|
||||
../flask/testsuite/test_apps/flaskext/oldext_simple.pyc
|
||||
../flask/testsuite/test_apps/flaskext/oldext_package/__init__.pyc
|
||||
../flask/testsuite/test_apps/flaskext/oldext_package/submodule.pyc
|
||||
../flask/testsuite/test_apps/lib/python2.5/site-packages/site_app.pyc
|
||||
../flask/testsuite/test_apps/lib/python2.5/site-packages/site_package/__init__.pyc
|
||||
../flask/testsuite/test_apps/moduleapp/__init__.pyc
|
||||
../flask/testsuite/test_apps/moduleapp/apps/__init__.pyc
|
||||
../flask/testsuite/test_apps/moduleapp/apps/admin/__init__.pyc
|
||||
../flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.pyc
|
||||
../flask/testsuite/test_apps/path/installed_package/__init__.pyc
|
||||
../flask/testsuite/test_apps/subdomaintestmodule/__init__.pyc
|
||||
./
|
||||
top_level.txt
|
||||
SOURCES.txt
|
||||
requires.txt
|
||||
PKG-INFO
|
||||
not-zip-safe
|
||||
dependency_links.txt
|
1
lib/Flask-0.10.1-py2.7.egg-info/not-zip-safe
Normal file
1
lib/Flask-0.10.1-py2.7.egg-info/not-zip-safe
Normal file
|
@ -0,0 +1 @@
|
|||
|
3
lib/Flask-0.10.1-py2.7.egg-info/requires.txt
Normal file
3
lib/Flask-0.10.1-py2.7.egg-info/requires.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Werkzeug>=0.7
|
||||
Jinja2>=2.4
|
||||
itsdangerous>=0.21
|
1
lib/Flask-0.10.1-py2.7.egg-info/top_level.txt
Normal file
1
lib/Flask-0.10.1-py2.7.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
flask
|
166
lib/Flask_Cors-2.0.0-py2.7.egg-info/PKG-INFO
Normal file
166
lib/Flask_Cors-2.0.0-py2.7.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,166 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: Flask-Cors
|
||||
Version: 2.0.0
|
||||
Summary: A Flask extension adding a decorator for CORS support
|
||||
Home-page: https://github.com/corydolphin/flask-cors
|
||||
Author: Cory Dolphin
|
||||
Author-email: corydolphin@gmail.com
|
||||
License: MIT
|
||||
Description: Flask-CORS
|
||||
==========
|
||||
|
||||
|Build Status| |Latest Version| |Downloads| |Supported Python versions|
|
||||
|License|
|
||||
|
||||
A Flask extension for handling Cross Origin Resource Sharing (CORS),
|
||||
making cross-origin AJAX possible.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install the extension with using pip, or easy\_install.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ pip install -U flask-cors
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
This extension enables CORS support either via a decorator, or a Flask
|
||||
extension. There are three examples shown in the
|
||||
`examples <https://github.com/corydolphin/flask-cors/tree/master/examples>`__
|
||||
directory, showing the major use cases. The suggested configuration is
|
||||
the
|
||||
`simple\_example.py <https://github.com/corydolphin/flask-cors/tree/master/examples/simple_example.py>`__,
|
||||
or the
|
||||
`app\_example.py <https://github.com/corydolphin/flask-cors/tree/master/examples/app_based_example.py>`__.
|
||||
A full list of options can be found in the
|
||||
`documentation <http://flask-cors.readthedocs.org/en/latest/>`__.
|
||||
|
||||
This package has a simple philosophy, when you want to enable CORS, you
|
||||
wish to enable it for all use cases on a domain. This means no mucking
|
||||
around with different allowed headers, methods, etc. By default,
|
||||
submission of cookies across domains is disabled due to the security
|
||||
implications, please see the documentation for how to enable
|
||||
credential'ed requests, and please make sure you add some sort of
|
||||
`CRSF <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`__
|
||||
protection before doing so!
|
||||
|
||||
Simple Usage
|
||||
~~~~~~~~~~~~
|
||||
|
||||
In the simplest case, initialize the Flask-Cors extension with default
|
||||
arguments in order to allow CORS for all domains on all routes.
|
||||
|
||||
.. code:: python
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
cors = CORS(app)
|
||||
|
||||
@app.route("/")
|
||||
def helloWorld():
|
||||
return "Hello, cross-origin-world!"
|
||||
|
||||
Resource specific CORS
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Alternatively, you can specify CORS options on a resource and origin
|
||||
level of granularity by passing a dictionary as the 'resources' option,
|
||||
mapping paths to a set of options.
|
||||
|
||||
.. code:: python
|
||||
|
||||
app = Flask(__name__)
|
||||
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
|
||||
|
||||
@app.route("/api/v1/users")
|
||||
def list_users():
|
||||
return "user example"
|
||||
|
||||
Route specific CORS via decorator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This extension also exposes a simple decorator to decorate flask routes
|
||||
with. Simply add ``@cross_origin()`` below a call to Flask's
|
||||
``@app.route(..)`` to allow CORS on a given route.
|
||||
|
||||
.. code:: python
|
||||
|
||||
@app.route("/")
|
||||
@cross_origin()
|
||||
def helloWorld():
|
||||
return "Hello, cross-origin-world!"
|
||||
|
||||
Logging
|
||||
^^^^^^^
|
||||
|
||||
Flask-Cors uses standard Python logging, using the logger name
|
||||
'``app.logger_name``.cors'. The app's logger name attribute is usually
|
||||
the same as the name of the app. You can read more about logging from
|
||||
`Flask's
|
||||
documentation <http://flask.pocoo.org/docs/0.10/errorhandling/>`__.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import logging
|
||||
# make your awesome app
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
For a full list of options, please see the full
|
||||
`documentation <http://flask-cors.readthedocs.org/en/latest/>`__
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
A simple set of tests is included in ``test/``. To run, install nose,
|
||||
and simply invoke ``nosetests`` or ``python setup.py test`` to exercise
|
||||
the tests.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Questions, comments or improvements? Please create an issue on
|
||||
`Github <https://github.com/corydolphin/flask-cors>`__, tweet at
|
||||
`@corydolphin <https://twitter.com/corydolphin>`__ or send me an email.
|
||||
I do my best to include every contribution proposed in any way that I
|
||||
can.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
This Flask extension is based upon the `Decorator for the HTTP Access
|
||||
Control <http://flask.pocoo.org/snippets/56/>`__ written by Armin
|
||||
Ronacher.
|
||||
|
||||
.. |Build Status| image:: https://api.travis-ci.org/corydolphin/flask-cors.svg?branch=master
|
||||
:target: https://travis-ci.org/corydolphin/flask-cors
|
||||
.. |Latest Version| image:: https://pypip.in/version/Flask-Cors/badge.svg
|
||||
:target: https://pypi.python.org/pypi/Flask-Cors/
|
||||
.. |Downloads| image:: https://pypip.in/download/Flask-Cors/badge.svg
|
||||
:target: https://pypi.python.org/pypi/Flask-Cors/
|
||||
.. |Supported Python versions| image:: https://pypip.in/py_versions/Flask-Cors/badge.svg
|
||||
:target: https://pypi.python.org/pypi/Flask-Cors/
|
||||
.. |License| image:: https://pypip.in/license/Flask-Cors/badge.svg
|
||||
:target: https://pypi.python.org/pypi/Flask-Cors/
|
||||
|
||||
Platform: any
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
38
lib/Flask_Cors-2.0.0-py2.7.egg-info/SOURCES.txt
Normal file
38
lib/Flask_Cors-2.0.0-py2.7.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
LICENSE
|
||||
MANIFEST.in
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
Flask_Cors.egg-info/PKG-INFO
|
||||
Flask_Cors.egg-info/SOURCES.txt
|
||||
Flask_Cors.egg-info/dependency_links.txt
|
||||
Flask_Cors.egg-info/not-zip-safe
|
||||
Flask_Cors.egg-info/requires.txt
|
||||
Flask_Cors.egg-info/top_level.txt
|
||||
docs/Makefile
|
||||
docs/conf.py
|
||||
docs/index.rst
|
||||
examples/app_based_example.py
|
||||
examples/view_based_example.py
|
||||
flask_cors/__init__.py
|
||||
flask_cors/core.py
|
||||
flask_cors/decorator.py
|
||||
flask_cors/extension.py
|
||||
flask_cors/version.py
|
||||
tests/__init__.py
|
||||
tests/base_test.py
|
||||
tests/core/__init__.py
|
||||
tests/core/helper_tests.py
|
||||
tests/decorator/__init__.py
|
||||
tests/decorator/test_allow_headers.py
|
||||
tests/decorator/test_credentials.py
|
||||
tests/decorator/test_exception_interception.py
|
||||
tests/decorator/test_expose_headers.py
|
||||
tests/decorator/test_max_age.py
|
||||
tests/decorator/test_methods.py
|
||||
tests/decorator/test_options.py
|
||||
tests/decorator/test_origins.py
|
||||
tests/decorator/test_vary_header.py
|
||||
tests/decorator/test_w3.py
|
||||
tests/extension/__init__.py
|
||||
tests/extension/test_app_extension.py
|
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/dependency_links.txt
Normal file
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
17
lib/Flask_Cors-2.0.0-py2.7.egg-info/installed-files.txt
Normal file
17
lib/Flask_Cors-2.0.0-py2.7.egg-info/installed-files.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
../flask_cors/__init__.py
|
||||
../flask_cors/version.py
|
||||
../flask_cors/decorator.py
|
||||
../flask_cors/core.py
|
||||
../flask_cors/extension.py
|
||||
../flask_cors/__init__.pyc
|
||||
../flask_cors/version.pyc
|
||||
../flask_cors/decorator.pyc
|
||||
../flask_cors/core.pyc
|
||||
../flask_cors/extension.pyc
|
||||
./
|
||||
SOURCES.txt
|
||||
requires.txt
|
||||
not-zip-safe
|
||||
PKG-INFO
|
||||
top_level.txt
|
||||
dependency_links.txt
|
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/not-zip-safe
Normal file
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/not-zip-safe
Normal file
|
@ -0,0 +1 @@
|
|||
|
2
lib/Flask_Cors-2.0.0-py2.7.egg-info/requires.txt
Normal file
2
lib/Flask_Cors-2.0.0-py2.7.egg-info/requires.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Flask >= 0.9
|
||||
Six
|
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/top_level.txt
Normal file
1
lib/Flask_Cors-2.0.0-py2.7.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
flask_cors
|
29
lib/GitHub_Flask-2.0.1-py2.7.egg-info/PKG-INFO
Executable file
29
lib/GitHub_Flask-2.0.1-py2.7.egg-info/PKG-INFO
Executable file
|
@ -0,0 +1,29 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: GitHub-Flask
|
||||
Version: 2.0.1
|
||||
Summary: GitHub extension for Flask microframework
|
||||
Home-page: http://github.com/cenkalti/github-flask
|
||||
Author: Cenk Alti
|
||||
Author-email: cenkalti@gmail.com
|
||||
License: MIT
|
||||
Description:
|
||||
GitHub-Flask
|
||||
------------
|
||||
|
||||
Adds support to authorize users with GitHub and make API requests with Flask.
|
||||
|
||||
Links
|
||||
`````
|
||||
|
||||
* `documentation <http://github-flask.readthedocs.org>`_
|
||||
* `development version
|
||||
<http://github.com/cenkalti/github-flask/zipball/master#egg=GitHub-Flask-dev>`_
|
||||
|
||||
|
||||
Platform: any
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
10
lib/GitHub_Flask-2.0.1-py2.7.egg-info/SOURCES.txt
Executable file
10
lib/GitHub_Flask-2.0.1-py2.7.egg-info/SOURCES.txt
Executable file
|
@ -0,0 +1,10 @@
|
|||
README.rst
|
||||
flask_github.py
|
||||
setup.cfg
|
||||
setup.py
|
||||
GitHub_Flask.egg-info/PKG-INFO
|
||||
GitHub_Flask.egg-info/SOURCES.txt
|
||||
GitHub_Flask.egg-info/dependency_links.txt
|
||||
GitHub_Flask.egg-info/not-zip-safe
|
||||
GitHub_Flask.egg-info/requires.txt
|
||||
GitHub_Flask.egg-info/top_level.txt
|
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/dependency_links.txt
Executable file
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/dependency_links.txt
Executable file
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
../flask_github.py
|
||||
../flask_github.pyc
|
||||
./
|
||||
SOURCES.txt
|
||||
requires.txt
|
||||
not-zip-safe
|
||||
PKG-INFO
|
||||
top_level.txt
|
||||
dependency_links.txt
|
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/not-zip-safe
Executable file
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/not-zip-safe
Executable file
|
@ -0,0 +1 @@
|
|||
|
2
lib/GitHub_Flask-2.0.1-py2.7.egg-info/requires.txt
Executable file
2
lib/GitHub_Flask-2.0.1-py2.7.egg-info/requires.txt
Executable file
|
@ -0,0 +1,2 @@
|
|||
Flask
|
||||
requests
|
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/top_level.txt
Executable file
1
lib/GitHub_Flask-2.0.1-py2.7.egg-info/top_level.txt
Executable file
|
@ -0,0 +1 @@
|
|||
flask_github
|
55
lib/Jinja2-2.7.3-py2.7.egg-info/PKG-INFO
Normal file
55
lib/Jinja2-2.7.3-py2.7.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,55 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: Jinja2
|
||||
Version: 2.7.3
|
||||
Summary: A small but fast and easy to use stand-alone template engine written in pure python.
|
||||
Home-page: http://jinja.pocoo.org/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description:
|
||||
Jinja2
|
||||
~~~~~~
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
`Django`_ inspired non-XML syntax but supports inline expressions and
|
||||
an optional `sandboxed`_ environment.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Application logic is for the controller but don't try to make the life
|
||||
for the template designer too hard by giving him too few functionality.
|
||||
|
||||
For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
|
||||
|
||||
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security)
|
||||
.. _Django: http://www.djangoproject.com/
|
||||
.. _Jinja2 webpage: http://jinja.pocoo.org/
|
||||
.. _documentation: http://jinja.pocoo.org/2/documentation/
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
126
lib/Jinja2-2.7.3-py2.7.egg-info/SOURCES.txt
Normal file
126
lib/Jinja2-2.7.3-py2.7.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,126 @@
|
|||
AUTHORS
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
Makefile
|
||||
README.rst
|
||||
run-tests.py
|
||||
setup.cfg
|
||||
setup.py
|
||||
Jinja2.egg-info/PKG-INFO
|
||||
Jinja2.egg-info/SOURCES.txt
|
||||
Jinja2.egg-info/dependency_links.txt
|
||||
Jinja2.egg-info/entry_points.txt
|
||||
Jinja2.egg-info/not-zip-safe
|
||||
Jinja2.egg-info/requires.txt
|
||||
Jinja2.egg-info/top_level.txt
|
||||
artwork/jinjalogo.svg
|
||||
docs/Makefile
|
||||
docs/api.rst
|
||||
docs/cache_extension.py
|
||||
docs/changelog.rst
|
||||
docs/conf.py
|
||||
docs/contents.rst.inc
|
||||
docs/extensions.rst
|
||||
docs/faq.rst
|
||||
docs/index.rst
|
||||
docs/integration.rst
|
||||
docs/intro.rst
|
||||
docs/jinjaext.py
|
||||
docs/jinjastyle.sty
|
||||
docs/latexindex.rst
|
||||
docs/logo.pdf
|
||||
docs/sandbox.rst
|
||||
docs/switching.rst
|
||||
docs/templates.rst
|
||||
docs/tricks.rst
|
||||
docs/_static/.ignore
|
||||
docs/_static/jinja-small.png
|
||||
docs/_templates/sidebarintro.html
|
||||
docs/_templates/sidebarlogo.html
|
||||
docs/_themes/LICENSE
|
||||
docs/_themes/README
|
||||
docs/_themes/jinja/layout.html
|
||||
docs/_themes/jinja/relations.html
|
||||
docs/_themes/jinja/theme.conf
|
||||
docs/_themes/jinja/static/jinja.css_t
|
||||
examples/bench.py
|
||||
examples/profile.py
|
||||
examples/basic/cycle.py
|
||||
examples/basic/debugger.py
|
||||
examples/basic/inheritance.py
|
||||
examples/basic/test.py
|
||||
examples/basic/test_filter_and_linestatements.py
|
||||
examples/basic/test_loop_filter.py
|
||||
examples/basic/translate.py
|
||||
examples/basic/templates/broken.html
|
||||
examples/basic/templates/subbroken.html
|
||||
examples/rwbench/djangoext.py
|
||||
examples/rwbench/rwbench.py
|
||||
examples/rwbench/django/_form.html
|
||||
examples/rwbench/django/_input_field.html
|
||||
examples/rwbench/django/_textarea.html
|
||||
examples/rwbench/django/index.html
|
||||
examples/rwbench/django/layout.html
|
||||
examples/rwbench/genshi/helpers.html
|
||||
examples/rwbench/genshi/index.html
|
||||
examples/rwbench/genshi/layout.html
|
||||
examples/rwbench/jinja/helpers.html
|
||||
examples/rwbench/jinja/index.html
|
||||
examples/rwbench/jinja/layout.html
|
||||
examples/rwbench/mako/helpers.html
|
||||
examples/rwbench/mako/index.html
|
||||
examples/rwbench/mako/layout.html
|
||||
ext/djangojinja2.py
|
||||
ext/inlinegettext.py
|
||||
ext/jinja.el
|
||||
ext/Vim/jinja.vim
|
||||
ext/django2jinja/django2jinja.py
|
||||
ext/django2jinja/example.py
|
||||
ext/django2jinja/templates/index.html
|
||||
ext/django2jinja/templates/layout.html
|
||||
ext/django2jinja/templates/subtemplate.html
|
||||
jinja2/__init__.py
|
||||
jinja2/_compat.py
|
||||
jinja2/_stringdefs.py
|
||||
jinja2/bccache.py
|
||||
jinja2/compiler.py
|
||||
jinja2/constants.py
|
||||
jinja2/debug.py
|
||||
jinja2/defaults.py
|
||||
jinja2/environment.py
|
||||
jinja2/exceptions.py
|
||||
jinja2/ext.py
|
||||
jinja2/filters.py
|
||||
jinja2/lexer.py
|
||||
jinja2/loaders.py
|
||||
jinja2/meta.py
|
||||
jinja2/nodes.py
|
||||
jinja2/optimizer.py
|
||||
jinja2/parser.py
|
||||
jinja2/runtime.py
|
||||
jinja2/sandbox.py
|
||||
jinja2/tests.py
|
||||
jinja2/utils.py
|
||||
jinja2/visitor.py
|
||||
jinja2/testsuite/__init__.py
|
||||
jinja2/testsuite/api.py
|
||||
jinja2/testsuite/bytecode_cache.py
|
||||
jinja2/testsuite/core_tags.py
|
||||
jinja2/testsuite/debug.py
|
||||
jinja2/testsuite/doctests.py
|
||||
jinja2/testsuite/ext.py
|
||||
jinja2/testsuite/filters.py
|
||||
jinja2/testsuite/imports.py
|
||||
jinja2/testsuite/inheritance.py
|
||||
jinja2/testsuite/lexnparse.py
|
||||
jinja2/testsuite/loader.py
|
||||
jinja2/testsuite/regression.py
|
||||
jinja2/testsuite/security.py
|
||||
jinja2/testsuite/tests.py
|
||||
jinja2/testsuite/utils.py
|
||||
jinja2/testsuite/res/__init__.py
|
||||
jinja2/testsuite/res/templates/broken.html
|
||||
jinja2/testsuite/res/templates/syntaxerror.html
|
||||
jinja2/testsuite/res/templates/test.html
|
||||
jinja2/testsuite/res/templates/foo/test.html
|
1
lib/Jinja2-2.7.3-py2.7.egg-info/dependency_links.txt
Normal file
1
lib/Jinja2-2.7.3-py2.7.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
4
lib/Jinja2-2.7.3-py2.7.egg-info/entry_points.txt
Normal file
4
lib/Jinja2-2.7.3-py2.7.egg-info/entry_points.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
[babel.extractors]
|
||||
jinja2 = jinja2.ext:babel_extract[i18n]
|
||||
|
92
lib/Jinja2-2.7.3-py2.7.egg-info/installed-files.txt
Normal file
92
lib/Jinja2-2.7.3-py2.7.egg-info/installed-files.txt
Normal file
|
@ -0,0 +1,92 @@
|
|||
../jinja2/visitor.py
|
||||
../jinja2/utils.py
|
||||
../jinja2/tests.py
|
||||
../jinja2/sandbox.py
|
||||
../jinja2/runtime.py
|
||||
../jinja2/parser.py
|
||||
../jinja2/optimizer.py
|
||||
../jinja2/nodes.py
|
||||
../jinja2/meta.py
|
||||
../jinja2/loaders.py
|
||||
../jinja2/lexer.py
|
||||
../jinja2/filters.py
|
||||
../jinja2/ext.py
|
||||
../jinja2/exceptions.py
|
||||
../jinja2/environment.py
|
||||
../jinja2/defaults.py
|
||||
../jinja2/debug.py
|
||||
../jinja2/constants.py
|
||||
../jinja2/compiler.py
|
||||
../jinja2/bccache.py
|
||||
../jinja2/_stringdefs.py
|
||||
../jinja2/_compat.py
|
||||
../jinja2/__init__.py
|
||||
../jinja2/testsuite/utils.py
|
||||
../jinja2/testsuite/tests.py
|
||||
../jinja2/testsuite/security.py
|
||||
../jinja2/testsuite/regression.py
|
||||
../jinja2/testsuite/loader.py
|
||||
../jinja2/testsuite/lexnparse.py
|
||||
../jinja2/testsuite/inheritance.py
|
||||
../jinja2/testsuite/imports.py
|
||||
../jinja2/testsuite/filters.py
|
||||
../jinja2/testsuite/ext.py
|
||||
../jinja2/testsuite/doctests.py
|
||||
../jinja2/testsuite/debug.py
|
||||
../jinja2/testsuite/core_tags.py
|
||||
../jinja2/testsuite/bytecode_cache.py
|
||||
../jinja2/testsuite/api.py
|
||||
../jinja2/testsuite/__init__.py
|
||||
../jinja2/testsuite/res/__init__.py
|
||||
../jinja2/testsuite/res/templates/broken.html
|
||||
../jinja2/testsuite/res/templates/syntaxerror.html
|
||||
../jinja2/testsuite/res/templates/test.html
|
||||
../jinja2/testsuite/res/templates/foo/test.html
|
||||
../jinja2/visitor.pyc
|
||||
../jinja2/utils.pyc
|
||||
../jinja2/tests.pyc
|
||||
../jinja2/sandbox.pyc
|
||||
../jinja2/runtime.pyc
|
||||
../jinja2/parser.pyc
|
||||
../jinja2/optimizer.pyc
|
||||
../jinja2/nodes.pyc
|
||||
../jinja2/meta.pyc
|
||||
../jinja2/loaders.pyc
|
||||
../jinja2/lexer.pyc
|
||||
../jinja2/filters.pyc
|
||||
../jinja2/ext.pyc
|
||||
../jinja2/exceptions.pyc
|
||||
../jinja2/environment.pyc
|
||||
../jinja2/defaults.pyc
|
||||
../jinja2/debug.pyc
|
||||
../jinja2/constants.pyc
|
||||
../jinja2/compiler.pyc
|
||||
../jinja2/bccache.pyc
|
||||
../jinja2/_stringdefs.pyc
|
||||
../jinja2/_compat.pyc
|
||||
../jinja2/__init__.pyc
|
||||
../jinja2/testsuite/utils.pyc
|
||||
../jinja2/testsuite/tests.pyc
|
||||
../jinja2/testsuite/security.pyc
|
||||
../jinja2/testsuite/regression.pyc
|
||||
../jinja2/testsuite/loader.pyc
|
||||
../jinja2/testsuite/lexnparse.pyc
|
||||
../jinja2/testsuite/inheritance.pyc
|
||||
../jinja2/testsuite/imports.pyc
|
||||
../jinja2/testsuite/filters.pyc
|
||||
../jinja2/testsuite/ext.pyc
|
||||
../jinja2/testsuite/doctests.pyc
|
||||
../jinja2/testsuite/debug.pyc
|
||||
../jinja2/testsuite/core_tags.pyc
|
||||
../jinja2/testsuite/bytecode_cache.pyc
|
||||
../jinja2/testsuite/api.pyc
|
||||
../jinja2/testsuite/__init__.pyc
|
||||
../jinja2/testsuite/res/__init__.pyc
|
||||
./
|
||||
top_level.txt
|
||||
SOURCES.txt
|
||||
requires.txt
|
||||
PKG-INFO
|
||||
not-zip-safe
|
||||
entry_points.txt
|
||||
dependency_links.txt
|
1
lib/Jinja2-2.7.3-py2.7.egg-info/not-zip-safe
Normal file
1
lib/Jinja2-2.7.3-py2.7.egg-info/not-zip-safe
Normal file
|
@ -0,0 +1 @@
|
|||
|
4
lib/Jinja2-2.7.3-py2.7.egg-info/requires.txt
Normal file
4
lib/Jinja2-2.7.3-py2.7.egg-info/requires.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
markupsafe
|
||||
|
||||
[i18n]
|
||||
Babel>=0.8
|
1
lib/Jinja2-2.7.3-py2.7.egg-info/top_level.txt
Normal file
1
lib/Jinja2-2.7.3-py2.7.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
jinja2
|
119
lib/MarkupSafe-0.23-py2.7.egg-info/PKG-INFO
Normal file
119
lib/MarkupSafe-0.23-py2.7.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,119 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: MarkupSafe
|
||||
Version: 0.23
|
||||
Summary: Implements a XML/HTML/XHTML Markup safe string for Python
|
||||
Home-page: http://github.com/mitsuhiko/markupsafe
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description: MarkupSafe
|
||||
==========
|
||||
|
||||
Implements a unicode subclass that supports HTML strings:
|
||||
|
||||
>>> from markupsafe import Markup, escape
|
||||
>>> escape("<script>alert(document.cookie);</script>")
|
||||
Markup(u'<script>alert(document.cookie);</script>')
|
||||
>>> tmpl = Markup("<em>%s</em>")
|
||||
>>> tmpl % "Peter > Lustig"
|
||||
Markup(u'<em>Peter > Lustig</em>')
|
||||
|
||||
If you want to make an object unicode that is not yet unicode
|
||||
but don't want to lose the taint information, you can use the
|
||||
`soft_unicode` function. (On Python 3 you can also use `soft_str` which
|
||||
is a different name for the same function).
|
||||
|
||||
>>> from markupsafe import soft_unicode
|
||||
>>> soft_unicode(42)
|
||||
u'42'
|
||||
>>> soft_unicode(Markup('foo'))
|
||||
Markup(u'foo')
|
||||
|
||||
HTML Representations
|
||||
--------------------
|
||||
|
||||
Objects can customize their HTML markup equivalent by overriding
|
||||
the `__html__` function:
|
||||
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<strong>Nice</strong>'
|
||||
...
|
||||
>>> escape(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
|
||||
Silent Escapes
|
||||
--------------
|
||||
|
||||
Since MarkupSafe 0.10 there is now also a separate escape function
|
||||
called `escape_silent` that returns an empty string for `None` for
|
||||
consistency with other systems that return empty strings for `None`
|
||||
when escaping (for instance Pylons' webhelpers).
|
||||
|
||||
If you also want to use this for the escape method of the Markup
|
||||
object, you can create your own subclass that does that::
|
||||
|
||||
from markupsafe import Markup, escape_silent as escape
|
||||
|
||||
class SilentMarkup(Markup):
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
return cls(escape(s))
|
||||
|
||||
New-Style String Formatting
|
||||
---------------------------
|
||||
|
||||
Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and
|
||||
3.x are now fully supported. Previously the escape behavior of those
|
||||
functions was spotty at best. The new implementations operates under the
|
||||
following algorithm:
|
||||
|
||||
1. if an object has an ``__html_format__`` method it is called as
|
||||
replacement for ``__format__`` with the format specifier. It either
|
||||
has to return a string or markup object.
|
||||
2. if an object has an ``__html__`` method it is called.
|
||||
3. otherwise the default format system of Python kicks in and the result
|
||||
is HTML escaped.
|
||||
|
||||
Here is how you can implement your own formatting::
|
||||
|
||||
class User(object):
|
||||
|
||||
def __init__(self, id, username):
|
||||
self.id = id
|
||||
self.username = username
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec == 'link':
|
||||
return Markup('<a href="/user/{0}">{1}</a>').format(
|
||||
self.id,
|
||||
self.__html__(),
|
||||
)
|
||||
elif format_spec:
|
||||
raise ValueError('Invalid format spec')
|
||||
return self.__html__()
|
||||
|
||||
def __html__(self):
|
||||
return Markup('<span class=user>{0}</span>').format(self.username)
|
||||
|
||||
And to format that user:
|
||||
|
||||
>>> user = User(1, 'foo')
|
||||
>>> Markup('<p>User: {0:link}').format(user)
|
||||
Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>')
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
17
lib/MarkupSafe-0.23-py2.7.egg-info/SOURCES.txt
Normal file
17
lib/MarkupSafe-0.23-py2.7.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
AUTHORS
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
MarkupSafe.egg-info/PKG-INFO
|
||||
MarkupSafe.egg-info/SOURCES.txt
|
||||
MarkupSafe.egg-info/dependency_links.txt
|
||||
MarkupSafe.egg-info/not-zip-safe
|
||||
MarkupSafe.egg-info/top_level.txt
|
||||
markupsafe/__init__.py
|
||||
markupsafe/_compat.py
|
||||
markupsafe/_constants.py
|
||||
markupsafe/_native.py
|
||||
markupsafe/_speedups.c
|
||||
markupsafe/tests.py
|
1
lib/MarkupSafe-0.23-py2.7.egg-info/dependency_links.txt
Normal file
1
lib/MarkupSafe-0.23-py2.7.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
18
lib/MarkupSafe-0.23-py2.7.egg-info/installed-files.txt
Normal file
18
lib/MarkupSafe-0.23-py2.7.egg-info/installed-files.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
../markupsafe/tests.py
|
||||
../markupsafe/_native.py
|
||||
../markupsafe/_constants.py
|
||||
../markupsafe/_compat.py
|
||||
../markupsafe/__init__.py
|
||||
../markupsafe/_speedups.c
|
||||
../markupsafe/tests.pyc
|
||||
../markupsafe/_native.pyc
|
||||
../markupsafe/_constants.pyc
|
||||
../markupsafe/_compat.pyc
|
||||
../markupsafe/__init__.pyc
|
||||
../markupsafe/_speedups.so
|
||||
./
|
||||
top_level.txt
|
||||
SOURCES.txt
|
||||
PKG-INFO
|
||||
not-zip-safe
|
||||
dependency_links.txt
|
1
lib/MarkupSafe-0.23-py2.7.egg-info/not-zip-safe
Normal file
1
lib/MarkupSafe-0.23-py2.7.egg-info/not-zip-safe
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
lib/MarkupSafe-0.23-py2.7.egg-info/top_level.txt
Normal file
1
lib/MarkupSafe-0.23-py2.7.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
markupsafe
|
54
lib/Werkzeug-0.10.4.dist-info/DESCRIPTION.rst
Normal file
54
lib/Werkzeug-0.10.4.dist-info/DESCRIPTION.rst
Normal file
|
@ -0,0 +1,54 @@
|
|||
Werkzeug
|
||||
========
|
||||
|
||||
Werkzeug started as simple collection of various utilities for WSGI
|
||||
applications and has become one of the most advanced WSGI utility
|
||||
modules. It includes a powerful debugger, full featured request and
|
||||
response objects, HTTP utilities to handle entity tags, cache control
|
||||
headers, HTTP dates, cookie handling, file uploads, a powerful URL
|
||||
routing system and a bunch of community contributed addon modules.
|
||||
|
||||
Werkzeug is unicode aware and doesn't enforce a specific template
|
||||
engine, database adapter or anything else. It doesn't even enforce
|
||||
a specific way of handling requests and leaves all that up to the
|
||||
developer. It's most useful for end user applications which should work
|
||||
on as many server environments as possible (such as blogs, wikis,
|
||||
bulletin boards, etc.).
|
||||
|
||||
Details and example applications are available on the
|
||||
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- unicode awareness
|
||||
|
||||
- request and response objects
|
||||
|
||||
- various utility functions for dealing with HTTP headers such as
|
||||
`Accept` and `Cache-Control` headers.
|
||||
|
||||
- thread local objects with proper cleanup at request end
|
||||
|
||||
- an interactive debugger
|
||||
|
||||
- A simple WSGI server with support for threading and forking
|
||||
with an automatic reloader.
|
||||
|
||||
- a flexible URL routing system with REST support.
|
||||
|
||||
- fully WSGI compatible
|
||||
|
||||
|
||||
Development Version
|
||||
-------------------
|
||||
|
||||
The Werkzeug development version can be installed by cloning the git
|
||||
repository from `github`_::
|
||||
|
||||
git clone git@github.com:mitsuhiko/werkzeug.git
|
||||
|
||||
.. _github: http://github.com/mitsuhiko/werkzeug
|
||||
|
||||
|
73
lib/Werkzeug-0.10.4.dist-info/METADATA
Normal file
73
lib/Werkzeug-0.10.4.dist-info/METADATA
Normal file
|
@ -0,0 +1,73 @@
|
|||
Metadata-Version: 2.0
|
||||
Name: Werkzeug
|
||||
Version: 0.10.4
|
||||
Summary: The Swiss Army knife of Python web development
|
||||
Home-page: http://werkzeug.pocoo.org/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Platform: any
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
|
||||
Werkzeug
|
||||
========
|
||||
|
||||
Werkzeug started as simple collection of various utilities for WSGI
|
||||
applications and has become one of the most advanced WSGI utility
|
||||
modules. It includes a powerful debugger, full featured request and
|
||||
response objects, HTTP utilities to handle entity tags, cache control
|
||||
headers, HTTP dates, cookie handling, file uploads, a powerful URL
|
||||
routing system and a bunch of community contributed addon modules.
|
||||
|
||||
Werkzeug is unicode aware and doesn't enforce a specific template
|
||||
engine, database adapter or anything else. It doesn't even enforce
|
||||
a specific way of handling requests and leaves all that up to the
|
||||
developer. It's most useful for end user applications which should work
|
||||
on as many server environments as possible (such as blogs, wikis,
|
||||
bulletin boards, etc.).
|
||||
|
||||
Details and example applications are available on the
|
||||
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- unicode awareness
|
||||
|
||||
- request and response objects
|
||||
|
||||
- various utility functions for dealing with HTTP headers such as
|
||||
`Accept` and `Cache-Control` headers.
|
||||
|
||||
- thread local objects with proper cleanup at request end
|
||||
|
||||
- an interactive debugger
|
||||
|
||||
- A simple WSGI server with support for threading and forking
|
||||
with an automatic reloader.
|
||||
|
||||
- a flexible URL routing system with REST support.
|
||||
|
||||
- fully WSGI compatible
|
||||
|
||||
|
||||
Development Version
|
||||
-------------------
|
||||
|
||||
The Werkzeug development version can be installed by cloning the git
|
||||
repository from `github`_::
|
||||
|
||||
git clone git@github.com:mitsuhiko/werkzeug.git
|
||||
|
||||
.. _github: http://github.com/mitsuhiko/werkzeug
|
||||
|
||||
|
91
lib/Werkzeug-0.10.4.dist-info/RECORD
Normal file
91
lib/Werkzeug-0.10.4.dist-info/RECORD
Normal file
|
@ -0,0 +1,91 @@
|
|||
werkzeug/posixemulation.py,sha256=58IrWwAkpafaUgQMVvPfkPrjme73eIuY5PjKL2wT37Y,3483
|
||||
werkzeug/security.py,sha256=UxbSD8C-oFid9r5Oncofeuz-EALPpjE10fefDljbVfw,8971
|
||||
werkzeug/__init__.py,sha256=iYo261RIx6wL-YWtbovcn5skbouhaWdJJRGLh2kGAeM,7211
|
||||
werkzeug/testapp.py,sha256=txTIeAoq83FW6HVTBroW_0AAvnIUpZl0w8Vj2sluLDY,9398
|
||||
werkzeug/http.py,sha256=DtyRYXP06JzEdX6YS5mPFYTbtnrH7qpNvYqlqLMeoTs,34279
|
||||
werkzeug/routing.py,sha256=9fUMWiQePjbS_tugJgKTCOg9tyEnw9BaEfAj2VraAqY,64428
|
||||
werkzeug/utils.py,sha256=Ps0V2dT_HOXgXLxH4S6o_WyQG9IF8dpcAcvPsF_xgO4,23063
|
||||
werkzeug/exceptions.py,sha256=BAU0SzpOfZmU_18nyyIAzObKZhE_UrSnhymhrwGQg64,18577
|
||||
werkzeug/_reloader.py,sha256=v0WOEKO8b3q9VwKidJRQDGq4ycDZgu2znwphvgdOb_I,7938
|
||||
werkzeug/formparser.py,sha256=i0-ZzXhVxSNNMBDRHWzdbte9l5QRlycVHp0BENI2pCY,21205
|
||||
werkzeug/_compat.py,sha256=gsFXhixbO2A8rAcfm7oIDPLENSXKdeDkpieP__g8l1M,6190
|
||||
werkzeug/datastructures.py,sha256=zNlWB-h7ttq-Q-dvQqs-AAUDGH96EnYS1c60ffYanPw,86857
|
||||
werkzeug/wrappers.py,sha256=Fw1xlCuFSdq21pj6QvJkCXYkIykaSngF7w2NGPsGgdU,76919
|
||||
werkzeug/test.py,sha256=96zCaMfWMahIW2VeOdERQ66DlqslA5qrehIs6-orwrQ,34255
|
||||
werkzeug/urls.py,sha256=NObHlWIyrMA_PpOlRFCsiBn502kjM6giZ02LCgZzOfY,36596
|
||||
werkzeug/script.py,sha256=Fq9fTl2217_Chf4F2XRcpLU5xzQsNqK9tHz-CLZJKzw,11365
|
||||
werkzeug/useragents.py,sha256=DF0uXjvoKWcWgLHYOIRI98VlN0fezCNXWIJv0tmBE4A,5399
|
||||
werkzeug/local.py,sha256=r5qW9-Q2jNfZLBDoboae8A3ngw9olbVnpIs2byMDrLI,14095
|
||||
werkzeug/serving.py,sha256=WaJGURZx2uO1O95_J_zJ3eLLZq82qdxBGxFYWdWX5_0,25317
|
||||
werkzeug/wsgi.py,sha256=-lFqnoyiAV4pWf1C7gJISitddGTBKf2oqPc0Dt-XJZc,37837
|
||||
werkzeug/_internal.py,sha256=3ELUVz48eXOZeDfvgNaSSGdsg0keKw-Jvtl0Gu03WgY,13713
|
||||
werkzeug/contrib/fixers.py,sha256=8qM2G4yx1ZWm4wdHLzDVAAvFEVk03CZrY5fRnPFrUyk,10197
|
||||
werkzeug/contrib/limiter.py,sha256=wgprYdgFcwwxNy2sQlFrAyd2GxHvft0CObThiv6vPKI,1333
|
||||
werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623
|
||||
werkzeug/contrib/testtools.py,sha256=MxyzW_79JppFjNOasq_KhcmYPLUY2H536pB638SwaSg,2449
|
||||
werkzeug/contrib/iterio.py,sha256=UZeSPsHEIE1p-fH0XShk7xsJKBEMbLaTXNEOCe4twug,10811
|
||||
werkzeug/contrib/cache.py,sha256=QgVRvCzRxXd3HhUh_fZ1L1d9jwK5BW4LRkou-XAhncQ,24912
|
||||
werkzeug/contrib/securecookie.py,sha256=DHXl5WnT3R_nyUnNEiDvyJBl37dZen711_JGeg3fusA,12204
|
||||
werkzeug/contrib/lint.py,sha256=TsxFaxqzizN3ClRnv7lEeOILEjd2fYzXDqs6rIZu04M,12282
|
||||
werkzeug/contrib/profiler.py,sha256=Xot8BuERy7sXtQowEnmCjkv6KaAAzLDZ3J7KLnyElpI,4921
|
||||
werkzeug/contrib/wrappers.py,sha256=7zZmAFuNV-UEqehlfCBuB-BcVr6kH2HQZ2LLcXtmC1I,10331
|
||||
werkzeug/contrib/atom.py,sha256=Y-Iv5Dn0iZ8kCi9izPyM0Jo1p2Gc19r-8-3mkfpZ0vs,15498
|
||||
werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564
|
||||
werkzeug/contrib/sessions.py,sha256=wXPc0K-bI8VRuaeeTLvfxMk62FlCBb7-i2ACR1v4Ik0,12450
|
||||
werkzeug/debug/console.py,sha256=pzd4NmCmEUQ0Oxa1l1FSujBQUIv8U02CtMVnQmaEX5E,5557
|
||||
werkzeug/debug/repr.py,sha256=PqLZIJbL6FlAmgjmUeZ3b0J3FmM8Ocd3tng3o1EwBDU,9350
|
||||
werkzeug/debug/__init__.py,sha256=x5Wp4s4hMQK-uTsvHwmSDPxnROGaJ2LiX6XzX75Lin8,7800
|
||||
werkzeug/debug/tbtools.py,sha256=NbHBRvqsllp89aCFP9RELm-_xNd6R0seiVHx0Q85t7Y,16913
|
||||
werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191
|
||||
werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818
|
||||
werkzeug/debug/shared/debugger.js,sha256=YoeX5PlYuX1vSeufjCuCHXaoHQy2gpIm7FC0O9pq9PA,6345
|
||||
werkzeug/debug/shared/style.css,sha256=Yeipe9D_NlbxztN0dEazATYBLwGtkrGdbXtVYkJJK0U,6075
|
||||
werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507
|
||||
werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673
|
||||
werkzeug/debug/shared/jquery.js,sha256=UXNk8tRRYvtQN0N7W2y5U9ANmys7ebqH2f5X6m7mBww,78601
|
||||
werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220
|
||||
werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200
|
||||
Werkzeug-0.10.4.dist-info/DESCRIPTION.rst,sha256=5sTwZ_Sj5aeEN8mlcOdNJ_ng40HiGazGmILLyTMX8o0,1595
|
||||
Werkzeug-0.10.4.dist-info/metadata.json,sha256=D5TEHaD4DmqnhCQ00QghGhJerVwqszsbpOOzFS3Bo44,851
|
||||
Werkzeug-0.10.4.dist-info/RECORD,,
|
||||
Werkzeug-0.10.4.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9
|
||||
Werkzeug-0.10.4.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
|
||||
Werkzeug-0.10.4.dist-info/METADATA,sha256=S_im0ZAXS1A-ReOQFPve62nnhH0aTnewuDvnNltATd4,2301
|
||||
werkzeug/_reloader.pyc,,
|
||||
werkzeug/contrib/testtools.pyc,,
|
||||
werkzeug/formparser.pyc,,
|
||||
werkzeug/_compat.pyc,,
|
||||
werkzeug/posixemulation.pyc,,
|
||||
werkzeug/wsgi.pyc,,
|
||||
werkzeug/serving.pyc,,
|
||||
werkzeug/contrib/__init__.pyc,,
|
||||
werkzeug/contrib/iterio.pyc,,
|
||||
werkzeug/test.pyc,,
|
||||
werkzeug/contrib/limiter.pyc,,
|
||||
werkzeug/debug/tbtools.pyc,,
|
||||
werkzeug/contrib/sessions.pyc,,
|
||||
werkzeug/local.pyc,,
|
||||
werkzeug/utils.pyc,,
|
||||
werkzeug/_internal.pyc,,
|
||||
werkzeug/security.pyc,,
|
||||
werkzeug/contrib/cache.pyc,,
|
||||
werkzeug/contrib/securecookie.pyc,,
|
||||
werkzeug/script.pyc,,
|
||||
werkzeug/routing.pyc,,
|
||||
werkzeug/wrappers.pyc,,
|
||||
werkzeug/contrib/jsrouting.pyc,,
|
||||
werkzeug/contrib/fixers.pyc,,
|
||||
werkzeug/contrib/profiler.pyc,,
|
||||
werkzeug/debug/console.pyc,,
|
||||
werkzeug/debug/__init__.pyc,,
|
||||
werkzeug/datastructures.pyc,,
|
||||
werkzeug/http.pyc,,
|
||||
werkzeug/urls.pyc,,
|
||||
werkzeug/contrib/lint.pyc,,
|
||||
werkzeug/contrib/wrappers.pyc,,
|
||||
werkzeug/exceptions.pyc,,
|
||||
werkzeug/contrib/atom.pyc,,
|
||||
werkzeug/__init__.pyc,,
|
||||
werkzeug/testapp.pyc,,
|
||||
werkzeug/useragents.pyc,,
|
||||
werkzeug/debug/repr.pyc,,
|
6
lib/Werkzeug-0.10.4.dist-info/WHEEL
Normal file
6
lib/Werkzeug-0.10.4.dist-info/WHEEL
Normal file
|
@ -0,0 +1,6 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.24.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
1
lib/Werkzeug-0.10.4.dist-info/metadata.json
Normal file
1
lib/Werkzeug-0.10.4.dist-info/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"license": "BSD", "name": "Werkzeug", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "The Swiss Army knife of Python web development", "platform": "any", "version": "0.10.4", "extensions": {"python.details": {"project_urls": {"Home": "http://werkzeug.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"]}
|
1
lib/Werkzeug-0.10.4.dist-info/top_level.txt
Normal file
1
lib/Werkzeug-0.10.4.dist-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
werkzeug
|
5
lib/easy_install.py
Normal file
5
lib/easy_install.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
"""Run the EasyInstall command"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from setuptools.command.easy_install import main
|
||||
main()
|
50
lib/flask/__init__.py
Normal file
50
lib/flask/__init__.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask
|
||||
~~~~~
|
||||
|
||||
A microframework based on Werkzeug. It's extensively documented
|
||||
and follows best practice patterns.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
__version__ = '0.10.1'
|
||||
|
||||
# utilities we import from Werkzeug and Jinja2 that are unused
|
||||
# in the module but are exported as public interface.
|
||||
from werkzeug.exceptions import abort
|
||||
from werkzeug.utils import redirect
|
||||
from jinja2 import Markup, escape
|
||||
|
||||
from .app import Flask, Request, Response
|
||||
from .config import Config
|
||||
from .helpers import url_for, flash, send_file, send_from_directory, \
|
||||
get_flashed_messages, get_template_attribute, make_response, safe_join, \
|
||||
stream_with_context
|
||||
from .globals import current_app, g, request, session, _request_ctx_stack, \
|
||||
_app_ctx_stack
|
||||
from .ctx import has_request_context, has_app_context, \
|
||||
after_this_request, copy_current_request_context
|
||||
from .module import Module
|
||||
from .blueprints import Blueprint
|
||||
from .templating import render_template, render_template_string
|
||||
|
||||
# the signals
|
||||
from .signals import signals_available, template_rendered, request_started, \
|
||||
request_finished, got_request_exception, request_tearing_down, \
|
||||
appcontext_tearing_down, appcontext_pushed, \
|
||||
appcontext_popped, message_flashed
|
||||
|
||||
# We're not exposing the actual json module but a convenient wrapper around
|
||||
# it.
|
||||
from . import json
|
||||
|
||||
# This was the only thing that flask used to export at one point and it had
|
||||
# a more generic name.
|
||||
jsonify = json.jsonify
|
||||
|
||||
# backwards compat, goes away in 1.0
|
||||
from .sessions import SecureCookieSession as Session
|
||||
json_available = True
|
BIN
lib/flask/__init__.pyc
Normal file
BIN
lib/flask/__init__.pyc
Normal file
Binary file not shown.
73
lib/flask/_compat.py
Normal file
73
lib/flask/_compat.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask._compat
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Some py2/py3 compatibility support based on a stripped down
|
||||
version of six so we don't have to depend on a specific version
|
||||
of it.
|
||||
|
||||
:copyright: (c) 2013 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
_identity = lambda x: x
|
||||
|
||||
|
||||
if not PY2:
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
integer_types = (int, )
|
||||
|
||||
iterkeys = lambda d: iter(d.keys())
|
||||
itervalues = lambda d: iter(d.values())
|
||||
iteritems = lambda d: iter(d.items())
|
||||
|
||||
from io import StringIO
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
implements_to_string = _identity
|
||||
|
||||
else:
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
integer_types = (int, long)
|
||||
|
||||
iterkeys = lambda d: d.iterkeys()
|
||||
itervalues = lambda d: d.itervalues()
|
||||
iteritems = lambda d: d.iteritems()
|
||||
|
||||
from cStringIO import StringIO
|
||||
|
||||
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||
return cls
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
# This requires a bit of explanation: the basic idea is to make a
|
||||
# dummy metaclass for one level of class instantiation that replaces
|
||||
# itself with the actual metaclass. Because of internal type checks
|
||||
# we also need to make sure that we downgrade the custom metaclass
|
||||
# for one level to something closer to type (that's why __call__ and
|
||||
# __init__ comes back from type etc.).
|
||||
#
|
||||
# This has the advantage over six.with_metaclass in that it does not
|
||||
# introduce dummy classes into the final MRO.
|
||||
class metaclass(meta):
|
||||
__call__ = type.__call__
|
||||
__init__ = type.__init__
|
||||
def __new__(cls, name, this_bases, d):
|
||||
if this_bases is None:
|
||||
return type.__new__(cls, name, (), d)
|
||||
return meta(name, bases, d)
|
||||
return metaclass('temporary_class', None, {})
|
BIN
lib/flask/_compat.pyc
Normal file
BIN
lib/flask/_compat.pyc
Normal file
Binary file not shown.
1842
lib/flask/app.py
Normal file
1842
lib/flask/app.py
Normal file
File diff suppressed because it is too large
Load diff
BIN
lib/flask/app.pyc
Normal file
BIN
lib/flask/app.pyc
Normal file
Binary file not shown.
401
lib/flask/blueprints.py
Normal file
401
lib/flask/blueprints.py
Normal file
|
@ -0,0 +1,401 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.blueprints
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Blueprints are the recommended way to implement larger or more
|
||||
pluggable applications in Flask 0.7 and later.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from functools import update_wrapper
|
||||
|
||||
from .helpers import _PackageBoundObject, _endpoint_from_view_func
|
||||
|
||||
|
||||
class BlueprintSetupState(object):
|
||||
"""Temporary holder object for registering a blueprint with the
|
||||
application. An instance of this class is created by the
|
||||
:meth:`~flask.Blueprint.make_setup_state` method and later passed
|
||||
to all register callback functions.
|
||||
"""
|
||||
|
||||
def __init__(self, blueprint, app, options, first_registration):
|
||||
#: a reference to the current application
|
||||
self.app = app
|
||||
|
||||
#: a reference to the blueprint that created this setup state.
|
||||
self.blueprint = blueprint
|
||||
|
||||
#: a dictionary with all options that were passed to the
|
||||
#: :meth:`~flask.Flask.register_blueprint` method.
|
||||
self.options = options
|
||||
|
||||
#: as blueprints can be registered multiple times with the
|
||||
#: application and not everything wants to be registered
|
||||
#: multiple times on it, this attribute can be used to figure
|
||||
#: out if the blueprint was registered in the past already.
|
||||
self.first_registration = first_registration
|
||||
|
||||
subdomain = self.options.get('subdomain')
|
||||
if subdomain is None:
|
||||
subdomain = self.blueprint.subdomain
|
||||
|
||||
#: The subdomain that the blueprint should be active for, `None`
|
||||
#: otherwise.
|
||||
self.subdomain = subdomain
|
||||
|
||||
url_prefix = self.options.get('url_prefix')
|
||||
if url_prefix is None:
|
||||
url_prefix = self.blueprint.url_prefix
|
||||
|
||||
#: The prefix that should be used for all URLs defined on the
|
||||
#: blueprint.
|
||||
self.url_prefix = url_prefix
|
||||
|
||||
#: A dictionary with URL defaults that is added to each and every
|
||||
#: URL that was defined with the blueprint.
|
||||
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
||||
self.url_defaults.update(self.options.get('url_defaults', ()))
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
"""A helper method to register a rule (and optionally a view function)
|
||||
to the application. The endpoint is automatically prefixed with the
|
||||
blueprint's name.
|
||||
"""
|
||||
if self.url_prefix:
|
||||
rule = self.url_prefix + rule
|
||||
options.setdefault('subdomain', self.subdomain)
|
||||
if endpoint is None:
|
||||
endpoint = _endpoint_from_view_func(view_func)
|
||||
defaults = self.url_defaults
|
||||
if 'defaults' in options:
|
||||
defaults = dict(defaults, **options.pop('defaults'))
|
||||
self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
|
||||
view_func, defaults=defaults, **options)
|
||||
|
||||
|
||||
class Blueprint(_PackageBoundObject):
|
||||
"""Represents a blueprint. A blueprint is an object that records
|
||||
functions that will be called with the
|
||||
:class:`~flask.blueprint.BlueprintSetupState` later to register functions
|
||||
or other things on the main application. See :ref:`blueprints` for more
|
||||
information.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
|
||||
warn_on_modifications = False
|
||||
_got_registered_once = False
|
||||
|
||||
def __init__(self, name, import_name, static_folder=None,
|
||||
static_url_path=None, template_folder=None,
|
||||
url_prefix=None, subdomain=None, url_defaults=None):
|
||||
_PackageBoundObject.__init__(self, import_name, template_folder)
|
||||
self.name = name
|
||||
self.url_prefix = url_prefix
|
||||
self.subdomain = subdomain
|
||||
self.static_folder = static_folder
|
||||
self.static_url_path = static_url_path
|
||||
self.deferred_functions = []
|
||||
self.view_functions = {}
|
||||
if url_defaults is None:
|
||||
url_defaults = {}
|
||||
self.url_values_defaults = url_defaults
|
||||
|
||||
def record(self, func):
|
||||
"""Registers a function that is called when the blueprint is
|
||||
registered on the application. This function is called with the
|
||||
state as argument as returned by the :meth:`make_setup_state`
|
||||
method.
|
||||
"""
|
||||
if self._got_registered_once and self.warn_on_modifications:
|
||||
from warnings import warn
|
||||
warn(Warning('The blueprint was already registered once '
|
||||
'but is getting modified now. These changes '
|
||||
'will not show up.'))
|
||||
self.deferred_functions.append(func)
|
||||
|
||||
def record_once(self, func):
|
||||
"""Works like :meth:`record` but wraps the function in another
|
||||
function that will ensure the function is only called once. If the
|
||||
blueprint is registered a second time on the application, the
|
||||
function passed is not called.
|
||||
"""
|
||||
def wrapper(state):
|
||||
if state.first_registration:
|
||||
func(state)
|
||||
return self.record(update_wrapper(wrapper, func))
|
||||
|
||||
def make_setup_state(self, app, options, first_registration=False):
|
||||
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
||||
object that is later passed to the register callback functions.
|
||||
Subclasses can override this to return a subclass of the setup state.
|
||||
"""
|
||||
return BlueprintSetupState(self, app, options, first_registration)
|
||||
|
||||
def register(self, app, options, first_registration=False):
|
||||
"""Called by :meth:`Flask.register_blueprint` to register a blueprint
|
||||
on the application. This can be overridden to customize the register
|
||||
behavior. Keyword arguments from
|
||||
:func:`~flask.Flask.register_blueprint` are directly forwarded to this
|
||||
method in the `options` dictionary.
|
||||
"""
|
||||
self._got_registered_once = True
|
||||
state = self.make_setup_state(app, options, first_registration)
|
||||
if self.has_static_folder:
|
||||
state.add_url_rule(self.static_url_path + '/<path:filename>',
|
||||
view_func=self.send_static_file,
|
||||
endpoint='static')
|
||||
|
||||
for deferred in self.deferred_functions:
|
||||
deferred(state)
|
||||
|
||||
def route(self, rule, **options):
|
||||
"""Like :meth:`Flask.route` but for a blueprint. The endpoint for the
|
||||
:func:`url_for` function is prefixed with the name of the blueprint.
|
||||
"""
|
||||
def decorator(f):
|
||||
endpoint = options.pop("endpoint", f.__name__)
|
||||
self.add_url_rule(rule, endpoint, f, **options)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
||||
the :func:`url_for` function is prefixed with the name of the blueprint.
|
||||
"""
|
||||
if endpoint:
|
||||
assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
|
||||
self.record(lambda s:
|
||||
s.add_url_rule(rule, endpoint, view_func, **options))
|
||||
|
||||
def endpoint(self, endpoint):
|
||||
"""Like :meth:`Flask.endpoint` but for a blueprint. This does not
|
||||
prefix the endpoint with the blueprint name, this has to be done
|
||||
explicitly by the user of this method. If the endpoint is prefixed
|
||||
with a `.` it will be registered to the current blueprint, otherwise
|
||||
it's an application independent endpoint.
|
||||
"""
|
||||
def decorator(f):
|
||||
def register_endpoint(state):
|
||||
state.app.view_functions[endpoint] = f
|
||||
self.record_once(register_endpoint)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def app_template_filter(self, name=None):
|
||||
"""Register a custom template filter, available application wide. Like
|
||||
:meth:`Flask.template_filter` but for a blueprint.
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.add_app_template_filter(f, name=name)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_app_template_filter(self, f, name=None):
|
||||
"""Register a custom template filter, available application wide. Like
|
||||
:meth:`Flask.add_template_filter` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_filter` decorator.
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def register_template(state):
|
||||
state.app.jinja_env.filters[name or f.__name__] = f
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_test(self, name=None):
|
||||
"""Register a custom template test, available application wide. Like
|
||||
:meth:`Flask.template_test` but for a blueprint.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.add_app_template_test(f, name=name)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_app_template_test(self, f, name=None):
|
||||
"""Register a custom template test, available application wide. Like
|
||||
:meth:`Flask.add_template_test` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_test` decorator.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def register_template(state):
|
||||
state.app.jinja_env.tests[name or f.__name__] = f
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_global(self, name=None):
|
||||
"""Register a custom template global, available application wide. Like
|
||||
:meth:`Flask.template_global` but for a blueprint.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.add_app_template_global(f, name=name)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_app_template_global(self, f, name=None):
|
||||
"""Register a custom template global, available application wide. Like
|
||||
:meth:`Flask.add_template_global` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_global` decorator.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
def register_template(state):
|
||||
state.app.jinja_env.globals[name or f.__name__] = f
|
||||
self.record_once(register_template)
|
||||
|
||||
def before_request(self, f):
|
||||
"""Like :meth:`Flask.before_request` but for a blueprint. This function
|
||||
is only executed before each request that is handled by a function of
|
||||
that blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def before_app_request(self, f):
|
||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||
before each request, even if outside of a blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def before_app_first_request(self, f):
|
||||
"""Like :meth:`Flask.before_first_request`. Such a function is
|
||||
executed before the first request to the application.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
|
||||
return f
|
||||
|
||||
def after_request(self, f):
|
||||
"""Like :meth:`Flask.after_request` but for a blueprint. This function
|
||||
is only executed after each request that is handled by a function of
|
||||
that blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def after_app_request(self, f):
|
||||
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
||||
is executed after each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def teardown_request(self, f):
|
||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. This
|
||||
function is only executed when tearing down requests handled by a
|
||||
function of that blueprint. Teardown request functions are executed
|
||||
when the request context is popped, even when no actual request was
|
||||
performed.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.teardown_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def teardown_app_request(self, f):
|
||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
||||
function is executed when tearing down each request, even if outside of
|
||||
the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.teardown_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a blueprint. This
|
||||
function is only executed for requests handled by a blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def app_context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
||||
function is executed each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def app_errorhandler(self, code):
|
||||
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
||||
handler is used for all requests, even if outside of the blueprint.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def url_value_preprocessor(self, f):
|
||||
"""Registers a function as URL value preprocessor for this
|
||||
blueprint. It's called before the view functions are called and
|
||||
can modify the url values provided.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_value_preprocessors
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def url_defaults(self, f):
|
||||
"""Callback function for URL defaults for this blueprint. It's called
|
||||
with the endpoint and values and should update the values passed
|
||||
in place.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def app_url_value_preprocessor(self, f):
|
||||
"""Same as :meth:`url_value_preprocessor` but application wide.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_value_preprocessors
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def app_url_defaults(self, f):
|
||||
"""Same as :meth:`url_defaults` but application wide.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def errorhandler(self, code_or_exception):
|
||||
"""Registers an error handler that becomes active for this blueprint
|
||||
only. Please be aware that routing does not happen local to a
|
||||
blueprint so an error handler for 404 usually is not handled by
|
||||
a blueprint unless it is caused inside a view function. Another
|
||||
special case is the 500 internal server error which is always looked
|
||||
up from the application.
|
||||
|
||||
Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
|
||||
of the :class:`~flask.Flask` object.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.record_once(lambda s: s.app._register_error_handler(
|
||||
self.name, code_or_exception, f))
|
||||
return f
|
||||
return decorator
|
BIN
lib/flask/blueprints.pyc
Normal file
BIN
lib/flask/blueprints.pyc
Normal file
Binary file not shown.
168
lib/flask/config.py
Normal file
168
lib/flask/config.py
Normal file
|
@ -0,0 +1,168 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.config
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Implements the configuration related objects.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import imp
|
||||
import os
|
||||
import errno
|
||||
|
||||
from werkzeug.utils import import_string
|
||||
from ._compat import string_types
|
||||
|
||||
|
||||
class ConfigAttribute(object):
|
||||
"""Makes an attribute forward to the config"""
|
||||
|
||||
def __init__(self, name, get_converter=None):
|
||||
self.__name__ = name
|
||||
self.get_converter = get_converter
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
rv = obj.config[self.__name__]
|
||||
if self.get_converter is not None:
|
||||
rv = self.get_converter(rv)
|
||||
return rv
|
||||
|
||||
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 is basically 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 failure 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)
|
||||
return self.from_pyfile(rv, silent=silent)
|
||||
|
||||
def from_pyfile(self, filename, silent=False):
|
||||
"""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.
|
||||
:param silent: set to `True` if you want silent failure for missing
|
||||
files.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
`silent` parameter.
|
||||
"""
|
||||
filename = os.path.join(self.root_path, filename)
|
||||
d = imp.new_module('config')
|
||||
d.__file__ = filename
|
||||
try:
|
||||
with open(filename) as config_file:
|
||||
exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
|
||||
except IOError as e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||
return False
|
||||
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
|
||||
raise
|
||||
self.from_object(d)
|
||||
return True
|
||||
|
||||
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.
|
||||
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, string_types):
|
||||
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))
|
BIN
lib/flask/config.pyc
Normal file
BIN
lib/flask/config.pyc
Normal file
Binary file not shown.
394
lib/flask/ctx.py
Normal file
394
lib/flask/ctx.py
Normal file
|
@ -0,0 +1,394 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ctx
|
||||
~~~~~~~~~
|
||||
|
||||
Implements the objects required to keep the context.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import sys
|
||||
from functools import update_wrapper
|
||||
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||
from .module import blueprint_is_module
|
||||
from .signals import appcontext_pushed, appcontext_popped
|
||||
|
||||
|
||||
class _AppCtxGlobals(object):
|
||||
"""A plain object."""
|
||||
|
||||
def get(self, name, default=None):
|
||||
return self.__dict__.get(name, default)
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.__dict__
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__dict__)
|
||||
|
||||
def __repr__(self):
|
||||
top = _app_ctx_stack.top
|
||||
if top is not None:
|
||||
return '<flask.g of %r>' % top.app.name
|
||||
return object.__repr__(self)
|
||||
|
||||
|
||||
def after_this_request(f):
|
||||
"""Executes a function after this request. This is useful to modify
|
||||
response objects. The function is passed the response object and has
|
||||
to return the same or a new one.
|
||||
|
||||
Example::
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@after_this_request
|
||||
def add_header(response):
|
||||
response.headers['X-Foo'] = 'Parachute'
|
||||
return response
|
||||
return 'Hello World!'
|
||||
|
||||
This is more useful if a function other than the view function wants to
|
||||
modify a response. For instance think of a decorator that wants to add
|
||||
some headers without converting the return value into a response object.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
_request_ctx_stack.top._after_request_functions.append(f)
|
||||
return f
|
||||
|
||||
|
||||
def copy_current_request_context(f):
|
||||
"""A helper function that decorates a function to retain the current
|
||||
request context. This is useful when working with greenlets. The moment
|
||||
the function is decorated a copy of the request context is created and
|
||||
then pushed when the function is called.
|
||||
|
||||
Example::
|
||||
|
||||
import gevent
|
||||
from flask import copy_current_request_context
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@copy_current_request_context
|
||||
def do_some_work():
|
||||
# do some work here, it can access flask.request like you
|
||||
# would otherwise in the view function.
|
||||
...
|
||||
gevent.spawn(do_some_work)
|
||||
return 'Regular response'
|
||||
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
top = _request_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError('This decorator can only be used at local scopes '
|
||||
'when a request context is on the stack. For instance within '
|
||||
'view functions.')
|
||||
reqctx = top.copy()
|
||||
def wrapper(*args, **kwargs):
|
||||
with reqctx:
|
||||
return f(*args, **kwargs)
|
||||
return update_wrapper(wrapper, f)
|
||||
|
||||
|
||||
def has_request_context():
|
||||
"""If you have code that wants to test if a request context is there or
|
||||
not this function can be used. For instance, you may want to take advantage
|
||||
of request information if the request object is available, but fail
|
||||
silently if it is unavailable.
|
||||
|
||||
::
|
||||
|
||||
class User(db.Model):
|
||||
|
||||
def __init__(self, username, remote_addr=None):
|
||||
self.username = username
|
||||
if remote_addr is None and has_request_context():
|
||||
remote_addr = request.remote_addr
|
||||
self.remote_addr = remote_addr
|
||||
|
||||
Alternatively you can also just test any of the context bound objects
|
||||
(such as :class:`request` or :class:`g` for truthness)::
|
||||
|
||||
class User(db.Model):
|
||||
|
||||
def __init__(self, username, remote_addr=None):
|
||||
self.username = username
|
||||
if remote_addr is None and request:
|
||||
remote_addr = request.remote_addr
|
||||
self.remote_addr = remote_addr
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
return _request_ctx_stack.top is not None
|
||||
|
||||
|
||||
def has_app_context():
|
||||
"""Works like :func:`has_request_context` but for the application
|
||||
context. You can also just do a boolean check on the
|
||||
:data:`current_app` object instead.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
return _app_ctx_stack.top is not None
|
||||
|
||||
|
||||
class AppContext(object):
|
||||
"""The application context binds an application object implicitly
|
||||
to the current thread or greenlet, similar to how the
|
||||
:class:`RequestContext` binds request information. The application
|
||||
context is also implicitly created if a request context is created
|
||||
but the application is not on top of the individual application
|
||||
context.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.url_adapter = app.create_url_adapter(None)
|
||||
self.g = app.app_ctx_globals_class()
|
||||
|
||||
# Like request context, app contexts can be pushed multiple times
|
||||
# but there a basic "refcount" is enough to track them.
|
||||
self._refcnt = 0
|
||||
|
||||
def push(self):
|
||||
"""Binds the app context to the current context."""
|
||||
self._refcnt += 1
|
||||
_app_ctx_stack.push(self)
|
||||
appcontext_pushed.send(self.app)
|
||||
|
||||
def pop(self, exc=None):
|
||||
"""Pops the app context."""
|
||||
self._refcnt -= 1
|
||||
if self._refcnt <= 0:
|
||||
if exc is None:
|
||||
exc = sys.exc_info()[1]
|
||||
self.app.do_teardown_appcontext(exc)
|
||||
rv = _app_ctx_stack.pop()
|
||||
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
||||
% (rv, self)
|
||||
appcontext_popped.send(self.app)
|
||||
|
||||
def __enter__(self):
|
||||
self.push()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.pop(exc_value)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Do not attempt to use this class directly, instead use
|
||||
:meth:`~flask.Flask.test_request_context` and
|
||||
:meth:`~flask.Flask.request_context` to create this object.
|
||||
|
||||
When the request context is popped, it will evaluate all the
|
||||
functions registered on the application for teardown execution
|
||||
(:meth:`~flask.Flask.teardown_request`).
|
||||
|
||||
The request context is automatically popped at the end of the request
|
||||
for you. In debug mode the request context is kept around if
|
||||
exceptions happen so that interactive debuggers have a chance to
|
||||
introspect the data. With 0.4 this can also be forced for requests
|
||||
that did not fail and outside of `DEBUG` mode. By setting
|
||||
``'flask._preserve_context'`` to `True` on the WSGI environment the
|
||||
context will not pop itself at the end of the request. This is used by
|
||||
the :meth:`~flask.Flask.test_client` for example to implement the
|
||||
deferred cleanup functionality.
|
||||
|
||||
You might find this helpful for unittests where you need the
|
||||
information from the context local around for a little longer. Make
|
||||
sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
|
||||
that situation, otherwise your unittests will leak memory.
|
||||
"""
|
||||
|
||||
def __init__(self, app, environ, request=None):
|
||||
self.app = app
|
||||
if request is None:
|
||||
request = app.request_class(environ)
|
||||
self.request = request
|
||||
self.url_adapter = app.create_url_adapter(self.request)
|
||||
self.flashes = None
|
||||
self.session = None
|
||||
|
||||
# Request contexts can be pushed multiple times and interleaved with
|
||||
# other request contexts. Now only if the last level is popped we
|
||||
# get rid of them. Additionally if an application context is missing
|
||||
# one is created implicitly so for each level we add this information
|
||||
self._implicit_app_ctx_stack = []
|
||||
|
||||
# indicator if the context was preserved. Next time another context
|
||||
# is pushed the preserved context is popped.
|
||||
self.preserved = False
|
||||
|
||||
# remembers the exception for pop if there is one in case the context
|
||||
# preservation kicks in.
|
||||
self._preserved_exc = None
|
||||
|
||||
# Functions that should be executed after the request on the response
|
||||
# object. These will be called before the regular "after_request"
|
||||
# functions.
|
||||
self._after_request_functions = []
|
||||
|
||||
self.match_request()
|
||||
|
||||
# XXX: Support for deprecated functionality. This is going away with
|
||||
# Flask 1.0
|
||||
blueprint = self.request.blueprint
|
||||
if blueprint is not None:
|
||||
# better safe than sorry, we don't want to break code that
|
||||
# already worked
|
||||
bp = app.blueprints.get(blueprint)
|
||||
if bp is not None and blueprint_is_module(bp):
|
||||
self.request._is_old_module = True
|
||||
|
||||
def _get_g(self):
|
||||
return _app_ctx_stack.top.g
|
||||
def _set_g(self, value):
|
||||
_app_ctx_stack.top.g = value
|
||||
g = property(_get_g, _set_g)
|
||||
del _get_g, _set_g
|
||||
|
||||
def copy(self):
|
||||
"""Creates a copy of this request context with the same request object.
|
||||
This can be used to move a request context to a different greenlet.
|
||||
Because the actual request object is the same this cannot be used to
|
||||
move a request context to a different thread unless access to the
|
||||
request object is locked.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
return self.__class__(self.app,
|
||||
environ=self.request.environ,
|
||||
request=self.request
|
||||
)
|
||||
|
||||
def match_request(self):
|
||||
"""Can be overridden by a subclass to hook into the matching
|
||||
of the request.
|
||||
"""
|
||||
try:
|
||||
url_rule, self.request.view_args = \
|
||||
self.url_adapter.match(return_rule=True)
|
||||
self.request.url_rule = url_rule
|
||||
except HTTPException as e:
|
||||
self.request.routing_exception = e
|
||||
|
||||
def push(self):
|
||||
"""Binds the request context to the current context."""
|
||||
# If an exception occurs in debug mode or if context preservation is
|
||||
# activated under exception situations exactly one context stays
|
||||
# on the stack. The rationale is that you want to access that
|
||||
# information under debug situations. However if someone forgets to
|
||||
# pop that context again we want to make sure that on the next push
|
||||
# it's invalidated, otherwise we run at risk that something leaks
|
||||
# memory. This is usually only a problem in testsuite since this
|
||||
# functionality is not active in production environments.
|
||||
top = _request_ctx_stack.top
|
||||
if top is not None and top.preserved:
|
||||
top.pop(top._preserved_exc)
|
||||
|
||||
# Before we push the request context we have to ensure that there
|
||||
# is an application context.
|
||||
app_ctx = _app_ctx_stack.top
|
||||
if app_ctx is None or app_ctx.app != self.app:
|
||||
app_ctx = self.app.app_context()
|
||||
app_ctx.push()
|
||||
self._implicit_app_ctx_stack.append(app_ctx)
|
||||
else:
|
||||
self._implicit_app_ctx_stack.append(None)
|
||||
|
||||
_request_ctx_stack.push(self)
|
||||
|
||||
# Open the session at the moment that the request context is
|
||||
# available. This allows a custom open_session method to use the
|
||||
# request context (e.g. code that access database information
|
||||
# stored on `g` instead of the appcontext).
|
||||
self.session = self.app.open_session(self.request)
|
||||
if self.session is None:
|
||||
self.session = self.app.make_null_session()
|
||||
|
||||
def pop(self, exc=None):
|
||||
"""Pops the request context and unbinds it by doing that. This will
|
||||
also trigger the execution of functions registered by the
|
||||
:meth:`~flask.Flask.teardown_request` decorator.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
Added the `exc` argument.
|
||||
"""
|
||||
app_ctx = self._implicit_app_ctx_stack.pop()
|
||||
|
||||
clear_request = False
|
||||
if not self._implicit_app_ctx_stack:
|
||||
self.preserved = False
|
||||
self._preserved_exc = None
|
||||
if exc is None:
|
||||
exc = sys.exc_info()[1]
|
||||
self.app.do_teardown_request(exc)
|
||||
|
||||
# If this interpreter supports clearing the exception information
|
||||
# we do that now. This will only go into effect on Python 2.x,
|
||||
# on 3.x it disappears automatically at the end of the exception
|
||||
# stack.
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
sys.exc_clear()
|
||||
|
||||
request_close = getattr(self.request, 'close', None)
|
||||
if request_close is not None:
|
||||
request_close()
|
||||
clear_request = True
|
||||
|
||||
rv = _request_ctx_stack.pop()
|
||||
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
|
||||
% (rv, self)
|
||||
|
||||
# get rid of circular dependencies at the end of the request
|
||||
# so that we don't require the GC to be active.
|
||||
if clear_request:
|
||||
rv.request.environ['werkzeug.request'] = None
|
||||
|
||||
# Get rid of the app as well if necessary.
|
||||
if app_ctx is not None:
|
||||
app_ctx.pop(exc)
|
||||
|
||||
def auto_pop(self, exc):
|
||||
if self.request.environ.get('flask._preserve_context') or \
|
||||
(exc is not None and self.app.preserve_context_on_exception):
|
||||
self.preserved = True
|
||||
self._preserved_exc = exc
|
||||
else:
|
||||
self.pop(exc)
|
||||
|
||||
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.
|
||||
self.auto_pop(exc_value)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s \'%s\' [%s] of %s>' % (
|
||||
self.__class__.__name__,
|
||||
self.request.url,
|
||||
self.request.method,
|
||||
self.app.name,
|
||||
)
|
BIN
lib/flask/ctx.pyc
Normal file
BIN
lib/flask/ctx.pyc
Normal file
Binary file not shown.
87
lib/flask/debughelpers.py
Normal file
87
lib/flask/debughelpers.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.debughelpers
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Various helpers to make the development experience better.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from ._compat import implements_to_string
|
||||
|
||||
|
||||
class UnexpectedUnicodeError(AssertionError, UnicodeError):
|
||||
"""Raised in places where we want some better error reporting for
|
||||
unexpected unicode or binary data.
|
||||
"""
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class DebugFilesKeyError(KeyError, AssertionError):
|
||||
"""Raised from request.files during debugging. The idea is that it can
|
||||
provide a better error message than just a generic KeyError/BadRequest.
|
||||
"""
|
||||
|
||||
def __init__(self, request, key):
|
||||
form_matches = request.form.getlist(key)
|
||||
buf = ['You tried to access the file "%s" in the request.files '
|
||||
'dictionary but it does not exist. The mimetype for the request '
|
||||
'is "%s" instead of "multipart/form-data" which means that no '
|
||||
'file contents were transmitted. To fix this error you should '
|
||||
'provide enctype="multipart/form-data" in your form.' %
|
||||
(key, request.mimetype)]
|
||||
if form_matches:
|
||||
buf.append('\n\nThe browser instead transmitted some file names. '
|
||||
'This was submitted: %s' % ', '.join('"%s"' % x
|
||||
for x in form_matches))
|
||||
self.msg = ''.join(buf)
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
|
||||
class FormDataRoutingRedirect(AssertionError):
|
||||
"""This exception is raised by Flask in debug mode if it detects a
|
||||
redirect caused by the routing system when the request method is not
|
||||
GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
|
||||
"""
|
||||
|
||||
def __init__(self, request):
|
||||
exc = request.routing_exception
|
||||
buf = ['A request was sent to this URL (%s) but a redirect was '
|
||||
'issued automatically by the routing system to "%s".'
|
||||
% (request.url, exc.new_url)]
|
||||
|
||||
# In case just a slash was appended we can be extra helpful
|
||||
if request.base_url + '/' == exc.new_url.split('?')[0]:
|
||||
buf.append(' The URL was defined with a trailing slash so '
|
||||
'Flask will automatically redirect to the URL '
|
||||
'with the trailing slash if it was accessed '
|
||||
'without one.')
|
||||
|
||||
buf.append(' Make sure to directly send your %s-request to this URL '
|
||||
'since we can\'t make browsers or HTTP clients redirect '
|
||||
'with form data reliably or without user interaction.' %
|
||||
request.method)
|
||||
buf.append('\n\nNote: this exception is only raised in debug mode')
|
||||
AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
|
||||
|
||||
|
||||
def attach_enctype_error_multidict(request):
|
||||
"""Since Flask 0.8 we're monkeypatching the files object in case a
|
||||
request is detected that does not use multipart form data but the files
|
||||
object is accessed.
|
||||
"""
|
||||
oldcls = request.files.__class__
|
||||
class newcls(oldcls):
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return oldcls.__getitem__(self, key)
|
||||
except KeyError as e:
|
||||
if key not in request.form:
|
||||
raise
|
||||
raise DebugFilesKeyError(request, key)
|
||||
newcls.__name__ = oldcls.__name__
|
||||
newcls.__module__ = oldcls.__module__
|
||||
request.files.__class__ = newcls
|
BIN
lib/flask/debughelpers.pyc
Normal file
BIN
lib/flask/debughelpers.pyc
Normal file
Binary file not shown.
29
lib/flask/ext/__init__.py
Normal file
29
lib/flask/ext/__init__.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext
|
||||
~~~~~~~~~
|
||||
|
||||
Redirect imports for extensions. This module basically makes it possible
|
||||
for us to transition from flaskext.foo to flask_foo without having to
|
||||
force all extensions to upgrade at the same time.
|
||||
|
||||
When a user does ``from flask.ext.foo import bar`` it will attempt to
|
||||
import ``from flask_foo import bar`` first and when that fails it will
|
||||
try to import ``from flaskext.foo import bar``.
|
||||
|
||||
We're switching from namespace packages because it was just too painful for
|
||||
everybody involved.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
def setup():
|
||||
from ..exthook import ExtensionImporter
|
||||
importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__)
|
||||
importer.install()
|
||||
|
||||
|
||||
setup()
|
||||
del setup
|
BIN
lib/flask/ext/__init__.pyc
Normal file
BIN
lib/flask/ext/__init__.pyc
Normal file
Binary file not shown.
120
lib/flask/exthook.py
Normal file
120
lib/flask/exthook.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.exthook
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Redirect imports for extensions. This module basically makes it possible
|
||||
for us to transition from flaskext.foo to flask_foo without having to
|
||||
force all extensions to upgrade at the same time.
|
||||
|
||||
When a user does ``from flask.ext.foo import bar`` it will attempt to
|
||||
import ``from flask_foo import bar`` first and when that fails it will
|
||||
try to import ``from flaskext.foo import bar``.
|
||||
|
||||
We're switching from namespace packages because it was just too painful for
|
||||
everybody involved.
|
||||
|
||||
This is used by `flask.ext`.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
from ._compat import reraise
|
||||
|
||||
|
||||
class ExtensionImporter(object):
|
||||
"""This importer redirects imports from this submodule to other locations.
|
||||
This makes it possible to transition from the old flaskext.name to the
|
||||
newer flask_name without people having a hard time.
|
||||
"""
|
||||
|
||||
def __init__(self, module_choices, wrapper_module):
|
||||
self.module_choices = module_choices
|
||||
self.wrapper_module = wrapper_module
|
||||
self.prefix = wrapper_module + '.'
|
||||
self.prefix_cutoff = wrapper_module.count('.') + 1
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__.__module__ == other.__class__.__module__ and \
|
||||
self.__class__.__name__ == other.__class__.__name__ and \
|
||||
self.wrapper_module == other.wrapper_module and \
|
||||
self.module_choices == other.module_choices
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def install(self):
|
||||
sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname.startswith(self.prefix):
|
||||
return self
|
||||
|
||||
def load_module(self, fullname):
|
||||
if fullname in sys.modules:
|
||||
return sys.modules[fullname]
|
||||
modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]
|
||||
for path in self.module_choices:
|
||||
realname = path % modname
|
||||
try:
|
||||
__import__(realname)
|
||||
except ImportError:
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
# since we only establish the entry in sys.modules at the
|
||||
# very this seems to be redundant, but if recursive imports
|
||||
# happen we will call into the move import a second time.
|
||||
# On the second invocation we still don't have an entry for
|
||||
# fullname in sys.modules, but we will end up with the same
|
||||
# fake module name and that import will succeed since this
|
||||
# one already has a temporary entry in the modules dict.
|
||||
# Since this one "succeeded" temporarily that second
|
||||
# invocation now will have created a fullname entry in
|
||||
# sys.modules which we have to kill.
|
||||
sys.modules.pop(fullname, None)
|
||||
|
||||
# If it's an important traceback we reraise it, otherwise
|
||||
# we swallow it and try the next choice. The skipped frame
|
||||
# is the one from __import__ above which we don't care about
|
||||
if self.is_important_traceback(realname, tb):
|
||||
reraise(exc_type, exc_value, tb.tb_next)
|
||||
continue
|
||||
module = sys.modules[fullname] = sys.modules[realname]
|
||||
if '.' not in modname:
|
||||
setattr(sys.modules[self.wrapper_module], modname, module)
|
||||
return module
|
||||
raise ImportError('No module named %s' % fullname)
|
||||
|
||||
def is_important_traceback(self, important_module, tb):
|
||||
"""Walks a traceback's frames and checks if any of the frames
|
||||
originated in the given important module. If that is the case then we
|
||||
were able to import the module itself but apparently something went
|
||||
wrong when the module was imported. (Eg: import of an import failed).
|
||||
"""
|
||||
while tb is not None:
|
||||
if self.is_important_frame(important_module, tb):
|
||||
return True
|
||||
tb = tb.tb_next
|
||||
return False
|
||||
|
||||
def is_important_frame(self, important_module, tb):
|
||||
"""Checks a single frame if it's important."""
|
||||
g = tb.tb_frame.f_globals
|
||||
if '__name__' not in g:
|
||||
return False
|
||||
|
||||
module_name = g['__name__']
|
||||
|
||||
# Python 2.7 Behavior. Modules are cleaned up late so the
|
||||
# name shows up properly here. Success!
|
||||
if module_name == important_module:
|
||||
return True
|
||||
|
||||
# Some python versions will will clean up modules so early that the
|
||||
# module name at that point is no longer set. Try guessing from
|
||||
# the filename then.
|
||||
filename = os.path.abspath(tb.tb_frame.f_code.co_filename)
|
||||
test_string = os.path.sep + important_module.replace('.', os.path.sep)
|
||||
return test_string + '.py' in filename or \
|
||||
test_string + os.path.sep + '__init__.py' in filename
|
BIN
lib/flask/exthook.pyc
Normal file
BIN
lib/flask/exthook.pyc
Normal file
Binary file not shown.
44
lib/flask/globals.py
Normal file
44
lib/flask/globals.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.globals
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Defines all the global objects that are proxies to the current
|
||||
active context.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
from werkzeug.local import LocalStack, LocalProxy
|
||||
|
||||
|
||||
def _lookup_req_object(name):
|
||||
top = _request_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError('working outside of request context')
|
||||
return getattr(top, name)
|
||||
|
||||
|
||||
def _lookup_app_object(name):
|
||||
top = _app_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError('working outside of application context')
|
||||
return getattr(top, name)
|
||||
|
||||
|
||||
def _find_app():
|
||||
top = _app_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError('working outside of application context')
|
||||
return top.app
|
||||
|
||||
|
||||
# context locals
|
||||
_request_ctx_stack = LocalStack()
|
||||
_app_ctx_stack = LocalStack()
|
||||
current_app = LocalProxy(_find_app)
|
||||
request = LocalProxy(partial(_lookup_req_object, 'request'))
|
||||
session = LocalProxy(partial(_lookup_req_object, 'session'))
|
||||
g = LocalProxy(partial(_lookup_app_object, 'g'))
|
BIN
lib/flask/globals.pyc
Normal file
BIN
lib/flask/globals.pyc
Normal file
Binary file not shown.
849
lib/flask/helpers.py
Normal file
849
lib/flask/helpers.py
Normal file
|
@ -0,0 +1,849 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.helpers
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements various helpers.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pkgutil
|
||||
import posixpath
|
||||
import mimetypes
|
||||
from time import time
|
||||
from zlib import adler32
|
||||
from threading import RLock
|
||||
from werkzeug.routing import BuildError
|
||||
from functools import update_wrapper
|
||||
|
||||
try:
|
||||
from werkzeug.urls import url_quote
|
||||
except ImportError:
|
||||
from urlparse import quote as url_quote
|
||||
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
# this was moved in 0.7
|
||||
try:
|
||||
from werkzeug.wsgi import wrap_file
|
||||
except ImportError:
|
||||
from werkzeug.utils import wrap_file
|
||||
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
from .signals import message_flashed
|
||||
from .globals import session, _request_ctx_stack, _app_ctx_stack, \
|
||||
current_app, request
|
||||
from ._compat import string_types, text_type
|
||||
|
||||
|
||||
# sentinel
|
||||
_missing = object()
|
||||
|
||||
|
||||
# what separators does this operating system provide that are not a slash?
|
||||
# this is used by the send_from_directory function to ensure that nobody is
|
||||
# able to access files from outside the filesystem.
|
||||
_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
|
||||
if sep not in (None, '/'))
|
||||
|
||||
|
||||
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 stream_with_context(generator_or_function):
|
||||
"""Request contexts disappear when the response is started on the server.
|
||||
This is done for efficiency reasons and to make it less likely to encounter
|
||||
memory leaks with badly written WSGI middlewares. The downside is that if
|
||||
you are using streamed responses, the generator cannot access request bound
|
||||
information any more.
|
||||
|
||||
This function however can help you keep the context around for longer::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
@stream_with_context
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(generate())
|
||||
|
||||
Alternatively it can also be used around a specific generator::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(stream_with_context(generate()))
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
try:
|
||||
gen = iter(generator_or_function)
|
||||
except TypeError:
|
||||
def decorator(*args, **kwargs):
|
||||
gen = generator_or_function()
|
||||
return stream_with_context(gen)
|
||||
return update_wrapper(decorator, generator_or_function)
|
||||
|
||||
def generator():
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is None:
|
||||
raise RuntimeError('Attempted to stream with context but '
|
||||
'there was no context in the first place to keep around.')
|
||||
with ctx:
|
||||
# Dummy sentinel. Has to be inside the context block or we're
|
||||
# not actually keeping the context around.
|
||||
yield None
|
||||
|
||||
# The try/finally is here so that if someone passes a WSGI level
|
||||
# iterator in we're still running the cleanup logic. Generators
|
||||
# don't need that because they are closed on their destruction
|
||||
# automatically.
|
||||
try:
|
||||
for item in gen:
|
||||
yield item
|
||||
finally:
|
||||
if hasattr(gen, 'close'):
|
||||
gen.close()
|
||||
|
||||
# The trick is to start the generator. Then the code execution runs until
|
||||
# the first dummy None is yielded at which point the context was already
|
||||
# pushed. This item is discarded. Then when the iteration continues the
|
||||
# real generator is executed.
|
||||
wrapped_g = generator()
|
||||
next(wrapped_g)
|
||||
return wrapped_g
|
||||
|
||||
|
||||
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)
|
||||
|
||||
The other use case of this function is to force the return value of a
|
||||
view function into a response which is helpful with view
|
||||
decorators::
|
||||
|
||||
response = make_response(view_function())
|
||||
response.headers['X-Parachutes'] = 'parachutes are cool'
|
||||
|
||||
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.
|
||||
|
||||
Variable arguments that are unknown to the target endpoint are appended
|
||||
to the generated URL as query arguments. If the value of a query argument
|
||||
is `None`, the whole pair is skipped. In case blueprints are active
|
||||
you can shortcut references to the same blueprint by prefixing the
|
||||
local endpoint with a dot (``.``).
|
||||
|
||||
This will reference the index function local to the current blueprint::
|
||||
|
||||
url_for('.index')
|
||||
|
||||
For more information, head over to the :ref:`Quickstart <url-building>`.
|
||||
|
||||
To integrate applications, :class:`Flask` has a hook to intercept URL build
|
||||
errors through :attr:`Flask.build_error_handler`. The `url_for` function
|
||||
results in a :exc:`~werkzeug.routing.BuildError` when the current app does
|
||||
not have a URL for the given endpoint and values. When it does, the
|
||||
:data:`~flask.current_app` calls its :attr:`~Flask.build_error_handler` if
|
||||
it is not `None`, which can return a string to use as the result of
|
||||
`url_for` (instead of `url_for`'s default to raise the
|
||||
:exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
|
||||
An example::
|
||||
|
||||
def external_url_handler(error, endpoint, **values):
|
||||
"Looks up an external URL when `url_for` cannot build a URL."
|
||||
# This is an example of hooking the build_error_handler.
|
||||
# Here, lookup_url is some utility function you've built
|
||||
# which looks up the endpoint in some external URL registry.
|
||||
url = lookup_url(endpoint, **values)
|
||||
if url is None:
|
||||
# External lookup did not have a URL.
|
||||
# Re-raise the BuildError, in context of original traceback.
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
if exc_value is error:
|
||||
raise exc_type, exc_value, tb
|
||||
else:
|
||||
raise error
|
||||
# url_for will use this result, instead of raising BuildError.
|
||||
return url
|
||||
|
||||
app.build_error_handler = external_url_handler
|
||||
|
||||
Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
|
||||
`endpoint` and `**values` are the arguments passed into `url_for`. Note
|
||||
that this is for building URLs outside the current application, and not for
|
||||
handling 404 NotFound errors.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
The `_scheme` parameter was added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
The `_anchor` and `_method` parameters were added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
Calls :meth:`Flask.handle_build_error` on
|
||||
:exc:`~werkzeug.routing.BuildError`.
|
||||
|
||||
: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. Server
|
||||
address can be changed via `SERVER_NAME` configuration variable which
|
||||
defaults to `localhost`.
|
||||
:param _scheme: a string specifying the desired URL scheme. The `_external`
|
||||
parameter must be set to `True` or a `ValueError` is raised.
|
||||
:param _anchor: if provided this is added as anchor to the URL.
|
||||
:param _method: if provided this explicitly specifies an HTTP method.
|
||||
"""
|
||||
appctx = _app_ctx_stack.top
|
||||
reqctx = _request_ctx_stack.top
|
||||
if appctx is None:
|
||||
raise RuntimeError('Attempted to generate a URL without the '
|
||||
'application context being pushed. This has to be '
|
||||
'executed when application context is available.')
|
||||
|
||||
# If request specific information is available we have some extra
|
||||
# features that support "relative" urls.
|
||||
if reqctx is not None:
|
||||
url_adapter = reqctx.url_adapter
|
||||
blueprint_name = request.blueprint
|
||||
if not reqctx.request._is_old_module:
|
||||
if endpoint[:1] == '.':
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + endpoint
|
||||
else:
|
||||
endpoint = endpoint[1:]
|
||||
else:
|
||||
# TODO: get rid of this deprecated functionality in 1.0
|
||||
if '.' not in endpoint:
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + '.' + endpoint
|
||||
elif endpoint.startswith('.'):
|
||||
endpoint = endpoint[1:]
|
||||
external = values.pop('_external', False)
|
||||
|
||||
# Otherwise go with the url adapter from the appctx and make
|
||||
# the urls external by default.
|
||||
else:
|
||||
url_adapter = appctx.url_adapter
|
||||
if url_adapter is None:
|
||||
raise RuntimeError('Application was not able to create a URL '
|
||||
'adapter for request independent URL generation. '
|
||||
'You might be able to fix this by setting '
|
||||
'the SERVER_NAME config variable.')
|
||||
external = values.pop('_external', True)
|
||||
|
||||
anchor = values.pop('_anchor', None)
|
||||
method = values.pop('_method', None)
|
||||
scheme = values.pop('_scheme', None)
|
||||
appctx.app.inject_url_defaults(endpoint, values)
|
||||
|
||||
if scheme is not None:
|
||||
if not external:
|
||||
raise ValueError('When specifying _scheme, _external must be True')
|
||||
url_adapter.url_scheme = scheme
|
||||
|
||||
try:
|
||||
rv = url_adapter.build(endpoint, values, method=method,
|
||||
force_external=external)
|
||||
except BuildError as error:
|
||||
# We need to inject the values again so that the app callback can
|
||||
# deal with that sort of stuff.
|
||||
values['_external'] = external
|
||||
values['_anchor'] = anchor
|
||||
values['_method'] = method
|
||||
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||
|
||||
if anchor is not None:
|
||||
rv += '#' + url_quote(anchor)
|
||||
return rv
|
||||
|
||||
|
||||
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 access
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
# Original implementation:
|
||||
#
|
||||
# session.setdefault('_flashes', []).append((category, message))
|
||||
#
|
||||
# This assumed that changes made to mutable structures in the session are
|
||||
# are always in sync with the sess on object, which is not true for session
|
||||
# implementations that use external storage for keeping their keys/values.
|
||||
flashes = session.get('_flashes', [])
|
||||
flashes.append((category, message))
|
||||
session['_flashes'] = flashes
|
||||
message_flashed.send(current_app._get_current_object(),
|
||||
message=message, category=category)
|
||||
|
||||
|
||||
def get_flashed_messages(with_categories=False, category_filter=[]):
|
||||
"""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.
|
||||
|
||||
Filter the flashed messages to one or more categories by providing those
|
||||
categories in `category_filter`. This allows rendering categories in
|
||||
separate html blocks. The `with_categories` and `category_filter`
|
||||
arguments are distinct:
|
||||
|
||||
* `with_categories` controls whether categories are returned with message
|
||||
text (`True` gives a tuple, where `False` gives just the message text).
|
||||
* `category_filter` filters the messages down to only those matching the
|
||||
provided categories.
|
||||
|
||||
See :ref:`message-flashing-pattern` for examples.
|
||||
|
||||
.. versionchanged:: 0.3
|
||||
`with_categories` parameter added.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
`category_filter` parameter added.
|
||||
|
||||
:param with_categories: set to `True` to also receive categories.
|
||||
:param category_filter: whitelist of categories to limit return values
|
||||
"""
|
||||
flashes = _request_ctx_stack.top.flashes
|
||||
if flashes is None:
|
||||
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
|
||||
if '_flashes' in session else []
|
||||
if category_filter:
|
||||
flashes = list(filter(lambda f: f[0] in category_filter, 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=None, 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 send certain files as attachment (HTML for instance). The mimetype
|
||||
guessing requires a `filename` or an `attachment_filename` to be
|
||||
provided.
|
||||
|
||||
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 behavior is now to attach etags.
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
mimetype guessing and etag support for file objects was
|
||||
deprecated because it was unreliable. Pass a filename if you are
|
||||
able to, otherwise attach an etag yourself. This functionality
|
||||
will be removed in Flask 1.0
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
cache_timeout pulls its default from application config, when None.
|
||||
|
||||
: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. Make sure
|
||||
that the file pointer is positioned at the start
|
||||
of data to send before calling :func:`send_file`.
|
||||
: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.
|
||||
:param add_etags: set to `False` to disable attaching of etags.
|
||||
:param conditional: set to `True` to enable conditional responses.
|
||||
|
||||
:param cache_timeout: the timeout in seconds for the headers. When `None`
|
||||
(default), this value is set by
|
||||
:meth:`~Flask.get_send_file_max_age` of
|
||||
:data:`~flask.current_app`.
|
||||
"""
|
||||
mtime = None
|
||||
if isinstance(filename_or_fp, string_types):
|
||||
filename = filename_or_fp
|
||||
file = None
|
||||
else:
|
||||
from warnings import warn
|
||||
file = filename_or_fp
|
||||
filename = getattr(file, 'name', None)
|
||||
|
||||
# XXX: this behavior is now deprecated because it was unreliable.
|
||||
# removed in Flask 1.0
|
||||
if not attachment_filename and not mimetype \
|
||||
and isinstance(filename, string_types):
|
||||
warn(DeprecationWarning('The filename support for file objects '
|
||||
'passed to send_file is now deprecated. Pass an '
|
||||
'attach_filename if you want mimetypes to be guessed.'),
|
||||
stacklevel=2)
|
||||
if add_etags:
|
||||
warn(DeprecationWarning('In future flask releases etags will no '
|
||||
'longer be generated for file objects passed to the send_file '
|
||||
'function because this behavior was unreliable. Pass '
|
||||
'filenames instead if possible, otherwise attach an etag '
|
||||
'yourself based on another value'), stacklevel=2)
|
||||
|
||||
if filename is not None:
|
||||
if not os.path.isabs(filename):
|
||||
filename = os.path.join(current_app.root_path, filename)
|
||||
if mimetype is None and (filename or attachment_filename):
|
||||
mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
|
||||
if mimetype is None:
|
||||
mimetype = 'application/octet-stream'
|
||||
|
||||
headers = Headers()
|
||||
if as_attachment:
|
||||
if attachment_filename is None:
|
||||
if filename is None:
|
||||
raise TypeError('filename unavailable, required for '
|
||||
'sending as attachment')
|
||||
attachment_filename = os.path.basename(filename)
|
||||
headers.add('Content-Disposition', 'attachment',
|
||||
filename=attachment_filename)
|
||||
|
||||
if current_app.use_x_sendfile and filename:
|
||||
if file is not None:
|
||||
file.close()
|
||||
headers['X-Sendfile'] = filename
|
||||
headers['Content-Length'] = os.path.getsize(filename)
|
||||
data = None
|
||||
else:
|
||||
if file is None:
|
||||
file = open(filename, 'rb')
|
||||
mtime = os.path.getmtime(filename)
|
||||
headers['Content-Length'] = os.path.getsize(filename)
|
||||
data = wrap_file(request.environ, file)
|
||||
|
||||
rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
|
||||
direct_passthrough=True)
|
||||
|
||||
# if we know the file modification date, we can store it as the
|
||||
# the time of the last modification.
|
||||
if mtime is not None:
|
||||
rv.last_modified = int(mtime)
|
||||
|
||||
rv.cache_control.public = True
|
||||
if cache_timeout is None:
|
||||
cache_timeout = current_app.get_send_file_max_age(filename)
|
||||
if cache_timeout is not None:
|
||||
rv.cache_control.max_age = cache_timeout
|
||||
rv.expires = int(time() + cache_timeout)
|
||||
|
||||
if add_etags and filename is not None:
|
||||
rv.set_etag('flask-%s-%s-%s' % (
|
||||
os.path.getmtime(filename),
|
||||
os.path.getsize(filename),
|
||||
adler32(
|
||||
filename.encode('utf-8') if isinstance(filename, text_type)
|
||||
else filename
|
||||
) & 0xffffffff
|
||||
))
|
||||
if conditional:
|
||||
rv = rv.make_conditional(request)
|
||||
# make sure we don't send x-sendfile for servers that
|
||||
# ignore the 304 status code for x-sendfile.
|
||||
if rv.status_code == 304:
|
||||
rv.headers.pop('x-sendfile', None)
|
||||
return rv
|
||||
|
||||
|
||||
def safe_join(directory, filename):
|
||||
"""Safely join `directory` and `filename`.
|
||||
|
||||
Example usage::
|
||||
|
||||
@app.route('/wiki/<path:filename>')
|
||||
def wiki_page(filename):
|
||||
filename = safe_join(app.config['WIKI_FOLDER'], filename)
|
||||
with open(filename, 'rb') as fd:
|
||||
content = fd.read() # Read and process the file content...
|
||||
|
||||
:param directory: the base directory.
|
||||
:param filename: the untrusted filename relative to that directory.
|
||||
:raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path
|
||||
would fall out of `directory`.
|
||||
"""
|
||||
filename = posixpath.normpath(filename)
|
||||
for sep in _os_alt_seps:
|
||||
if sep in filename:
|
||||
raise NotFound()
|
||||
if os.path.isabs(filename) or \
|
||||
filename == '..' or \
|
||||
filename.startswith('../'):
|
||||
raise NotFound()
|
||||
return os.path.join(directory, filename)
|
||||
|
||||
|
||||
def send_from_directory(directory, filename, **options):
|
||||
"""Send a file from a given directory with :func:`send_file`. This
|
||||
is a secure way to quickly expose static files from an upload folder
|
||||
or something similar.
|
||||
|
||||
Example usage::
|
||||
|
||||
@app.route('/uploads/<path:filename>')
|
||||
def download_file(filename):
|
||||
return send_from_directory(app.config['UPLOAD_FOLDER'],
|
||||
filename, as_attachment=True)
|
||||
|
||||
.. admonition:: Sending files and Performance
|
||||
|
||||
It is strongly recommended to activate either `X-Sendfile` support in
|
||||
your webserver or (if no authentication happens) to tell the webserver
|
||||
to serve files for the given path on its own without calling into the
|
||||
web application for improved performance.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
:param directory: the directory where all the files are stored.
|
||||
:param filename: the filename relative to that directory to
|
||||
download.
|
||||
:param options: optional keyword arguments that are directly
|
||||
forwarded to :func:`send_file`.
|
||||
"""
|
||||
filename = safe_join(directory, filename)
|
||||
if not os.path.isfile(filename):
|
||||
raise NotFound()
|
||||
options.setdefault('conditional', True)
|
||||
return send_file(filename, **options)
|
||||
|
||||
|
||||
def get_root_path(import_name):
|
||||
"""Returns the path to a package or cwd if that cannot be found. This
|
||||
returns the path of a package or the folder that contains a module.
|
||||
|
||||
Not to be confused with the package path returned by :func:`find_package`.
|
||||
"""
|
||||
# Module already imported and has a file attribute. Use that first.
|
||||
mod = sys.modules.get(import_name)
|
||||
if mod is not None and hasattr(mod, '__file__'):
|
||||
return os.path.dirname(os.path.abspath(mod.__file__))
|
||||
|
||||
# Next attempt: check the loader.
|
||||
loader = pkgutil.get_loader(import_name)
|
||||
|
||||
# Loader does not exist or we're referring to an unloaded main module
|
||||
# or a main module without path (interactive sessions), go with the
|
||||
# current working directory.
|
||||
if loader is None or import_name == '__main__':
|
||||
return os.getcwd()
|
||||
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
# Some other loaders might exhibit the same behavior.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
filepath = loader.get_filename(import_name)
|
||||
else:
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
filepath = sys.modules[import_name].__file__
|
||||
|
||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||
return os.path.dirname(os.path.abspath(filepath))
|
||||
|
||||
|
||||
def find_package(import_name):
|
||||
"""Finds a package and returns the prefix (or None if the package is
|
||||
not installed) as well as the folder that contains the package or
|
||||
module as a tuple. The package path returned is the module that would
|
||||
have to be added to the pythonpath in order to make it possible to
|
||||
import the module. The prefix is the path below which a UNIX like
|
||||
folder structure exists (lib, share etc.).
|
||||
"""
|
||||
root_mod_name = import_name.split('.')[0]
|
||||
loader = pkgutil.get_loader(root_mod_name)
|
||||
if loader is None or import_name == '__main__':
|
||||
# import name is not found, or interactive/main module
|
||||
package_path = os.getcwd()
|
||||
else:
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
filename = loader.get_filename(root_mod_name)
|
||||
elif hasattr(loader, 'archive'):
|
||||
# zipimporter's loader.archive points to the .egg or .zip
|
||||
# archive filename is dropped in call to dirname below.
|
||||
filename = loader.archive
|
||||
else:
|
||||
# At least one loader is missing both get_filename and archive:
|
||||
# Google App Engine's HardenedModulesHook
|
||||
#
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
filename = sys.modules[import_name].__file__
|
||||
package_path = os.path.abspath(os.path.dirname(filename))
|
||||
# package_path ends with __init__.py for a package
|
||||
if loader.is_package(root_mod_name):
|
||||
package_path = os.path.dirname(package_path)
|
||||
|
||||
site_parent, site_folder = os.path.split(package_path)
|
||||
py_prefix = os.path.abspath(sys.prefix)
|
||||
if package_path.startswith(py_prefix):
|
||||
return py_prefix, package_path
|
||||
elif site_folder.lower() == 'site-packages':
|
||||
parent, folder = os.path.split(site_parent)
|
||||
# Windows like installations
|
||||
if folder.lower() == 'lib':
|
||||
base_dir = parent
|
||||
# UNIX like installations
|
||||
elif os.path.basename(parent).lower() == 'lib':
|
||||
base_dir = os.path.dirname(parent)
|
||||
else:
|
||||
base_dir = site_parent
|
||||
return base_dir, package_path
|
||||
return None, package_path
|
||||
|
||||
|
||||
class locked_cached_property(object):
|
||||
"""A decorator that converts a function into a lazy property. The
|
||||
function wrapped is called the first time to retrieve the result
|
||||
and then that calculated result is used the next time you access
|
||||
the value. Works like the one in Werkzeug but has a lock for
|
||||
thread safety.
|
||||
"""
|
||||
|
||||
def __init__(self, func, name=None, doc=None):
|
||||
self.__name__ = name or func.__name__
|
||||
self.__module__ = func.__module__
|
||||
self.__doc__ = doc or func.__doc__
|
||||
self.func = func
|
||||
self.lock = RLock()
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
with self.lock:
|
||||
value = obj.__dict__.get(self.__name__, _missing)
|
||||
if value is _missing:
|
||||
value = self.func(obj)
|
||||
obj.__dict__[self.__name__] = value
|
||||
return value
|
||||
|
||||
|
||||
class _PackageBoundObject(object):
|
||||
|
||||
def __init__(self, import_name, template_folder=None):
|
||||
#: The name of the package or module. Do not change this once
|
||||
#: it was set by the constructor.
|
||||
self.import_name = import_name
|
||||
|
||||
#: location of the templates. `None` if templates should not be
|
||||
#: exposed.
|
||||
self.template_folder = template_folder
|
||||
|
||||
#: Where is the app root located?
|
||||
self.root_path = get_root_path(self.import_name)
|
||||
|
||||
self._static_folder = None
|
||||
self._static_url_path = None
|
||||
|
||||
def _get_static_folder(self):
|
||||
if self._static_folder is not None:
|
||||
return os.path.join(self.root_path, self._static_folder)
|
||||
def _set_static_folder(self, value):
|
||||
self._static_folder = value
|
||||
static_folder = property(_get_static_folder, _set_static_folder)
|
||||
del _get_static_folder, _set_static_folder
|
||||
|
||||
def _get_static_url_path(self):
|
||||
if self._static_url_path is None:
|
||||
if self.static_folder is None:
|
||||
return None
|
||||
return '/' + os.path.basename(self.static_folder)
|
||||
return self._static_url_path
|
||||
def _set_static_url_path(self, value):
|
||||
self._static_url_path = value
|
||||
static_url_path = property(_get_static_url_path, _set_static_url_path)
|
||||
del _get_static_url_path, _set_static_url_path
|
||||
|
||||
@property
|
||||
def has_static_folder(self):
|
||||
"""This is `True` if the package bound object's container has a
|
||||
folder named ``'static'``.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return self.static_folder is not None
|
||||
|
||||
@locked_cached_property
|
||||
def jinja_loader(self):
|
||||
"""The Jinja loader for this package bound object.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if self.template_folder is not None:
|
||||
return FileSystemLoader(os.path.join(self.root_path,
|
||||
self.template_folder))
|
||||
|
||||
def get_send_file_max_age(self, filename):
|
||||
"""Provides default cache_timeout for the :func:`send_file` functions.
|
||||
|
||||
By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
|
||||
the configuration of :data:`~flask.current_app`.
|
||||
|
||||
Static file functions such as :func:`send_from_directory` use this
|
||||
function, and :func:`send_file` calls this function on
|
||||
:data:`~flask.current_app` when the given cache_timeout is `None`. If a
|
||||
cache_timeout is given in :func:`send_file`, that timeout is used;
|
||||
otherwise, this method is called.
|
||||
|
||||
This allows subclasses to change the behavior when sending files based
|
||||
on the filename. For example, to set the cache timeout for .js files
|
||||
to 60 seconds::
|
||||
|
||||
class MyFlask(flask.Flask):
|
||||
def get_send_file_max_age(self, name):
|
||||
if name.lower().endswith('.js'):
|
||||
return 60
|
||||
return flask.Flask.get_send_file_max_age(self, name)
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
|
||||
|
||||
def send_static_file(self, filename):
|
||||
"""Function used internally to send static files from the static
|
||||
folder to the browser.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if not self.has_static_folder:
|
||||
raise RuntimeError('No static folder for this object')
|
||||
# Ensure get_send_file_max_age is called in all cases.
|
||||
# Here, we ensure get_send_file_max_age is called for Blueprints.
|
||||
cache_timeout = self.get_send_file_max_age(filename)
|
||||
return send_from_directory(self.static_folder, filename,
|
||||
cache_timeout=cache_timeout)
|
||||
|
||||
def open_resource(self, resource, mode='rb'):
|
||||
"""Opens a resource from the application's resource folder. To see
|
||||
how this works, consider the following folder structure::
|
||||
|
||||
/myapplication.py
|
||||
/schema.sql
|
||||
/static
|
||||
/style.css
|
||||
/templates
|
||||
/layout.html
|
||||
/index.html
|
||||
|
||||
If you want to open the `schema.sql` file you would do the
|
||||
following::
|
||||
|
||||
with app.open_resource('schema.sql') as f:
|
||||
contents = f.read()
|
||||
do_something_with(contents)
|
||||
|
||||
:param resource: the name of the resource. To access resources within
|
||||
subfolders use forward slashes as separator.
|
||||
:param mode: resource file opening mode, default is 'rb'.
|
||||
"""
|
||||
if mode not in ('r', 'rb'):
|
||||
raise ValueError('Resources can only be opened for reading')
|
||||
return open(os.path.join(self.root_path, resource), mode)
|
BIN
lib/flask/helpers.pyc
Normal file
BIN
lib/flask/helpers.pyc
Normal file
Binary file not shown.
243
lib/flask/json.py
Normal file
243
lib/flask/json.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.jsonimpl
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Implementation helpers for the JSON support in Flask.
|
||||
|
||||
:copyright: (c) 2012 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import io
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from .globals import current_app, request
|
||||
from ._compat import text_type, PY2
|
||||
|
||||
from werkzeug.http import http_date
|
||||
from jinja2 import Markup
|
||||
|
||||
# Use the same json implementation as itsdangerous on which we
|
||||
# depend anyways.
|
||||
try:
|
||||
from itsdangerous import simplejson as _json
|
||||
except ImportError:
|
||||
from itsdangerous import json as _json
|
||||
|
||||
|
||||
# figure out if simplejson escapes slashes. This behavior was changed
|
||||
# from one version to another without reason.
|
||||
_slash_escape = '\\/' not in _json.dumps('/')
|
||||
|
||||
|
||||
__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump',
|
||||
'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder',
|
||||
'jsonify']
|
||||
|
||||
|
||||
def _wrap_reader_for_text(fp, encoding):
|
||||
if isinstance(fp.read(0), bytes):
|
||||
fp = io.TextIOWrapper(io.BufferedReader(fp), encoding)
|
||||
return fp
|
||||
|
||||
|
||||
def _wrap_writer_for_text(fp, encoding):
|
||||
try:
|
||||
fp.write('')
|
||||
except TypeError:
|
||||
fp = io.TextIOWrapper(fp, encoding)
|
||||
return fp
|
||||
|
||||
|
||||
class JSONEncoder(_json.JSONEncoder):
|
||||
"""The default Flask JSON encoder. This one extends the default simplejson
|
||||
encoder by also supporting ``datetime`` objects, ``UUID`` as well as
|
||||
``Markup`` objects which are serialized as RFC 822 datetime strings (same
|
||||
as the HTTP date format). In order to support more data types override the
|
||||
:meth:`default` method.
|
||||
"""
|
||||
|
||||
def default(self, o):
|
||||
"""Implement this method in a subclass such that it returns a
|
||||
serializable object for ``o``, or calls the base implementation (to
|
||||
raise a ``TypeError``).
|
||||
|
||||
For example, to support arbitrary iterators, you could implement
|
||||
default like this::
|
||||
|
||||
def default(self, o):
|
||||
try:
|
||||
iterable = iter(o)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
return list(iterable)
|
||||
return JSONEncoder.default(self, o)
|
||||
"""
|
||||
if isinstance(o, datetime):
|
||||
return http_date(o)
|
||||
if isinstance(o, uuid.UUID):
|
||||
return str(o)
|
||||
if hasattr(o, '__html__'):
|
||||
return text_type(o.__html__())
|
||||
return _json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
class JSONDecoder(_json.JSONDecoder):
|
||||
"""The default JSON decoder. This one does not change the behavior from
|
||||
the default simplejson encoder. Consult the :mod:`json` documentation
|
||||
for more information. This decoder is not only used for the load
|
||||
functions of this module but also :attr:`~flask.Request`.
|
||||
"""
|
||||
|
||||
|
||||
def _dump_arg_defaults(kwargs):
|
||||
"""Inject default arguments for dump functions."""
|
||||
if current_app:
|
||||
kwargs.setdefault('cls', current_app.json_encoder)
|
||||
if not current_app.config['JSON_AS_ASCII']:
|
||||
kwargs.setdefault('ensure_ascii', False)
|
||||
kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS'])
|
||||
else:
|
||||
kwargs.setdefault('sort_keys', True)
|
||||
kwargs.setdefault('cls', JSONEncoder)
|
||||
|
||||
|
||||
def _load_arg_defaults(kwargs):
|
||||
"""Inject default arguments for load functions."""
|
||||
if current_app:
|
||||
kwargs.setdefault('cls', current_app.json_decoder)
|
||||
else:
|
||||
kwargs.setdefault('cls', JSONDecoder)
|
||||
|
||||
|
||||
def dumps(obj, **kwargs):
|
||||
"""Serialize ``obj`` to a JSON formatted ``str`` by using the application's
|
||||
configured encoder (:attr:`~flask.Flask.json_encoder`) if there is an
|
||||
application on the stack.
|
||||
|
||||
This function can return ``unicode`` strings or ascii-only bytestrings by
|
||||
default which coerce into unicode strings automatically. That behavior by
|
||||
default is controlled by the ``JSON_AS_ASCII`` configuration variable
|
||||
and can be overriden by the simplejson ``ensure_ascii`` parameter.
|
||||
"""
|
||||
_dump_arg_defaults(kwargs)
|
||||
encoding = kwargs.pop('encoding', None)
|
||||
rv = _json.dumps(obj, **kwargs)
|
||||
if encoding is not None and isinstance(rv, text_type):
|
||||
rv = rv.encode(encoding)
|
||||
return rv
|
||||
|
||||
|
||||
def dump(obj, fp, **kwargs):
|
||||
"""Like :func:`dumps` but writes into a file object."""
|
||||
_dump_arg_defaults(kwargs)
|
||||
encoding = kwargs.pop('encoding', None)
|
||||
if encoding is not None:
|
||||
fp = _wrap_writer_for_text(fp, encoding)
|
||||
_json.dump(obj, fp, **kwargs)
|
||||
|
||||
|
||||
def loads(s, **kwargs):
|
||||
"""Unserialize a JSON object from a string ``s`` by using the application's
|
||||
configured decoder (:attr:`~flask.Flask.json_decoder`) if there is an
|
||||
application on the stack.
|
||||
"""
|
||||
_load_arg_defaults(kwargs)
|
||||
if isinstance(s, bytes):
|
||||
s = s.decode(kwargs.pop('encoding', None) or 'utf-8')
|
||||
return _json.loads(s, **kwargs)
|
||||
|
||||
|
||||
def load(fp, **kwargs):
|
||||
"""Like :func:`loads` but reads from a file object.
|
||||
"""
|
||||
_load_arg_defaults(kwargs)
|
||||
if not PY2:
|
||||
fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8')
|
||||
return _json.load(fp, **kwargs)
|
||||
|
||||
|
||||
def htmlsafe_dumps(obj, **kwargs):
|
||||
"""Works exactly like :func:`dumps` but is safe for use in ``<script>``
|
||||
tags. It accepts the same arguments and returns a JSON string. Note that
|
||||
this is available in templates through the ``|tojson`` filter which will
|
||||
also mark the result as safe. Due to how this function escapes certain
|
||||
characters this is safe even if used outside of ``<script>`` tags.
|
||||
|
||||
The following characters are escaped in strings:
|
||||
|
||||
- ``<``
|
||||
- ``>``
|
||||
- ``&``
|
||||
- ``'``
|
||||
|
||||
This makes it safe to embed such strings in any place in HTML with the
|
||||
notable exception of double quoted attributes. In that case single
|
||||
quote your attributes or HTML escape it in addition.
|
||||
|
||||
.. versionchanged:: 0.10
|
||||
This function's return value is now always safe for HTML usage, even
|
||||
if outside of script tags or if used in XHTML. This rule does not
|
||||
hold true when using this function in HTML attributes that are double
|
||||
quoted. Always single quote attributes if you use the ``|tojson``
|
||||
filter. Alternatively use ``|tojson|forceescape``.
|
||||
"""
|
||||
rv = dumps(obj, **kwargs) \
|
||||
.replace(u'<', u'\\u003c') \
|
||||
.replace(u'>', u'\\u003e') \
|
||||
.replace(u'&', u'\\u0026') \
|
||||
.replace(u"'", u'\\u0027')
|
||||
if not _slash_escape:
|
||||
rv = rv.replace('\\/', '/')
|
||||
return rv
|
||||
|
||||
|
||||
def htmlsafe_dump(obj, fp, **kwargs):
|
||||
"""Like :func:`htmlsafe_dumps` but writes into a file object."""
|
||||
fp.write(unicode(htmlsafe_dumps(obj, **kwargs)))
|
||||
|
||||
|
||||
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::
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
For security reasons only objects are supported toplevel. For more
|
||||
information about this, have a look at :ref:`json-security`.
|
||||
|
||||
This function's response will be pretty printed if it was not requested
|
||||
with ``X-Requested-With: XMLHttpRequest`` to simplify debugging unless
|
||||
the ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to false.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
"""
|
||||
indent = None
|
||||
if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] \
|
||||
and not request.is_xhr:
|
||||
indent = 2
|
||||
return current_app.response_class(dumps(dict(*args, **kwargs),
|
||||
indent=indent),
|
||||
mimetype='application/json')
|
||||
|
||||
|
||||
def tojson_filter(obj, **kwargs):
|
||||
return Markup(htmlsafe_dumps(obj, **kwargs))
|
BIN
lib/flask/json.pyc
Normal file
BIN
lib/flask/json.pyc
Normal file
Binary file not shown.
45
lib/flask/logging.py
Normal file
45
lib/flask/logging.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.logging
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements the logging support for Flask.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from logging import getLogger, StreamHandler, Formatter, getLoggerClass, DEBUG
|
||||
|
||||
|
||||
def create_logger(app):
|
||||
"""Creates a logger for the given application. This logger works
|
||||
similar to a regular Python logger but changes the effective logging
|
||||
level based on the application's debug flag. Furthermore this
|
||||
function also removes all attached handlers in case there was a
|
||||
logger with the log name before.
|
||||
"""
|
||||
Logger = getLoggerClass()
|
||||
|
||||
class DebugLogger(Logger):
|
||||
def getEffectiveLevel(x):
|
||||
if x.level == 0 and app.debug:
|
||||
return DEBUG
|
||||
return Logger.getEffectiveLevel(x)
|
||||
|
||||
class DebugHandler(StreamHandler):
|
||||
def emit(x, record):
|
||||
StreamHandler.emit(x, record) if app.debug else None
|
||||
|
||||
handler = DebugHandler()
|
||||
handler.setLevel(DEBUG)
|
||||
handler.setFormatter(Formatter(app.debug_log_format))
|
||||
logger = getLogger(app.logger_name)
|
||||
# just in case that was not a new logger, get rid of all the handlers
|
||||
# already attached to it.
|
||||
del logger.handlers[:]
|
||||
logger.__class__ = DebugLogger
|
||||
logger.addHandler(handler)
|
||||
return logger
|
BIN
lib/flask/logging.pyc
Normal file
BIN
lib/flask/logging.pyc
Normal file
Binary file not shown.
42
lib/flask/module.py
Normal file
42
lib/flask/module.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.module
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Implements a class that represents module blueprints.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .blueprints import Blueprint
|
||||
|
||||
|
||||
def blueprint_is_module(bp):
|
||||
"""Used to figure out if something is actually a module"""
|
||||
return isinstance(bp, Module)
|
||||
|
||||
|
||||
class Module(Blueprint):
|
||||
"""Deprecated module support. Until Flask 0.6 modules were a different
|
||||
name of the concept now available as blueprints in Flask. They are
|
||||
essentially doing the same but have some bad semantics for templates and
|
||||
static files that were fixed with blueprints.
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
Modules were deprecated in favor for blueprints.
|
||||
"""
|
||||
|
||||
def __init__(self, import_name, name=None, url_prefix=None,
|
||||
static_path=None, subdomain=None):
|
||||
if name is None:
|
||||
assert '.' in import_name, 'name required if package name ' \
|
||||
'does not point to a submodule'
|
||||
name = import_name.rsplit('.', 1)[1]
|
||||
Blueprint.__init__(self, name, import_name, url_prefix=url_prefix,
|
||||
subdomain=subdomain, template_folder='templates')
|
||||
|
||||
if os.path.isdir(os.path.join(self.root_path, 'static')):
|
||||
self._static_folder = 'static'
|
BIN
lib/flask/module.pyc
Normal file
BIN
lib/flask/module.pyc
Normal file
Binary file not shown.
332
lib/flask/sessions.py
Normal file
332
lib/flask/sessions.py
Normal file
|
@ -0,0 +1,332 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.sessions
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Implements cookie based sessions based on itsdangerous.
|
||||
|
||||
:copyright: (c) 2012 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
import hashlib
|
||||
from base64 import b64encode, b64decode
|
||||
from datetime import datetime
|
||||
from werkzeug.http import http_date, parse_date
|
||||
from werkzeug.datastructures import CallbackDict
|
||||
from . import Markup, json
|
||||
from ._compat import iteritems, text_type
|
||||
|
||||
from itsdangerous import URLSafeTimedSerializer, BadSignature
|
||||
|
||||
|
||||
def total_seconds(td):
|
||||
return td.days * 60 * 60 * 24 + td.seconds
|
||||
|
||||
|
||||
class SessionMixin(object):
|
||||
"""Expands a basic dictionary with an accessors that are expected
|
||||
by Flask extensions and users for the session.
|
||||
"""
|
||||
|
||||
def _get_permanent(self):
|
||||
return self.get('_permanent', False)
|
||||
|
||||
def _set_permanent(self, value):
|
||||
self['_permanent'] = bool(value)
|
||||
|
||||
#: this reflects the ``'_permanent'`` key in the dict.
|
||||
permanent = property(_get_permanent, _set_permanent)
|
||||
del _get_permanent, _set_permanent
|
||||
|
||||
#: some session backends can tell you if a session is new, but that is
|
||||
#: not necessarily guaranteed. Use with caution. The default mixin
|
||||
#: implementation just hardcodes `False` in.
|
||||
new = False
|
||||
|
||||
#: for some backends this will always be `True`, but some backends will
|
||||
#: default this to false and detect changes in the dictionary for as
|
||||
#: long as changes do not happen on mutable structures in the session.
|
||||
#: The default mixin implementation just hardcodes `True` in.
|
||||
modified = True
|
||||
|
||||
|
||||
class TaggedJSONSerializer(object):
|
||||
"""A customized JSON serializer that supports a few extra types that
|
||||
we take for granted when serializing (tuples, markup objects, datetime).
|
||||
"""
|
||||
|
||||
def dumps(self, value):
|
||||
def _tag(value):
|
||||
if isinstance(value, tuple):
|
||||
return {' t': [_tag(x) for x in value]}
|
||||
elif isinstance(value, uuid.UUID):
|
||||
return {' u': value.hex}
|
||||
elif isinstance(value, bytes):
|
||||
return {' b': b64encode(value).decode('ascii')}
|
||||
elif callable(getattr(value, '__html__', None)):
|
||||
return {' m': text_type(value.__html__())}
|
||||
elif isinstance(value, list):
|
||||
return [_tag(x) for x in value]
|
||||
elif isinstance(value, datetime):
|
||||
return {' d': http_date(value)}
|
||||
elif isinstance(value, dict):
|
||||
return dict((k, _tag(v)) for k, v in iteritems(value))
|
||||
elif isinstance(value, str):
|
||||
try:
|
||||
return text_type(value)
|
||||
except UnicodeError:
|
||||
raise UnexpectedUnicodeError(u'A byte string with '
|
||||
u'non-ASCII data was passed to the session system '
|
||||
u'which can only store unicode strings. Consider '
|
||||
u'base64 encoding your string (String was %r)' % value)
|
||||
return value
|
||||
return json.dumps(_tag(value), separators=(',', ':'))
|
||||
|
||||
def loads(self, value):
|
||||
def object_hook(obj):
|
||||
if len(obj) != 1:
|
||||
return obj
|
||||
the_key, the_value = next(iteritems(obj))
|
||||
if the_key == ' t':
|
||||
return tuple(the_value)
|
||||
elif the_key == ' u':
|
||||
return uuid.UUID(the_value)
|
||||
elif the_key == ' b':
|
||||
return b64decode(the_value)
|
||||
elif the_key == ' m':
|
||||
return Markup(the_value)
|
||||
elif the_key == ' d':
|
||||
return parse_date(the_value)
|
||||
return obj
|
||||
return json.loads(value, object_hook=object_hook)
|
||||
|
||||
|
||||
session_json_serializer = TaggedJSONSerializer()
|
||||
|
||||
|
||||
class SecureCookieSession(CallbackDict, SessionMixin):
|
||||
"""Baseclass for sessions based on signed cookies."""
|
||||
|
||||
def __init__(self, initial=None):
|
||||
def on_update(self):
|
||||
self.modified = True
|
||||
CallbackDict.__init__(self, initial, on_update)
|
||||
self.modified = False
|
||||
|
||||
|
||||
class NullSession(SecureCookieSession):
|
||||
"""Class used to generate nicer error messages if sessions are not
|
||||
available. Will still allow read-only access to the empty session
|
||||
but fail on setting.
|
||||
"""
|
||||
|
||||
def _fail(self, *args, **kwargs):
|
||||
raise RuntimeError('the session is unavailable because no secret '
|
||||
'key was set. Set the secret_key on the '
|
||||
'application to something unique and secret.')
|
||||
__setitem__ = __delitem__ = clear = pop = popitem = \
|
||||
update = setdefault = _fail
|
||||
del _fail
|
||||
|
||||
|
||||
class SessionInterface(object):
|
||||
"""The basic interface you have to implement in order to replace the
|
||||
default session interface which uses werkzeug's securecookie
|
||||
implementation. The only methods you have to implement are
|
||||
:meth:`open_session` and :meth:`save_session`, the others have
|
||||
useful defaults which you don't need to change.
|
||||
|
||||
The session object returned by the :meth:`open_session` method has to
|
||||
provide a dictionary like interface plus the properties and methods
|
||||
from the :class:`SessionMixin`. We recommend just subclassing a dict
|
||||
and adding that mixin::
|
||||
|
||||
class Session(dict, SessionMixin):
|
||||
pass
|
||||
|
||||
If :meth:`open_session` returns `None` Flask will call into
|
||||
:meth:`make_null_session` to create a session that acts as replacement
|
||||
if the session support cannot work because some requirement is not
|
||||
fulfilled. The default :class:`NullSession` class that is created
|
||||
will complain that the secret key was not set.
|
||||
|
||||
To replace the session interface on an application all you have to do
|
||||
is to assign :attr:`flask.Flask.session_interface`::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.session_interface = MySessionInterface()
|
||||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
|
||||
#: :meth:`make_null_session` will look here for the class that should
|
||||
#: be created when a null session is requested. Likewise the
|
||||
#: :meth:`is_null_session` method will perform a typecheck against
|
||||
#: this type.
|
||||
null_session_class = NullSession
|
||||
|
||||
#: A flag that indicates if the session interface is pickle based.
|
||||
#: This can be used by flask extensions to make a decision in regards
|
||||
#: to how to deal with the session object.
|
||||
#:
|
||||
#: .. versionadded:: 0.10
|
||||
pickle_based = False
|
||||
|
||||
def make_null_session(self, app):
|
||||
"""Creates a null session which acts as a replacement object if the
|
||||
real session support could not be loaded due to a configuration
|
||||
error. This mainly aids the user experience because the job of the
|
||||
null session is to still support lookup without complaining but
|
||||
modifications are answered with a helpful error message of what
|
||||
failed.
|
||||
|
||||
This creates an instance of :attr:`null_session_class` by default.
|
||||
"""
|
||||
return self.null_session_class()
|
||||
|
||||
def is_null_session(self, obj):
|
||||
"""Checks if a given object is a null session. Null sessions are
|
||||
not asked to be saved.
|
||||
|
||||
This checks if the object is an instance of :attr:`null_session_class`
|
||||
by default.
|
||||
"""
|
||||
return isinstance(obj, self.null_session_class)
|
||||
|
||||
def get_cookie_domain(self, app):
|
||||
"""Helpful helper method that returns the cookie domain that should
|
||||
be used for the session cookie if session cookies are used.
|
||||
"""
|
||||
if app.config['SESSION_COOKIE_DOMAIN'] is not None:
|
||||
return app.config['SESSION_COOKIE_DOMAIN']
|
||||
if app.config['SERVER_NAME'] is not None:
|
||||
# chop of the port which is usually not supported by browsers
|
||||
rv = '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0]
|
||||
|
||||
# Google chrome does not like cookies set to .localhost, so
|
||||
# we just go with no domain then. Flask documents anyways that
|
||||
# cross domain cookies need a fully qualified domain name
|
||||
if rv == '.localhost':
|
||||
rv = None
|
||||
|
||||
# If we infer the cookie domain from the server name we need
|
||||
# to check if we are in a subpath. In that case we can't
|
||||
# set a cross domain cookie.
|
||||
if rv is not None:
|
||||
path = self.get_cookie_path(app)
|
||||
if path != '/':
|
||||
rv = rv.lstrip('.')
|
||||
|
||||
return rv
|
||||
|
||||
def get_cookie_path(self, app):
|
||||
"""Returns the path for which the cookie should be valid. The
|
||||
default implementation uses the value from the SESSION_COOKIE_PATH``
|
||||
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
|
||||
uses ``/`` if it's `None`.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_PATH'] or \
|
||||
app.config['APPLICATION_ROOT'] or '/'
|
||||
|
||||
def get_cookie_httponly(self, app):
|
||||
"""Returns True if the session cookie should be httponly. This
|
||||
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
|
||||
config var.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_HTTPONLY']
|
||||
|
||||
def get_cookie_secure(self, app):
|
||||
"""Returns True if the cookie should be secure. This currently
|
||||
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_SECURE']
|
||||
|
||||
def get_expiration_time(self, app, session):
|
||||
"""A helper method that returns an expiration date for the session
|
||||
or `None` if the session is linked to the browser session. The
|
||||
default implementation returns now + the permanent session
|
||||
lifetime configured on the application.
|
||||
"""
|
||||
if session.permanent:
|
||||
return datetime.utcnow() + app.permanent_session_lifetime
|
||||
|
||||
def open_session(self, app, request):
|
||||
"""This method has to be implemented and must either return `None`
|
||||
in case the loading failed because of a configuration error or an
|
||||
instance of a session object which implements a dictionary like
|
||||
interface + the methods and attributes on :class:`SessionMixin`.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
"""This is called for actual sessions returned by :meth:`open_session`
|
||||
at the end of the request. This is still called during a request
|
||||
context so if you absolutely need access to the request you can do
|
||||
that.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SecureCookieSessionInterface(SessionInterface):
|
||||
"""The default session interface that stores sessions in signed cookies
|
||||
through the :mod:`itsdangerous` module.
|
||||
"""
|
||||
#: the salt that should be applied on top of the secret key for the
|
||||
#: signing of cookie based sessions.
|
||||
salt = 'cookie-session'
|
||||
#: the hash function to use for the signature. The default is sha1
|
||||
digest_method = staticmethod(hashlib.sha1)
|
||||
#: the name of the itsdangerous supported key derivation. The default
|
||||
#: is hmac.
|
||||
key_derivation = 'hmac'
|
||||
#: A python serializer for the payload. The default is a compact
|
||||
#: JSON derived serializer with support for some extra Python types
|
||||
#: such as datetime objects or tuples.
|
||||
serializer = session_json_serializer
|
||||
session_class = SecureCookieSession
|
||||
|
||||
def get_signing_serializer(self, app):
|
||||
if not app.secret_key:
|
||||
return None
|
||||
signer_kwargs = dict(
|
||||
key_derivation=self.key_derivation,
|
||||
digest_method=self.digest_method
|
||||
)
|
||||
return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
|
||||
serializer=self.serializer,
|
||||
signer_kwargs=signer_kwargs)
|
||||
|
||||
def open_session(self, app, request):
|
||||
s = self.get_signing_serializer(app)
|
||||
if s is None:
|
||||
return None
|
||||
val = request.cookies.get(app.session_cookie_name)
|
||||
if not val:
|
||||
return self.session_class()
|
||||
max_age = total_seconds(app.permanent_session_lifetime)
|
||||
try:
|
||||
data = s.loads(val, max_age=max_age)
|
||||
return self.session_class(data)
|
||||
except BadSignature:
|
||||
return self.session_class()
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
if not session:
|
||||
if session.modified:
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
val = self.get_signing_serializer(app).dumps(dict(session))
|
||||
response.set_cookie(app.session_cookie_name, val,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
|
||||
|
||||
from flask.debughelpers import UnexpectedUnicodeError
|
BIN
lib/flask/sessions.pyc
Normal file
BIN
lib/flask/sessions.pyc
Normal file
Binary file not shown.
55
lib/flask/signals.py
Normal file
55
lib/flask/signals.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.signals
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements signals based on blinker if available, otherwise
|
||||
falls silently back to a noop
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
signals_available = False
|
||||
try:
|
||||
from blinker import Namespace
|
||||
signals_available = True
|
||||
except ImportError:
|
||||
class Namespace(object):
|
||||
def signal(self, name, doc=None):
|
||||
return _FakeSignal(name, doc)
|
||||
|
||||
class _FakeSignal(object):
|
||||
"""If blinker is unavailable, create a fake class with the same
|
||||
interface that allows sending of signals but will fail with an
|
||||
error on anything else. Instead of doing anything on send, it
|
||||
will just ignore the arguments and do nothing instead.
|
||||
"""
|
||||
|
||||
def __init__(self, name, doc=None):
|
||||
self.name = name
|
||||
self.__doc__ = doc
|
||||
def _fail(self, *args, **kwargs):
|
||||
raise RuntimeError('signalling support is unavailable '
|
||||
'because the blinker library is '
|
||||
'not installed.')
|
||||
send = lambda *a, **kw: None
|
||||
connect = disconnect = has_receivers_for = receivers_for = \
|
||||
temporarily_connected_to = connected_to = _fail
|
||||
del _fail
|
||||
|
||||
# the namespace for code signals. If you are not flask code, do
|
||||
# not put signals in here. Create your own namespace instead.
|
||||
_signals = Namespace()
|
||||
|
||||
|
||||
# core signals. For usage examples grep the sourcecode or consult
|
||||
# the API documentation in docs/api.rst as well as docs/signals.rst
|
||||
template_rendered = _signals.signal('template-rendered')
|
||||
request_started = _signals.signal('request-started')
|
||||
request_finished = _signals.signal('request-finished')
|
||||
request_tearing_down = _signals.signal('request-tearing-down')
|
||||
got_request_exception = _signals.signal('got-request-exception')
|
||||
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
|
||||
appcontext_pushed = _signals.signal('appcontext-pushed')
|
||||
appcontext_popped = _signals.signal('appcontext-popped')
|
||||
message_flashed = _signals.signal('message-flashed')
|
BIN
lib/flask/signals.pyc
Normal file
BIN
lib/flask/signals.pyc
Normal file
Binary file not shown.
143
lib/flask/templating.py
Normal file
143
lib/flask/templating.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.templating
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Implements the bridge to Jinja2.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import posixpath
|
||||
from jinja2 import BaseLoader, Environment as BaseEnvironment, \
|
||||
TemplateNotFound
|
||||
|
||||
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||
from .signals import template_rendered
|
||||
from .module import blueprint_is_module
|
||||
from ._compat import itervalues, iteritems
|
||||
|
||||
|
||||
def _default_template_ctx_processor():
|
||||
"""Default template context processor. Injects `request`,
|
||||
`session` and `g`.
|
||||
"""
|
||||
reqctx = _request_ctx_stack.top
|
||||
appctx = _app_ctx_stack.top
|
||||
rv = {}
|
||||
if appctx is not None:
|
||||
rv['g'] = appctx.g
|
||||
if reqctx is not None:
|
||||
rv['request'] = reqctx.request
|
||||
rv['session'] = reqctx.session
|
||||
return rv
|
||||
|
||||
|
||||
class Environment(BaseEnvironment):
|
||||
"""Works like a regular Jinja2 environment but has some additional
|
||||
knowledge of how Flask's blueprint works so that it can prepend the
|
||||
name of the blueprint to referenced templates if necessary.
|
||||
"""
|
||||
|
||||
def __init__(self, app, **options):
|
||||
if 'loader' not in options:
|
||||
options['loader'] = app.create_global_jinja_loader()
|
||||
BaseEnvironment.__init__(self, **options)
|
||||
self.app = app
|
||||
|
||||
|
||||
class DispatchingJinjaLoader(BaseLoader):
|
||||
"""A loader that looks for templates in the application and all
|
||||
the blueprint folders.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def get_source(self, environment, template):
|
||||
for loader, local_name in self._iter_loaders(template):
|
||||
try:
|
||||
return loader.get_source(environment, local_name)
|
||||
except TemplateNotFound:
|
||||
pass
|
||||
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def _iter_loaders(self, template):
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, template
|
||||
|
||||
# old style module based loaders in case we are dealing with a
|
||||
# blueprint that is an old style module
|
||||
try:
|
||||
module, local_name = posixpath.normpath(template).split('/', 1)
|
||||
blueprint = self.app.blueprints[module]
|
||||
if blueprint_is_module(blueprint):
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, local_name
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
|
||||
for blueprint in itervalues(self.app.blueprints):
|
||||
if blueprint_is_module(blueprint):
|
||||
continue
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, template
|
||||
|
||||
def list_templates(self):
|
||||
result = set()
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
result.update(loader.list_templates())
|
||||
|
||||
for name, blueprint in iteritems(self.app.blueprints):
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
for template in loader.list_templates():
|
||||
prefix = ''
|
||||
if blueprint_is_module(blueprint):
|
||||
prefix = name + '/'
|
||||
result.add(prefix + template)
|
||||
|
||||
return list(result)
|
||||
|
||||
|
||||
def _render(template, context, app):
|
||||
"""Renders the template and fires the signal"""
|
||||
rv = template.render(context)
|
||||
template_rendered.send(app, template=template, context=context)
|
||||
return rv
|
||||
|
||||
|
||||
def render_template(template_name_or_list, **context):
|
||||
"""Renders a template from the template folder with the given
|
||||
context.
|
||||
|
||||
:param template_name_or_list: the name of the template to be
|
||||
rendered, or an iterable with template names
|
||||
the first one existing will be rendered
|
||||
:param context: the variables that should be available in the
|
||||
context of the template.
|
||||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
|
||||
context, ctx.app)
|
||||
|
||||
|
||||
def render_template_string(source, **context):
|
||||
"""Renders a template from the given template source string
|
||||
with the given context.
|
||||
|
||||
:param source: the sourcecode of the template to be
|
||||
rendered
|
||||
:param context: the variables that should be available in the
|
||||
context of the template.
|
||||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(ctx.app.jinja_env.from_string(source),
|
||||
context, ctx.app)
|
BIN
lib/flask/templating.pyc
Normal file
BIN
lib/flask/templating.pyc
Normal file
Binary file not shown.
124
lib/flask/testing.py
Normal file
124
lib/flask/testing.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.testing
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements test support helpers. This module is lazily imported
|
||||
and usually not used in production environments.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from contextlib import contextmanager
|
||||
from werkzeug.test import Client, EnvironBuilder
|
||||
from flask import _request_ctx_stack
|
||||
|
||||
try:
|
||||
from werkzeug.urls import url_parse
|
||||
except ImportError:
|
||||
from urlparse import urlsplit as url_parse
|
||||
|
||||
|
||||
def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs):
|
||||
"""Creates a new test builder with some application defaults thrown in."""
|
||||
http_host = app.config.get('SERVER_NAME')
|
||||
app_root = app.config.get('APPLICATION_ROOT')
|
||||
if base_url is None:
|
||||
url = url_parse(path)
|
||||
base_url = 'http://%s/' % (url.netloc or http_host or 'localhost')
|
||||
if app_root:
|
||||
base_url += app_root.lstrip('/')
|
||||
if url.netloc:
|
||||
path = url.path
|
||||
return EnvironBuilder(path, base_url, *args, **kwargs)
|
||||
|
||||
|
||||
class FlaskClient(Client):
|
||||
"""Works like a regular Werkzeug test client but has some knowledge about
|
||||
how Flask works to defer the cleanup of the request context stack to the
|
||||
end of a with body when used in a with statement. For general information
|
||||
about how to use this class refer to :class:`werkzeug.test.Client`.
|
||||
|
||||
Basic usage is outlined in the :ref:`testing` chapter.
|
||||
"""
|
||||
|
||||
preserve_context = False
|
||||
|
||||
@contextmanager
|
||||
def session_transaction(self, *args, **kwargs):
|
||||
"""When used in combination with a with statement this opens a
|
||||
session transaction. This can be used to modify the session that
|
||||
the test client uses. Once the with block is left the session is
|
||||
stored back.
|
||||
|
||||
with client.session_transaction() as session:
|
||||
session['value'] = 42
|
||||
|
||||
Internally this is implemented by going through a temporary test
|
||||
request context and since session handling could depend on
|
||||
request variables this function accepts the same arguments as
|
||||
:meth:`~flask.Flask.test_request_context` which are directly
|
||||
passed through.
|
||||
"""
|
||||
if self.cookie_jar is None:
|
||||
raise RuntimeError('Session transactions only make sense '
|
||||
'with cookies enabled.')
|
||||
app = self.application
|
||||
environ_overrides = kwargs.setdefault('environ_overrides', {})
|
||||
self.cookie_jar.inject_wsgi(environ_overrides)
|
||||
outer_reqctx = _request_ctx_stack.top
|
||||
with app.test_request_context(*args, **kwargs) as c:
|
||||
sess = app.open_session(c.request)
|
||||
if sess is None:
|
||||
raise RuntimeError('Session backend did not open a session. '
|
||||
'Check the configuration')
|
||||
|
||||
# Since we have to open a new request context for the session
|
||||
# handling we want to make sure that we hide out own context
|
||||
# from the caller. By pushing the original request context
|
||||
# (or None) on top of this and popping it we get exactly that
|
||||
# behavior. It's important to not use the push and pop
|
||||
# methods of the actual request context object since that would
|
||||
# mean that cleanup handlers are called
|
||||
_request_ctx_stack.push(outer_reqctx)
|
||||
try:
|
||||
yield sess
|
||||
finally:
|
||||
_request_ctx_stack.pop()
|
||||
|
||||
resp = app.response_class()
|
||||
if not app.session_interface.is_null_session(sess):
|
||||
app.save_session(sess, resp)
|
||||
headers = resp.get_wsgi_headers(c.request.environ)
|
||||
self.cookie_jar.extract_wsgi(c.request.environ, headers)
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
kwargs.setdefault('environ_overrides', {}) \
|
||||
['flask._preserve_context'] = self.preserve_context
|
||||
|
||||
as_tuple = kwargs.pop('as_tuple', False)
|
||||
buffered = kwargs.pop('buffered', False)
|
||||
follow_redirects = kwargs.pop('follow_redirects', False)
|
||||
builder = make_test_environ_builder(self.application, *args, **kwargs)
|
||||
|
||||
return Client.open(self, builder,
|
||||
as_tuple=as_tuple,
|
||||
buffered=buffered,
|
||||
follow_redirects=follow_redirects)
|
||||
|
||||
def __enter__(self):
|
||||
if self.preserve_context:
|
||||
raise RuntimeError('Cannot nest client invocations')
|
||||
self.preserve_context = True
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.preserve_context = False
|
||||
|
||||
# on exit we want to clean up earlier. Normally the request context
|
||||
# stays preserved until the next request in the same thread comes
|
||||
# in. See RequestGlobals.push() for the general behavior.
|
||||
top = _request_ctx_stack.top
|
||||
if top is not None and top.preserved:
|
||||
top.pop()
|
BIN
lib/flask/testing.pyc
Normal file
BIN
lib/flask/testing.pyc
Normal file
Binary file not shown.
246
lib/flask/testsuite/__init__.py
Normal file
246
lib/flask/testsuite/__init__.py
Normal file
|
@ -0,0 +1,246 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.testsuite
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Tests Flask itself. The majority of Flask is already tested
|
||||
as part of Werkzeug.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import flask
|
||||
import warnings
|
||||
import unittest
|
||||
from functools import update_wrapper
|
||||
from contextlib import contextmanager
|
||||
from werkzeug.utils import import_string, find_modules
|
||||
from flask._compat import reraise, StringIO
|
||||
|
||||
|
||||
def add_to_path(path):
|
||||
"""Adds an entry to sys.path if it's not already there. This does
|
||||
not append it but moves it to the front so that we can be sure it
|
||||
is loaded.
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
raise RuntimeError('Tried to add nonexisting path')
|
||||
|
||||
def _samefile(x, y):
|
||||
if x == y:
|
||||
return True
|
||||
try:
|
||||
return os.path.samefile(x, y)
|
||||
except (IOError, OSError, AttributeError):
|
||||
# Windows has no samefile
|
||||
return False
|
||||
sys.path[:] = [x for x in sys.path if not _samefile(path, x)]
|
||||
sys.path.insert(0, path)
|
||||
|
||||
|
||||
def iter_suites():
|
||||
"""Yields all testsuites."""
|
||||
for module in find_modules(__name__):
|
||||
mod = import_string(module)
|
||||
if hasattr(mod, 'suite'):
|
||||
yield mod.suite()
|
||||
|
||||
|
||||
def find_all_tests(suite):
|
||||
"""Yields all the tests and their names from a given suite."""
|
||||
suites = [suite]
|
||||
while suites:
|
||||
s = suites.pop()
|
||||
try:
|
||||
suites.extend(s)
|
||||
except TypeError:
|
||||
yield s, '%s.%s.%s' % (
|
||||
s.__class__.__module__,
|
||||
s.__class__.__name__,
|
||||
s._testMethodName
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def catch_warnings():
|
||||
"""Catch warnings in a with block in a list"""
|
||||
# make sure deprecation warnings are active in tests
|
||||
warnings.simplefilter('default', category=DeprecationWarning)
|
||||
|
||||
filters = warnings.filters
|
||||
warnings.filters = filters[:]
|
||||
old_showwarning = warnings.showwarning
|
||||
log = []
|
||||
def showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
log.append(locals())
|
||||
try:
|
||||
warnings.showwarning = showwarning
|
||||
yield log
|
||||
finally:
|
||||
warnings.filters = filters
|
||||
warnings.showwarning = old_showwarning
|
||||
|
||||
|
||||
@contextmanager
|
||||
def catch_stderr():
|
||||
"""Catch stderr in a StringIO"""
|
||||
old_stderr = sys.stderr
|
||||
sys.stderr = rv = StringIO()
|
||||
try:
|
||||
yield rv
|
||||
finally:
|
||||
sys.stderr = old_stderr
|
||||
|
||||
|
||||
def emits_module_deprecation_warning(f):
|
||||
def new_f(self, *args, **kwargs):
|
||||
with catch_warnings() as log:
|
||||
f(self, *args, **kwargs)
|
||||
self.assert_true(log, 'expected deprecation warning')
|
||||
for entry in log:
|
||||
self.assert_in('Modules are deprecated', str(entry['message']))
|
||||
return update_wrapper(new_f, f)
|
||||
|
||||
|
||||
class FlaskTestCase(unittest.TestCase):
|
||||
"""Baseclass for all the tests that Flask uses. Use these methods
|
||||
for testing instead of the camelcased ones in the baseclass for
|
||||
consistency.
|
||||
"""
|
||||
|
||||
def ensure_clean_request_context(self):
|
||||
# make sure we're not leaking a request context since we are
|
||||
# testing flask internally in debug mode in a few cases
|
||||
leaks = []
|
||||
while flask._request_ctx_stack.top is not None:
|
||||
leaks.append(flask._request_ctx_stack.pop())
|
||||
self.assert_equal(leaks, [])
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
self.setup()
|
||||
|
||||
def tearDown(self):
|
||||
unittest.TestCase.tearDown(self)
|
||||
self.ensure_clean_request_context()
|
||||
self.teardown()
|
||||
|
||||
def assert_equal(self, x, y):
|
||||
return self.assertEqual(x, y)
|
||||
|
||||
def assert_raises(self, exc_type, callable=None, *args, **kwargs):
|
||||
catcher = _ExceptionCatcher(self, exc_type)
|
||||
if callable is None:
|
||||
return catcher
|
||||
with catcher:
|
||||
callable(*args, **kwargs)
|
||||
|
||||
def assert_true(self, x, msg=None):
|
||||
self.assertTrue(x, msg)
|
||||
|
||||
def assert_false(self, x, msg=None):
|
||||
self.assertFalse(x, msg)
|
||||
|
||||
def assert_in(self, x, y):
|
||||
self.assertIn(x, y)
|
||||
|
||||
def assert_not_in(self, x, y):
|
||||
self.assertNotIn(x, y)
|
||||
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
def assertIn(self, x, y):
|
||||
assert x in y, "%r unexpectedly not in %r" % (x, y)
|
||||
|
||||
def assertNotIn(self, x, y):
|
||||
assert x not in y, "%r unexpectedly in %r" % (x, y)
|
||||
|
||||
|
||||
class _ExceptionCatcher(object):
|
||||
|
||||
def __init__(self, test_case, exc_type):
|
||||
self.test_case = test_case
|
||||
self.exc_type = exc_type
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
exception_name = self.exc_type.__name__
|
||||
if exc_type is None:
|
||||
self.test_case.fail('Expected exception of type %r' %
|
||||
exception_name)
|
||||
elif not issubclass(exc_type, self.exc_type):
|
||||
reraise(exc_type, exc_value, tb)
|
||||
return True
|
||||
|
||||
|
||||
class BetterLoader(unittest.TestLoader):
|
||||
"""A nicer loader that solves two problems. First of all we are setting
|
||||
up tests from different sources and we're doing this programmatically
|
||||
which breaks the default loading logic so this is required anyways.
|
||||
Secondly this loader has a nicer interpolation for test names than the
|
||||
default one so you can just do ``run-tests.py ViewTestCase`` and it
|
||||
will work.
|
||||
"""
|
||||
|
||||
def getRootSuite(self):
|
||||
return suite()
|
||||
|
||||
def loadTestsFromName(self, name, module=None):
|
||||
root = self.getRootSuite()
|
||||
if name == 'suite':
|
||||
return root
|
||||
|
||||
all_tests = []
|
||||
for testcase, testname in find_all_tests(root):
|
||||
if testname == name or \
|
||||
testname.endswith('.' + name) or \
|
||||
('.' + name + '.') in testname or \
|
||||
testname.startswith(name + '.'):
|
||||
all_tests.append(testcase)
|
||||
|
||||
if not all_tests:
|
||||
raise LookupError('could not find test case for "%s"' % name)
|
||||
|
||||
if len(all_tests) == 1:
|
||||
return all_tests[0]
|
||||
rv = unittest.TestSuite()
|
||||
for test in all_tests:
|
||||
rv.addTest(test)
|
||||
return rv
|
||||
|
||||
|
||||
def setup_path():
|
||||
add_to_path(os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__), 'test_apps')))
|
||||
|
||||
|
||||
def suite():
|
||||
"""A testsuite that has all the Flask tests. You can use this
|
||||
function to integrate the Flask tests into your own testsuite
|
||||
in case you want to test that monkeypatches to Flask do not
|
||||
break it.
|
||||
"""
|
||||
setup_path()
|
||||
suite = unittest.TestSuite()
|
||||
for other_suite in iter_suites():
|
||||
suite.addTest(other_suite)
|
||||
return suite
|
||||
|
||||
|
||||
def main():
|
||||
"""Runs the testsuite as command line application."""
|
||||
try:
|
||||
unittest.main(testLoader=BetterLoader(), defaultTest='suite')
|
||||
except Exception as e:
|
||||
print('Error: %s' % e)
|
BIN
lib/flask/testsuite/__init__.pyc
Normal file
BIN
lib/flask/testsuite/__init__.pyc
Normal file
Binary file not shown.
101
lib/flask/testsuite/appctx.py
Normal file
101
lib/flask/testsuite/appctx.py
Normal file
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.testsuite.appctx
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests the application context.
|
||||
|
||||
:copyright: (c) 2012 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import flask
|
||||
import unittest
|
||||
from flask.testsuite import FlaskTestCase
|
||||
|
||||
|
||||
class AppContextTestCase(FlaskTestCase):
|
||||
|
||||
def test_basic_url_generation(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config['SERVER_NAME'] = 'localhost'
|
||||
app.config['PREFERRED_URL_SCHEME'] = 'https'
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
pass
|
||||
|
||||
with app.app_context():
|
||||
rv = flask.url_for('index')
|
||||
self.assert_equal(rv, 'https://localhost/')
|
||||
|
||||
def test_url_generation_requires_server_name(self):
|
||||
app = flask.Flask(__name__)
|
||||
with app.app_context():
|
||||
with self.assert_raises(RuntimeError):
|
||||
flask.url_for('index')
|
||||
|
||||
def test_url_generation_without_context_fails(self):
|
||||
with self.assert_raises(RuntimeError):
|
||||
flask.url_for('index')
|
||||
|
||||
def test_request_context_means_app_context(self):
|
||||
app = flask.Flask(__name__)
|
||||
with app.test_request_context():
|
||||
self.assert_equal(flask.current_app._get_current_object(), app)
|
||||
self.assert_equal(flask._app_ctx_stack.top, None)
|
||||
|
||||
def test_app_context_provides_current_app(self):
|
||||
app = flask.Flask(__name__)
|
||||
with app.app_context():
|
||||
self.assert_equal(flask.current_app._get_current_object(), app)
|
||||
self.assert_equal(flask._app_ctx_stack.top, None)
|
||||
|
||||
def test_app_tearing_down(self):
|
||||
cleanup_stuff = []
|
||||
app = flask.Flask(__name__)
|
||||
@app.teardown_appcontext
|
||||
def cleanup(exception):
|
||||
cleanup_stuff.append(exception)
|
||||
|
||||
with app.app_context():
|
||||
pass
|
||||
|
||||
self.assert_equal(cleanup_stuff, [None])
|
||||
|
||||
def test_custom_app_ctx_globals_class(self):
|
||||
class CustomRequestGlobals(object):
|
||||
def __init__(self):
|
||||
self.spam = 'eggs'
|
||||
app = flask.Flask(__name__)
|
||||
app.app_ctx_globals_class = CustomRequestGlobals
|
||||
with app.app_context():
|
||||
self.assert_equal(
|
||||
flask.render_template_string('{{ g.spam }}'), 'eggs')
|
||||
|
||||
def test_context_refcounts(self):
|
||||
called = []
|
||||
app = flask.Flask(__name__)
|
||||
@app.teardown_request
|
||||
def teardown_req(error=None):
|
||||
called.append('request')
|
||||
@app.teardown_appcontext
|
||||
def teardown_app(error=None):
|
||||
called.append('app')
|
||||
@app.route('/')
|
||||
def index():
|
||||
with flask._app_ctx_stack.top:
|
||||
with flask._request_ctx_stack.top:
|
||||
pass
|
||||
self.assert_true(flask._request_ctx_stack.top.request.environ
|
||||
['werkzeug.request'] is not None)
|
||||
return u''
|
||||
c = app.test_client()
|
||||
c.get('/')
|
||||
self.assertEqual(called, ['request', 'app'])
|
||||
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(AppContextTestCase))
|
||||
return suite
|
BIN
lib/flask/testsuite/appctx.pyc
Normal file
BIN
lib/flask/testsuite/appctx.pyc
Normal file
Binary file not shown.
1254
lib/flask/testsuite/basic.py
Normal file
1254
lib/flask/testsuite/basic.py
Normal file
File diff suppressed because it is too large
Load diff
BIN
lib/flask/testsuite/basic.pyc
Normal file
BIN
lib/flask/testsuite/basic.pyc
Normal file
Binary file not shown.
790
lib/flask/testsuite/blueprints.py
Normal file
790
lib/flask/testsuite/blueprints.py
Normal file
|
@ -0,0 +1,790 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.testsuite.blueprints
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Blueprints (and currently modules)
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import flask
|
||||
import unittest
|
||||
import warnings
|
||||
from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
|
||||
from flask._compat import text_type
|
||||
from werkzeug.exceptions import NotFound
|
||||
from werkzeug.http import parse_cache_control_header
|
||||
from jinja2 import TemplateNotFound
|
||||
|
||||
|
||||
# import moduleapp here because it uses deprecated features and we don't
|
||||
# want to see the warnings
|
||||
warnings.simplefilter('ignore', DeprecationWarning)
|
||||
from moduleapp import app as moduleapp
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
|
||||
class ModuleTestCase(FlaskTestCase):
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_basic_module(self):
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||
@admin.route('/')
|
||||
def admin_index():
|
||||
return 'admin index'
|
||||
@admin.route('/login')
|
||||
def admin_login():
|
||||
return 'admin login'
|
||||
@admin.route('/logout')
|
||||
def admin_logout():
|
||||
return 'admin logout'
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'the index'
|
||||
app.register_module(admin)
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/').data, b'the index')
|
||||
self.assert_equal(c.get('/admin/').data, b'admin index')
|
||||
self.assert_equal(c.get('/admin/login').data, b'admin login')
|
||||
self.assert_equal(c.get('/admin/logout').data, b'admin logout')
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_default_endpoint_name(self):
|
||||
app = flask.Flask(__name__)
|
||||
mod = flask.Module(__name__, 'frontend')
|
||||
def index():
|
||||
return 'Awesome'
|
||||
mod.add_url_rule('/', view_func=index)
|
||||
app.register_module(mod)
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'Awesome')
|
||||
with app.test_request_context():
|
||||
self.assert_equal(flask.url_for('frontend.index'), '/')
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_request_processing(self):
|
||||
catched = []
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||
@admin.before_request
|
||||
def before_admin_request():
|
||||
catched.append('before-admin')
|
||||
@admin.after_request
|
||||
def after_admin_request(response):
|
||||
catched.append('after-admin')
|
||||
return response
|
||||
@admin.route('/')
|
||||
def admin_index():
|
||||
return 'the admin'
|
||||
@app.before_request
|
||||
def before_request():
|
||||
catched.append('before-app')
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
catched.append('after-app')
|
||||
return response
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'the index'
|
||||
app.register_module(admin)
|
||||
c = app.test_client()
|
||||
|
||||
self.assert_equal(c.get('/').data, b'the index')
|
||||
self.assert_equal(catched, ['before-app', 'after-app'])
|
||||
del catched[:]
|
||||
|
||||
self.assert_equal(c.get('/admin/').data, b'the admin')
|
||||
self.assert_equal(catched, ['before-app', 'before-admin',
|
||||
'after-admin', 'after-app'])
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_context_processors(self):
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||
@app.context_processor
|
||||
def inject_all_regular():
|
||||
return {'a': 1}
|
||||
@admin.context_processor
|
||||
def inject_admin():
|
||||
return {'b': 2}
|
||||
@admin.app_context_processor
|
||||
def inject_all_module():
|
||||
return {'c': 3}
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
|
||||
@admin.route('/')
|
||||
def admin_index():
|
||||
return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
|
||||
app.register_module(admin)
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/').data, b'13')
|
||||
self.assert_equal(c.get('/admin/').data, b'123')
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_late_binding(self):
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module(__name__, 'admin')
|
||||
@admin.route('/')
|
||||
def index():
|
||||
return '42'
|
||||
app.register_module(admin, url_prefix='/admin')
|
||||
self.assert_equal(app.test_client().get('/admin/').data, b'42')
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_error_handling(self):
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module(__name__, 'admin')
|
||||
@admin.app_errorhandler(404)
|
||||
def not_found(e):
|
||||
return 'not found', 404
|
||||
@admin.app_errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return 'internal server error', 500
|
||||
@admin.route('/')
|
||||
def index():
|
||||
flask.abort(404)
|
||||
@admin.route('/error')
|
||||
def error():
|
||||
1 // 0
|
||||
app.register_module(admin)
|
||||
c = app.test_client()
|
||||
rv = c.get('/')
|
||||
self.assert_equal(rv.status_code, 404)
|
||||
self.assert_equal(rv.data, b'not found')
|
||||
rv = c.get('/error')
|
||||
self.assert_equal(rv.status_code, 500)
|
||||
self.assert_equal(b'internal server error', rv.data)
|
||||
|
||||
def test_templates_and_static(self):
|
||||
app = moduleapp
|
||||
app.testing = True
|
||||
c = app.test_client()
|
||||
|
||||
rv = c.get('/')
|
||||
self.assert_equal(rv.data, b'Hello from the Frontend')
|
||||
rv = c.get('/admin/')
|
||||
self.assert_equal(rv.data, b'Hello from the Admin')
|
||||
rv = c.get('/admin/index2')
|
||||
self.assert_equal(rv.data, b'Hello from the Admin')
|
||||
rv = c.get('/admin/static/test.txt')
|
||||
self.assert_equal(rv.data.strip(), b'Admin File')
|
||||
rv.close()
|
||||
rv = c.get('/admin/static/css/test.css')
|
||||
self.assert_equal(rv.data.strip(), b'/* nested file */')
|
||||
rv.close()
|
||||
|
||||
with app.test_request_context():
|
||||
self.assert_equal(flask.url_for('admin.static', filename='test.txt'),
|
||||
'/admin/static/test.txt')
|
||||
|
||||
with app.test_request_context():
|
||||
try:
|
||||
flask.render_template('missing.html')
|
||||
except TemplateNotFound as e:
|
||||
self.assert_equal(e.name, 'missing.html')
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
|
||||
with flask.Flask(__name__).test_request_context():
|
||||
self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
|
||||
|
||||
def test_safe_access(self):
|
||||
app = moduleapp
|
||||
|
||||
with app.test_request_context():
|
||||
f = app.view_functions['admin.static']
|
||||
|
||||
try:
|
||||
f('/etc/passwd')
|
||||
except NotFound:
|
||||
pass
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
try:
|
||||
f('../__init__.py')
|
||||
except NotFound:
|
||||
pass
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
|
||||
# testcase for a security issue that may exist on windows systems
|
||||
import os
|
||||
import ntpath
|
||||
old_path = os.path
|
||||
os.path = ntpath
|
||||
try:
|
||||
try:
|
||||
f('..\\__init__.py')
|
||||
except NotFound:
|
||||
pass
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
finally:
|
||||
os.path = old_path
|
||||
|
||||
@emits_module_deprecation_warning
|
||||
def test_endpoint_decorator(self):
|
||||
from werkzeug.routing import Submount, Rule
|
||||
from flask import Module
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.testing = True
|
||||
app.url_map.add(Submount('/foo', [
|
||||
Rule('/bar', endpoint='bar'),
|
||||
Rule('/', endpoint='index')
|
||||
]))
|
||||
module = Module(__name__, __name__)
|
||||
|
||||
@module.endpoint('bar')
|
||||
def bar():
|
||||
return 'bar'
|
||||
|
||||
@module.endpoint('index')
|
||||
def index():
|
||||
return 'index'
|
||||
|
||||
app.register_module(module)
|
||||
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/foo/').data, b'index')
|
||||
self.assert_equal(c.get('/foo/bar').data, b'bar')
|
||||
|
||||
|
||||
class BlueprintTestCase(FlaskTestCase):
|
||||
|
||||
def test_blueprint_specific_error_handling(self):
|
||||
frontend = flask.Blueprint('frontend', __name__)
|
||||
backend = flask.Blueprint('backend', __name__)
|
||||
sideend = flask.Blueprint('sideend', __name__)
|
||||
|
||||
@frontend.errorhandler(403)
|
||||
def frontend_forbidden(e):
|
||||
return 'frontend says no', 403
|
||||
|
||||
@frontend.route('/frontend-no')
|
||||
def frontend_no():
|
||||
flask.abort(403)
|
||||
|
||||
@backend.errorhandler(403)
|
||||
def backend_forbidden(e):
|
||||
return 'backend says no', 403
|
||||
|
||||
@backend.route('/backend-no')
|
||||
def backend_no():
|
||||
flask.abort(403)
|
||||
|
||||
@sideend.route('/what-is-a-sideend')
|
||||
def sideend_no():
|
||||
flask.abort(403)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(frontend)
|
||||
app.register_blueprint(backend)
|
||||
app.register_blueprint(sideend)
|
||||
|
||||
@app.errorhandler(403)
|
||||
def app_forbidden(e):
|
||||
return 'application itself says no', 403
|
||||
|
||||
c = app.test_client()
|
||||
|
||||
self.assert_equal(c.get('/frontend-no').data, b'frontend says no')
|
||||
self.assert_equal(c.get('/backend-no').data, b'backend says no')
|
||||
self.assert_equal(c.get('/what-is-a-sideend').data, b'application itself says no')
|
||||
|
||||
def test_blueprint_url_definitions(self):
|
||||
bp = flask.Blueprint('test', __name__)
|
||||
|
||||
@bp.route('/foo', defaults={'baz': 42})
|
||||
def foo(bar, baz):
|
||||
return '%s/%d' % (bar, baz)
|
||||
|
||||
@bp.route('/bar')
|
||||
def bar(bar):
|
||||
return text_type(bar)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23})
|
||||
app.register_blueprint(bp, url_prefix='/2', url_defaults={'bar': 19})
|
||||
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/1/foo').data, b'23/42')
|
||||
self.assert_equal(c.get('/2/foo').data, b'19/42')
|
||||
self.assert_equal(c.get('/1/bar').data, b'23')
|
||||
self.assert_equal(c.get('/2/bar').data, b'19')
|
||||
|
||||
def test_blueprint_url_processors(self):
|
||||
bp = flask.Blueprint('frontend', __name__, url_prefix='/<lang_code>')
|
||||
|
||||
@bp.url_defaults
|
||||
def add_language_code(endpoint, values):
|
||||
values.setdefault('lang_code', flask.g.lang_code)
|
||||
|
||||
@bp.url_value_preprocessor
|
||||
def pull_lang_code(endpoint, values):
|
||||
flask.g.lang_code = values.pop('lang_code')
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
return flask.url_for('.about')
|
||||
|
||||
@bp.route('/about')
|
||||
def about():
|
||||
return flask.url_for('.index')
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp)
|
||||
|
||||
c = app.test_client()
|
||||
|
||||
self.assert_equal(c.get('/de/').data, b'/de/about')
|
||||
self.assert_equal(c.get('/de/about').data, b'/de/')
|
||||
|
||||
def test_templates_and_static(self):
|
||||
from blueprintapp import app
|
||||
c = app.test_client()
|
||||
|
||||
rv = c.get('/')
|
||||
self.assert_equal(rv.data, b'Hello from the Frontend')
|
||||
rv = c.get('/admin/')
|
||||
self.assert_equal(rv.data, b'Hello from the Admin')
|
||||
rv = c.get('/admin/index2')
|
||||
self.assert_equal(rv.data, b'Hello from the Admin')
|
||||
rv = c.get('/admin/static/test.txt')
|
||||
self.assert_equal(rv.data.strip(), b'Admin File')
|
||||
rv.close()
|
||||
rv = c.get('/admin/static/css/test.css')
|
||||
self.assert_equal(rv.data.strip(), b'/* nested file */')
|
||||
rv.close()
|
||||
|
||||
# try/finally, in case other tests use this app for Blueprint tests.
|
||||
max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT']
|
||||
try:
|
||||
expected_max_age = 3600
|
||||
if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == expected_max_age:
|
||||
expected_max_age = 7200
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = expected_max_age
|
||||
rv = c.get('/admin/static/css/test.css')
|
||||
cc = parse_cache_control_header(rv.headers['Cache-Control'])
|
||||
self.assert_equal(cc.max_age, expected_max_age)
|
||||
rv.close()
|
||||
finally:
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default
|
||||
|
||||
with app.test_request_context():
|
||||
self.assert_equal(flask.url_for('admin.static', filename='test.txt'),
|
||||
'/admin/static/test.txt')
|
||||
|
||||
with app.test_request_context():
|
||||
try:
|
||||
flask.render_template('missing.html')
|
||||
except TemplateNotFound as e:
|
||||
self.assert_equal(e.name, 'missing.html')
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
|
||||
with flask.Flask(__name__).test_request_context():
|
||||
self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
|
||||
|
||||
def test_default_static_cache_timeout(self):
|
||||
app = flask.Flask(__name__)
|
||||
class MyBlueprint(flask.Blueprint):
|
||||
def get_send_file_max_age(self, filename):
|
||||
return 100
|
||||
|
||||
blueprint = MyBlueprint('blueprint', __name__, static_folder='static')
|
||||
app.register_blueprint(blueprint)
|
||||
|
||||
# try/finally, in case other tests use this app for Blueprint tests.
|
||||
max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT']
|
||||
try:
|
||||
with app.test_request_context():
|
||||
unexpected_max_age = 3600
|
||||
if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == unexpected_max_age:
|
||||
unexpected_max_age = 7200
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = unexpected_max_age
|
||||
rv = blueprint.send_static_file('index.html')
|
||||
cc = parse_cache_control_header(rv.headers['Cache-Control'])
|
||||
self.assert_equal(cc.max_age, 100)
|
||||
rv.close()
|
||||
finally:
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default
|
||||
|
||||
def test_templates_list(self):
|
||||
from blueprintapp import app
|
||||
templates = sorted(app.jinja_env.list_templates())
|
||||
self.assert_equal(templates, ['admin/index.html',
|
||||
'frontend/index.html'])
|
||||
|
||||
def test_dotted_names(self):
|
||||
frontend = flask.Blueprint('myapp.frontend', __name__)
|
||||
backend = flask.Blueprint('myapp.backend', __name__)
|
||||
|
||||
@frontend.route('/fe')
|
||||
def frontend_index():
|
||||
return flask.url_for('myapp.backend.backend_index')
|
||||
|
||||
@frontend.route('/fe2')
|
||||
def frontend_page2():
|
||||
return flask.url_for('.frontend_index')
|
||||
|
||||
@backend.route('/be')
|
||||
def backend_index():
|
||||
return flask.url_for('myapp.frontend.frontend_index')
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(frontend)
|
||||
app.register_blueprint(backend)
|
||||
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/fe').data.strip(), b'/be')
|
||||
self.assert_equal(c.get('/fe2').data.strip(), b'/fe')
|
||||
self.assert_equal(c.get('/be').data.strip(), b'/fe')
|
||||
|
||||
def test_dotted_names_from_app(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.testing = True
|
||||
test = flask.Blueprint('test', __name__)
|
||||
|
||||
@app.route('/')
|
||||
def app_index():
|
||||
return flask.url_for('test.index')
|
||||
|
||||
@test.route('/test/')
|
||||
def index():
|
||||
return flask.url_for('app_index')
|
||||
|
||||
app.register_blueprint(test)
|
||||
|
||||
with app.test_client() as c:
|
||||
rv = c.get('/')
|
||||
self.assert_equal(rv.data, b'/test/')
|
||||
|
||||
def test_empty_url_defaults(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
|
||||
@bp.route('/', defaults={'page': 1})
|
||||
@bp.route('/page/<int:page>')
|
||||
def something(page):
|
||||
return str(page)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp)
|
||||
|
||||
c = app.test_client()
|
||||
self.assert_equal(c.get('/').data, b'1')
|
||||
self.assert_equal(c.get('/page/2').data, b'2')
|
||||
|
||||
def test_route_decorator_custom_endpoint(self):
|
||||
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
|
||||
@bp.route('/foo')
|
||||
def foo():
|
||||
return flask.request.endpoint
|
||||
|
||||
@bp.route('/bar', endpoint='bar')
|
||||
def foo_bar():
|
||||
return flask.request.endpoint
|
||||
|
||||
@bp.route('/bar/123', endpoint='123')
|
||||
def foo_bar_foo():
|
||||
return flask.request.endpoint
|
||||
|
||||
@bp.route('/bar/foo')
|
||||
def bar_foo():
|
||||
return flask.request.endpoint
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.request.endpoint
|
||||
|
||||
c = app.test_client()
|
||||
self.assertEqual(c.get('/').data, b'index')
|
||||
self.assertEqual(c.get('/py/foo').data, b'bp.foo')
|
||||
self.assertEqual(c.get('/py/bar').data, b'bp.bar')
|
||||
self.assertEqual(c.get('/py/bar/123').data, b'bp.123')
|
||||
self.assertEqual(c.get('/py/bar/foo').data, b'bp.bar_foo')
|
||||
|
||||
def test_route_decorator_custom_endpoint_with_dots(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
|
||||
@bp.route('/foo')
|
||||
def foo():
|
||||
return flask.request.endpoint
|
||||
|
||||
try:
|
||||
@bp.route('/bar', endpoint='bar.bar')
|
||||
def foo_bar():
|
||||
return flask.request.endpoint
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('expected AssertionError not raised')
|
||||
|
||||
try:
|
||||
@bp.route('/bar/123', endpoint='bar.123')
|
||||
def foo_bar_foo():
|
||||
return flask.request.endpoint
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('expected AssertionError not raised')
|
||||
|
||||
def foo_foo_foo():
|
||||
pass
|
||||
|
||||
self.assertRaises(
|
||||
AssertionError,
|
||||
lambda: bp.add_url_rule(
|
||||
'/bar/123', endpoint='bar.123', view_func=foo_foo_foo
|
||||
)
|
||||
)
|
||||
|
||||
self.assertRaises(
|
||||
AssertionError,
|
||||
bp.route('/bar/123', endpoint='bar.123'),
|
||||
lambda: None
|
||||
)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
|
||||
c = app.test_client()
|
||||
self.assertEqual(c.get('/py/foo').data, b'bp.foo')
|
||||
# The rule's didn't actually made it through
|
||||
rv = c.get('/py/bar')
|
||||
assert rv.status_code == 404
|
||||
rv = c.get('/py/bar/123')
|
||||
assert rv.status_code == 404
|
||||
|
||||
def test_template_filter(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_filter()
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('my_reverse', app.jinja_env.filters.keys())
|
||||
self.assert_equal(app.jinja_env.filters['my_reverse'], my_reverse)
|
||||
self.assert_equal(app.jinja_env.filters['my_reverse']('abcd'), 'dcba')
|
||||
|
||||
def test_add_template_filter(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
bp.add_app_template_filter(my_reverse)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('my_reverse', app.jinja_env.filters.keys())
|
||||
self.assert_equal(app.jinja_env.filters['my_reverse'], my_reverse)
|
||||
self.assert_equal(app.jinja_env.filters['my_reverse']('abcd'), 'dcba')
|
||||
|
||||
def test_template_filter_with_name(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_filter('strrev')
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('strrev', app.jinja_env.filters.keys())
|
||||
self.assert_equal(app.jinja_env.filters['strrev'], my_reverse)
|
||||
self.assert_equal(app.jinja_env.filters['strrev']('abcd'), 'dcba')
|
||||
|
||||
def test_add_template_filter_with_name(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
bp.add_app_template_filter(my_reverse, 'strrev')
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('strrev', app.jinja_env.filters.keys())
|
||||
self.assert_equal(app.jinja_env.filters['strrev'], my_reverse)
|
||||
self.assert_equal(app.jinja_env.filters['strrev']('abcd'), 'dcba')
|
||||
|
||||
def test_template_filter_with_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_filter()
|
||||
def super_reverse(s):
|
||||
return s[::-1]
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_filter.html', value='abcd')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'dcba')
|
||||
|
||||
def test_template_filter_after_route_with_template(self):
|
||||
app = flask.Flask(__name__)
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_filter.html', value='abcd')
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_filter()
|
||||
def super_reverse(s):
|
||||
return s[::-1]
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'dcba')
|
||||
|
||||
def test_add_template_filter_with_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def super_reverse(s):
|
||||
return s[::-1]
|
||||
bp.add_app_template_filter(super_reverse)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_filter.html', value='abcd')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'dcba')
|
||||
|
||||
def test_template_filter_with_name_and_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_filter('super_reverse')
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_filter.html', value='abcd')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'dcba')
|
||||
|
||||
def test_add_template_filter_with_name_and_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def my_reverse(s):
|
||||
return s[::-1]
|
||||
bp.add_app_template_filter(my_reverse, 'super_reverse')
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_filter.html', value='abcd')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_equal(rv.data, b'dcba')
|
||||
|
||||
def test_template_test(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_test()
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('is_boolean', app.jinja_env.tests.keys())
|
||||
self.assert_equal(app.jinja_env.tests['is_boolean'], is_boolean)
|
||||
self.assert_true(app.jinja_env.tests['is_boolean'](False))
|
||||
|
||||
def test_add_template_test(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
bp.add_app_template_test(is_boolean)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('is_boolean', app.jinja_env.tests.keys())
|
||||
self.assert_equal(app.jinja_env.tests['is_boolean'], is_boolean)
|
||||
self.assert_true(app.jinja_env.tests['is_boolean'](False))
|
||||
|
||||
def test_template_test_with_name(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_test('boolean')
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('boolean', app.jinja_env.tests.keys())
|
||||
self.assert_equal(app.jinja_env.tests['boolean'], is_boolean)
|
||||
self.assert_true(app.jinja_env.tests['boolean'](False))
|
||||
|
||||
def test_add_template_test_with_name(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
bp.add_app_template_test(is_boolean, 'boolean')
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
self.assert_in('boolean', app.jinja_env.tests.keys())
|
||||
self.assert_equal(app.jinja_env.tests['boolean'], is_boolean)
|
||||
self.assert_true(app.jinja_env.tests['boolean'](False))
|
||||
|
||||
def test_template_test_with_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_test()
|
||||
def boolean(value):
|
||||
return isinstance(value, bool)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_test.html', value=False)
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_in(b'Success!', rv.data)
|
||||
|
||||
def test_template_test_after_route_with_template(self):
|
||||
app = flask.Flask(__name__)
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_test.html', value=False)
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_test()
|
||||
def boolean(value):
|
||||
return isinstance(value, bool)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_in(b'Success!', rv.data)
|
||||
|
||||
def test_add_template_test_with_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def boolean(value):
|
||||
return isinstance(value, bool)
|
||||
bp.add_app_template_test(boolean)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_test.html', value=False)
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_in(b'Success!', rv.data)
|
||||
|
||||
def test_template_test_with_name_and_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
@bp.app_template_test('boolean')
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_test.html', value=False)
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_in(b'Success!', rv.data)
|
||||
|
||||
def test_add_template_test_with_name_and_template(self):
|
||||
bp = flask.Blueprint('bp', __name__)
|
||||
def is_boolean(value):
|
||||
return isinstance(value, bool)
|
||||
bp.add_app_template_test(is_boolean, 'boolean')
|
||||
app = flask.Flask(__name__)
|
||||
app.register_blueprint(bp, url_prefix='/py')
|
||||
@app.route('/')
|
||||
def index():
|
||||
return flask.render_template('template_test.html', value=False)
|
||||
rv = app.test_client().get('/')
|
||||
self.assert_in(b'Success!', rv.data)
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(BlueprintTestCase))
|
||||
suite.addTest(unittest.makeSuite(ModuleTestCase))
|
||||
return suite
|
BIN
lib/flask/testsuite/blueprints.pyc
Normal file
BIN
lib/flask/testsuite/blueprints.pyc
Normal file
Binary file not shown.
299
lib/flask/testsuite/config.py
Normal file
299
lib/flask/testsuite/config.py
Normal file
|
@ -0,0 +1,299 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.testsuite.config
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Configuration and instances.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import flask
|
||||
import pkgutil
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from flask.testsuite import FlaskTestCase
|
||||
|
||||
|
||||
# config keys used for the ConfigTestCase
|
||||
TEST_KEY = 'foo'
|
||||
SECRET_KEY = 'devkey'
|
||||
|
||||
|
||||
class ConfigTestCase(FlaskTestCase):
|
||||
|
||||
def common_object_test(self, app):
|
||||
self.assert_equal(app.secret_key, 'devkey')
|
||||
self.assert_equal(app.config['TEST_KEY'], 'foo')
|
||||
self.assert_not_in('ConfigTestCase', app.config)
|
||||
|
||||
def test_config_from_file(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config.from_pyfile(__file__.rsplit('.', 1)[0] + '.py')
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_config_from_object(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_config_from_class(self):
|
||||
class Base(object):
|
||||
TEST_KEY = 'foo'
|
||||
class Test(Base):
|
||||
SECRET_KEY = 'devkey'
|
||||
app = flask.Flask(__name__)
|
||||
app.config.from_object(Test)
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_config_from_envvar(self):
|
||||
env = os.environ
|
||||
try:
|
||||
os.environ = {}
|
||||
app = flask.Flask(__name__)
|
||||
try:
|
||||
app.config.from_envvar('FOO_SETTINGS')
|
||||
except RuntimeError as e:
|
||||
self.assert_true("'FOO_SETTINGS' is not set" in str(e))
|
||||
else:
|
||||
self.assert_true(0, 'expected exception')
|
||||
self.assert_false(app.config.from_envvar('FOO_SETTINGS', silent=True))
|
||||
|
||||
os.environ = {'FOO_SETTINGS': __file__.rsplit('.', 1)[0] + '.py'}
|
||||
self.assert_true(app.config.from_envvar('FOO_SETTINGS'))
|
||||
self.common_object_test(app)
|
||||
finally:
|
||||
os.environ = env
|
||||
|
||||
def test_config_from_envvar_missing(self):
|
||||
env = os.environ
|
||||
try:
|
||||
os.environ = {'FOO_SETTINGS': 'missing.cfg'}
|
||||
try:
|
||||
app = flask.Flask(__name__)
|
||||
app.config.from_envvar('FOO_SETTINGS')
|
||||
except IOError as e:
|
||||
msg = str(e)
|
||||
self.assert_true(msg.startswith('[Errno 2] Unable to load configuration '
|
||||
'file (No such file or directory):'))
|
||||
self.assert_true(msg.endswith("missing.cfg'"))
|
||||
else:
|
||||
self.fail('expected IOError')
|
||||
self.assertFalse(app.config.from_envvar('FOO_SETTINGS', silent=True))
|
||||
finally:
|
||||
os.environ = env
|
||||
|
||||
def test_config_missing(self):
|
||||
app = flask.Flask(__name__)
|
||||
try:
|
||||
app.config.from_pyfile('missing.cfg')
|
||||
except IOError as e:
|
||||
msg = str(e)
|
||||
self.assert_true(msg.startswith('[Errno 2] Unable to load configuration '
|
||||
'file (No such file or directory):'))
|
||||
self.assert_true(msg.endswith("missing.cfg'"))
|
||||
else:
|
||||
self.assert_true(0, 'expected config')
|
||||
self.assert_false(app.config.from_pyfile('missing.cfg', silent=True))
|
||||
|
||||
def test_session_lifetime(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = 42
|
||||
self.assert_equal(app.permanent_session_lifetime.seconds, 42)
|
||||
|
||||
|
||||
class LimitedLoaderMockWrapper(object):
|
||||
def __init__(self, loader):
|
||||
self.loader = loader
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in ('archive', 'get_filename'):
|
||||
msg = 'Mocking a loader which does not have `%s.`' % name
|
||||
raise AttributeError(msg)
|
||||
return getattr(self.loader, name)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def patch_pkgutil_get_loader(wrapper_class=LimitedLoaderMockWrapper):
|
||||
"""Patch pkgutil.get_loader to give loader without get_filename or archive.
|
||||
|
||||
This provides for tests where a system has custom loaders, e.g. Google App
|
||||
Engine's HardenedModulesHook, which have neither the `get_filename` method
|
||||
nor the `archive` attribute.
|
||||
"""
|
||||
old_get_loader = pkgutil.get_loader
|
||||
def get_loader(*args, **kwargs):
|
||||
return wrapper_class(old_get_loader(*args, **kwargs))
|
||||
try:
|
||||
pkgutil.get_loader = get_loader
|
||||
yield
|
||||
finally:
|
||||
pkgutil.get_loader = old_get_loader
|
||||
|
||||
|
||||
class InstanceTestCase(FlaskTestCase):
|
||||
|
||||
def test_explicit_instance_paths(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
try:
|
||||
flask.Flask(__name__, instance_path='instance')
|
||||
except ValueError as e:
|
||||
self.assert_in('must be absolute', str(e))
|
||||
else:
|
||||
self.fail('Expected value error')
|
||||
|
||||
app = flask.Flask(__name__, instance_path=here)
|
||||
self.assert_equal(app.instance_path, here)
|
||||
|
||||
def test_main_module_paths(self):
|
||||
# Test an app with '__main__' as the import name, uses cwd.
|
||||
from main_app import app
|
||||
here = os.path.abspath(os.getcwd())
|
||||
self.assert_equal(app.instance_path, os.path.join(here, 'instance'))
|
||||
if 'main_app' in sys.modules:
|
||||
del sys.modules['main_app']
|
||||
|
||||
def test_uninstalled_module_paths(self):
|
||||
from config_module_app import app
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
|
||||
|
||||
def test_uninstalled_package_paths(self):
|
||||
from config_package_app import app
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
|
||||
|
||||
def test_installed_module_paths(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
site_packages = os.path.join(expected_prefix, 'lib', 'python2.5', 'site-packages')
|
||||
sys.path.append(site_packages)
|
||||
try:
|
||||
import site_app
|
||||
self.assert_equal(site_app.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'site_app-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(site_packages)
|
||||
if 'site_app' in sys.modules:
|
||||
del sys.modules['site_app']
|
||||
|
||||
def test_installed_module_paths_with_limited_loader(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
site_packages = os.path.join(expected_prefix, 'lib', 'python2.5', 'site-packages')
|
||||
sys.path.append(site_packages)
|
||||
with patch_pkgutil_get_loader():
|
||||
try:
|
||||
import site_app
|
||||
self.assert_equal(site_app.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'site_app-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(site_packages)
|
||||
if 'site_app' in sys.modules:
|
||||
del sys.modules['site_app']
|
||||
|
||||
def test_installed_package_paths(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
installed_path = os.path.join(expected_prefix, 'path')
|
||||
sys.path.append(installed_path)
|
||||
try:
|
||||
import installed_package
|
||||
self.assert_equal(installed_package.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'installed_package-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(installed_path)
|
||||
if 'installed_package' in sys.modules:
|
||||
del sys.modules['installed_package']
|
||||
|
||||
def test_installed_package_paths_with_limited_loader(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
installed_path = os.path.join(expected_prefix, 'path')
|
||||
sys.path.append(installed_path)
|
||||
with patch_pkgutil_get_loader():
|
||||
try:
|
||||
import installed_package
|
||||
self.assert_equal(installed_package.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'installed_package-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(installed_path)
|
||||
if 'installed_package' in sys.modules:
|
||||
del sys.modules['installed_package']
|
||||
|
||||
def test_prefix_package_paths(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
site_packages = os.path.join(expected_prefix, 'lib', 'python2.5', 'site-packages')
|
||||
sys.path.append(site_packages)
|
||||
try:
|
||||
import site_package
|
||||
self.assert_equal(site_package.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'site_package-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(site_packages)
|
||||
if 'site_package' in sys.modules:
|
||||
del sys.modules['site_package']
|
||||
|
||||
def test_prefix_package_paths_with_limited_loader(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
site_packages = os.path.join(expected_prefix, 'lib', 'python2.5', 'site-packages')
|
||||
sys.path.append(site_packages)
|
||||
with patch_pkgutil_get_loader():
|
||||
try:
|
||||
import site_package
|
||||
self.assert_equal(site_package.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'site_package-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(site_packages)
|
||||
if 'site_package' in sys.modules:
|
||||
del sys.modules['site_package']
|
||||
|
||||
def test_egg_installed_paths(self):
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
expected_prefix = os.path.join(here, 'test_apps')
|
||||
real_prefix, sys.prefix = sys.prefix, expected_prefix
|
||||
site_packages = os.path.join(expected_prefix, 'lib', 'python2.5', 'site-packages')
|
||||
egg_path = os.path.join(site_packages, 'SiteEgg.egg')
|
||||
sys.path.append(site_packages)
|
||||
sys.path.append(egg_path)
|
||||
try:
|
||||
import site_egg # in SiteEgg.egg
|
||||
self.assert_equal(site_egg.app.instance_path,
|
||||
os.path.join(expected_prefix, 'var',
|
||||
'site_egg-instance'))
|
||||
finally:
|
||||
sys.prefix = real_prefix
|
||||
sys.path.remove(site_packages)
|
||||
sys.path.remove(egg_path)
|
||||
if 'site_egg' in sys.modules:
|
||||
del sys.modules['site_egg']
|
||||
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(ConfigTestCase))
|
||||
suite.addTest(unittest.makeSuite(InstanceTestCase))
|
||||
return suite
|
BIN
lib/flask/testsuite/config.pyc
Normal file
BIN
lib/flask/testsuite/config.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue