forked from orbit-oss/flask
Merge branch 'master' into module-support
Conflicts: CHANGES docs/_themes tests/flask_tests.py
This commit is contained in:
commit
9fa4f94ad8
9 changed files with 177 additions and 355 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "docs/_themes"]
|
||||||
|
path = docs/_themes
|
||||||
|
url = git://github.com/mitsuhiko/flask-sphinx-themes.git
|
||||||
1
CHANGES
1
CHANGES
|
|
@ -15,6 +15,7 @@ Version 0.2
|
||||||
view function.
|
view function.
|
||||||
- server listens on 127.0.0.1 by default now to fix issues with chrome.
|
- server listens on 127.0.0.1 by default now to fix issues with chrome.
|
||||||
- added external URL support.
|
- added external URL support.
|
||||||
|
- added support for :func:`~flask.send_file`
|
||||||
- module support and internal request handling refactoring
|
- module support and internal request handling refactoring
|
||||||
to better support pluggable applications.
|
to better support pluggable applications.
|
||||||
|
|
||||||
|
|
|
||||||
1
docs/_themes
Submodule
1
docs/_themes
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 11cb6b51c9ea3bc8f94afa3d7411b617f9db2570
|
||||||
344
docs/_themes/flasky/static/flasky.css_t
vendored
344
docs/_themes/flasky/static/flasky.css_t
vendored
|
|
@ -1,344 +0,0 @@
|
||||||
/*
|
|
||||||
* flasky.css_t
|
|
||||||
* ~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx stylesheet -- flasky theme based on nature theme.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("basic.css");
|
|
||||||
|
|
||||||
/* -- page layout ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Georgia', serif;
|
|
||||||
font-size: 17px;
|
|
||||||
background-color: #ddd;
|
|
||||||
color: #000;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document {
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.documentwrapper {
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0 0 0 230px;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 1px solid #B1B4B6;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #3E4349;
|
|
||||||
padding: 0 30px 30px 30px;
|
|
||||||
min-height: 34em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.floatingflask {
|
|
||||||
padding: 0 0 10px 10px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
margin-top: -70px;
|
|
||||||
text-align: right;
|
|
||||||
color: #888;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer a {
|
|
||||||
color: #888;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related {
|
|
||||||
line-height: 32px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related ul {
|
|
||||||
padding: 0 0 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper {
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper p.logo {
|
|
||||||
padding: 20px 0 10px 0;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3,
|
|
||||||
div.sphinxsidebar h4 {
|
|
||||||
font-family: 'Garamond', 'Georgia', serif;
|
|
||||||
color: #222;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 20px 0 5px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h4 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3 a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p {
|
|
||||||
color: #555;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar a {
|
|
||||||
color: #444;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
font-family: 'Georgia', serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- body styles ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #004B6B;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #6D4100;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
padding-bottom: 40px; /* saved for footer */
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body h1,
|
|
||||||
div.body h2,
|
|
||||||
div.body h3,
|
|
||||||
div.body h4,
|
|
||||||
div.body h5,
|
|
||||||
div.body h6 {
|
|
||||||
font-family: 'Garamond', 'Georgia', serif;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 30px 0px 10px 0px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body h1 { margin-top: 0; padding-top: 20px; font-size: 240%; }
|
|
||||||
div.body h2 { font-size: 180%; }
|
|
||||||
div.body h3 { font-size: 150%; }
|
|
||||||
div.body h4 { font-size: 130%; }
|
|
||||||
div.body h5 { font-size: 100%; }
|
|
||||||
div.body h6 { font-size: 100%; }
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
color: white;
|
|
||||||
padding: 0 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink:hover {
|
|
||||||
color: #444;
|
|
||||||
background: #eaeaea;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body p, div.body dd, div.body li {
|
|
||||||
line-height: 1.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition {
|
|
||||||
background: #fafafa;
|
|
||||||
margin: 20px -30px;
|
|
||||||
padding: 10px 30px;
|
|
||||||
border-top: 1px solid #ccc;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.admonition-title {
|
|
||||||
font-family: 'Garamond', 'Georgia', serif;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 24px;
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.highlight{
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:target, .highlight {
|
|
||||||
background: #FAF3E8;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.note {
|
|
||||||
background-color: #eee;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.seealso {
|
|
||||||
background-color: #ffc;
|
|
||||||
border: 1px solid #ff6;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.warning {
|
|
||||||
background-color: #ffe4e4;
|
|
||||||
border: 1px solid #f66;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title:after {
|
|
||||||
content: ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, tt {
|
|
||||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.screenshot {
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descname, tt.descclassname {
|
|
||||||
font-size: 0.95em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descname {
|
|
||||||
padding-right: 0.08em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.screenshot {
|
|
||||||
-moz-box-shadow: 2px 2px 4px #eee;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
|
||||||
box-shadow: 2px 2px 4px #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
border: 1px solid #888;
|
|
||||||
-moz-box-shadow: 2px 2px 4px #eee;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
|
||||||
box-shadow: 2px 2px 4px #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th {
|
|
||||||
border: 1px solid #888;
|
|
||||||
padding: 0.25em 0.7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list, table.footnote {
|
|
||||||
border: none;
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote {
|
|
||||||
margin: 15px 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list th {
|
|
||||||
padding: 0 0.8em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list td {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote td {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl dd {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
background: #eee;
|
|
||||||
padding: 7px 30px;
|
|
||||||
margin: 15px -30px;
|
|
||||||
line-height: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl pre {
|
|
||||||
margin-left: -60px;
|
|
||||||
padding-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl dl pre {
|
|
||||||
margin-left: -90px;
|
|
||||||
padding-left: 90px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt {
|
|
||||||
background-color: #ecf0f3;
|
|
||||||
color: #222;
|
|
||||||
/* padding: 1px 2px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.xref, a tt {
|
|
||||||
background-color: #FBFBFB;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover tt {
|
|
||||||
background: #EEE;
|
|
||||||
}
|
|
||||||
3
docs/_themes/flasky/theme.conf
vendored
3
docs/_themes/flasky/theme.conf
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
[theme]
|
|
||||||
inherit = basic
|
|
||||||
stylesheet = flasky.css
|
|
||||||
|
|
@ -222,6 +222,8 @@ Useful Functions and Classes
|
||||||
|
|
||||||
.. autofunction:: redirect
|
.. autofunction:: redirect
|
||||||
|
|
||||||
|
.. autofunction:: send_file
|
||||||
|
|
||||||
.. autofunction:: escape
|
.. autofunction:: escape
|
||||||
|
|
||||||
.. autoclass:: Markup
|
.. autoclass:: Markup
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import sys, os
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path.append(os.path.abspath('.'))
|
sys.path.append(os.path.abspath('_themes'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -79,9 +79,6 @@ exclude_patterns = ['_build']
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
#show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'flaskext.FlaskyStyle'
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
#modindex_common_prefix = []
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
@ -90,7 +87,7 @@ pygments_style = 'flaskext.FlaskyStyle'
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
html_theme = 'flasky'
|
html_theme = 'flask'
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
|
|
@ -237,3 +234,5 @@ intersphinx_mapping = {
|
||||||
'http://www.sqlalchemy.org/docs/': None,
|
'http://www.sqlalchemy.org/docs/': None,
|
||||||
'http://wtforms.simplecodes.com/docs/0.5/': None
|
'http://wtforms.simplecodes.com/docs/0.5/': None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||||
|
|
|
||||||
82
flask.py
82
flask.py
|
|
@ -12,13 +12,14 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import mimetypes
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
||||||
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
||||||
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
|
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
|
||||||
ImmutableDict, cached_property
|
ImmutableDict, cached_property, wrap_file, Headers
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
from werkzeug.contrib.securecookie import SecureCookie
|
||||||
|
|
@ -268,6 +269,78 @@ def jsonify(*args, **kwargs):
|
||||||
indent=None if request.is_xhr else 2), mimetype='application/json')
|
indent=None if request.is_xhr else 2), mimetype='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||||
|
attachment_filename=None):
|
||||||
|
"""Sends the contents of a file to the client. This will use the
|
||||||
|
most efficient method available and configured. By default it will
|
||||||
|
try to use the WSGI server's file_wrapper support. Alternatively
|
||||||
|
you can set the application's :attr:`~Flask.use_x_sendfile` attribute
|
||||||
|
to ``True`` to directly emit an `X-Sendfile` header. This however
|
||||||
|
requires support of the underlying webserver for `X-Sendfile`.
|
||||||
|
|
||||||
|
By default it will try to guess the mimetype for you, but you can
|
||||||
|
also explicitly provide one. For extra security you probably want
|
||||||
|
to sent certain files as attachment (HTML for instance).
|
||||||
|
|
||||||
|
Please never pass filenames to this function from user sources without
|
||||||
|
checking them first. Something like this is usually sufficient to
|
||||||
|
avoid security problems::
|
||||||
|
|
||||||
|
if '..' in filename or filename.startswith('/'):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
.. versionadded:: 0.2
|
||||||
|
|
||||||
|
:param filename_or_fp: the filename of the file to send. This is
|
||||||
|
relative to the :attr:`~Flask.root_path` if a
|
||||||
|
relative path is specified.
|
||||||
|
Alternatively a file object might be provided
|
||||||
|
in which case `X-Sendfile` might not work and
|
||||||
|
fall back to the traditional method.
|
||||||
|
:param mimetype: the mimetype of the file if provided, otherwise
|
||||||
|
auto detection happens.
|
||||||
|
:param as_attachment: set to `True` if you want to send this file with
|
||||||
|
a ``Content-Disposition: attachment`` header.
|
||||||
|
:param attachment_filename: the filename for the attachment if it
|
||||||
|
differs from the file's filename.
|
||||||
|
"""
|
||||||
|
if isinstance(filename_or_fp, basestring):
|
||||||
|
filename = filename_or_fp
|
||||||
|
file = None
|
||||||
|
else:
|
||||||
|
file = filename_or_fp
|
||||||
|
filename = getattr(file, 'name', None)
|
||||||
|
if filename is not None:
|
||||||
|
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
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
if file is None:
|
||||||
|
file = open(filename, 'rb')
|
||||||
|
data = wrap_file(request.environ, file)
|
||||||
|
|
||||||
|
return Response(data, mimetype=mimetype, headers=headers,
|
||||||
|
direct_passthrough=True)
|
||||||
|
|
||||||
|
|
||||||
def render_template(template_name, **context):
|
def render_template(template_name, **context):
|
||||||
"""Renders a template from the template folder with the given
|
"""Renders a template from the template folder with the given
|
||||||
context.
|
context.
|
||||||
|
|
@ -554,6 +627,13 @@ class Flask(_PackageBoundObject):
|
||||||
#: permanent session survive for roughly one month.
|
#: permanent session survive for roughly one month.
|
||||||
permanent_session_lifetime = timedelta(days=31)
|
permanent_session_lifetime = timedelta(days=31)
|
||||||
|
|
||||||
|
#: Enable this if you want to use the X-Sendfile feature. Keep in
|
||||||
|
#: mind that the server has to support this. This only affects files
|
||||||
|
#: sent with the :func:`send_file` method.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.2
|
||||||
|
use_x_sendfile = False
|
||||||
|
|
||||||
#: options that are passed directly to the Jinja2 environment
|
#: options that are passed directly to the Jinja2 environment
|
||||||
jinja_options = ImmutableDict(
|
jinja_options = ImmutableDict(
|
||||||
autoescape=True,
|
autoescape=True,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ import flask
|
||||||
import unittest
|
import unittest
|
||||||
import tempfile
|
import tempfile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from werkzeug import parse_date
|
from werkzeug import parse_date, parse_options_header
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
|
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
|
||||||
|
|
@ -474,6 +475,87 @@ class ModuleTestCase(unittest.TestCase):
|
||||||
assert app.test_client().get('/admin/').data == '42'
|
assert app.test_client().get('/admin/').data == '42'
|
||||||
|
|
||||||
|
|
||||||
|
class SendfileTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_send_file_regular(self):
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
with app.test_request_context():
|
||||||
|
rv = flask.send_file('static/index.html')
|
||||||
|
assert rv.direct_passthrough
|
||||||
|
assert rv.mimetype == 'text/html'
|
||||||
|
with app.open_resource('static/index.html') as f:
|
||||||
|
assert rv.data == f.read()
|
||||||
|
|
||||||
|
def test_send_file_xsendfile(self):
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.use_x_sendfile = True
|
||||||
|
with app.test_request_context():
|
||||||
|
rv = flask.send_file('static/index.html')
|
||||||
|
assert rv.direct_passthrough
|
||||||
|
assert 'x-sendfile' in rv.headers
|
||||||
|
assert rv.headers['x-sendfile'] == \
|
||||||
|
os.path.join(app.root_path, 'static/index.html')
|
||||||
|
assert rv.mimetype == 'text/html'
|
||||||
|
|
||||||
|
def test_send_file_object(self):
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
with app.test_request_context():
|
||||||
|
f = open(os.path.join(app.root_path, 'static/index.html'))
|
||||||
|
rv = flask.send_file(f)
|
||||||
|
with app.open_resource('static/index.html') as f:
|
||||||
|
assert rv.data == f.read()
|
||||||
|
assert rv.mimetype == 'text/html'
|
||||||
|
|
||||||
|
app.use_x_sendfile = True
|
||||||
|
with app.test_request_context():
|
||||||
|
f = open(os.path.join(app.root_path, 'static/index.html'))
|
||||||
|
rv = flask.send_file(f)
|
||||||
|
assert rv.mimetype == 'text/html'
|
||||||
|
assert 'x-sendfile' in rv.headers
|
||||||
|
assert rv.headers['x-sendfile'] == \
|
||||||
|
os.path.join(app.root_path, 'static/index.html')
|
||||||
|
|
||||||
|
app.use_x_sendfile = False
|
||||||
|
with app.test_request_context():
|
||||||
|
f = StringIO('Test')
|
||||||
|
rv = flask.send_file(f)
|
||||||
|
assert rv.data == 'Test'
|
||||||
|
assert rv.mimetype == 'application/octet-stream'
|
||||||
|
f = StringIO('Test')
|
||||||
|
rv = flask.send_file(f, mimetype='text/plain')
|
||||||
|
assert rv.data == 'Test'
|
||||||
|
assert rv.mimetype == 'text/plain'
|
||||||
|
|
||||||
|
app.use_x_sendfile = True
|
||||||
|
with app.test_request_context():
|
||||||
|
f = StringIO('Test')
|
||||||
|
rv = flask.send_file(f)
|
||||||
|
assert 'x-sendfile' not in rv.headers
|
||||||
|
|
||||||
|
def test_attachment(self):
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
with app.test_request_context():
|
||||||
|
f = open(os.path.join(app.root_path, 'static/index.html'))
|
||||||
|
rv = flask.send_file(f, as_attachment=True)
|
||||||
|
value, options = parse_options_header(rv.headers['Content-Disposition'])
|
||||||
|
assert value == 'attachment'
|
||||||
|
|
||||||
|
with app.test_request_context():
|
||||||
|
assert options['filename'] == 'index.html'
|
||||||
|
rv = flask.send_file('static/index.html', as_attachment=True)
|
||||||
|
value, options = parse_options_header(rv.headers['Content-Disposition'])
|
||||||
|
assert value == 'attachment'
|
||||||
|
assert options['filename'] == 'index.html'
|
||||||
|
|
||||||
|
with app.test_request_context():
|
||||||
|
rv = flask.send_file(StringIO('Test'), as_attachment=True,
|
||||||
|
attachment_filename='index.txt')
|
||||||
|
assert rv.mimetype == 'text/plain'
|
||||||
|
value, options = parse_options_header(rv.headers['Content-Disposition'])
|
||||||
|
assert value == 'attachment'
|
||||||
|
assert options['filename'] == 'index.txt'
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
from minitwit_tests import MiniTwitTestCase
|
from minitwit_tests import MiniTwitTestCase
|
||||||
from flaskr_tests import FlaskrTestCase
|
from flaskr_tests import FlaskrTestCase
|
||||||
|
|
@ -481,11 +563,12 @@ def suite():
|
||||||
suite.addTest(unittest.makeSuite(ContextTestCase))
|
suite.addTest(unittest.makeSuite(ContextTestCase))
|
||||||
suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
|
suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
|
||||||
suite.addTest(unittest.makeSuite(TemplatingTestCase))
|
suite.addTest(unittest.makeSuite(TemplatingTestCase))
|
||||||
|
suite.addTest(unittest.makeSuite(SendfileTestCase))
|
||||||
|
suite.addTest(unittest.makeSuite(ModuleTestCase))
|
||||||
if flask.json_available:
|
if flask.json_available:
|
||||||
suite.addTest(unittest.makeSuite(JSONTestCase))
|
suite.addTest(unittest.makeSuite(JSONTestCase))
|
||||||
suite.addTest(unittest.makeSuite(MiniTwitTestCase))
|
suite.addTest(unittest.makeSuite(MiniTwitTestCase))
|
||||||
suite.addTest(unittest.makeSuite(FlaskrTestCase))
|
suite.addTest(unittest.makeSuite(FlaskrTestCase))
|
||||||
suite.addTest(unittest.makeSuite(ModuleTestCase))
|
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue