mirror of https://github.com/mitsuhiko/flask.git
Melvin
10 years ago
602 changed files with 0 additions and 151282 deletions
@ -1,80 +0,0 @@ |
|||||||
# This file must be used with "source bin/activate" *from bash* |
|
||||||
# you cannot run it directly |
|
||||||
|
|
||||||
deactivate () { |
|
||||||
unset pydoc |
|
||||||
|
|
||||||
# reset old environment variables |
|
||||||
if [ -n "$_OLD_VIRTUAL_PATH" ] ; then |
|
||||||
PATH="$_OLD_VIRTUAL_PATH" |
|
||||||
export PATH |
|
||||||
unset _OLD_VIRTUAL_PATH |
|
||||||
fi |
|
||||||
if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then |
|
||||||
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" |
|
||||||
export PYTHONHOME |
|
||||||
unset _OLD_VIRTUAL_PYTHONHOME |
|
||||||
fi |
|
||||||
|
|
||||||
# This should detect bash and zsh, which have a hash command that must |
|
||||||
# be called to get it to forget past commands. Without forgetting |
|
||||||
# past commands the $PATH changes we made may not be respected |
|
||||||
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then |
|
||||||
hash -r 2>/dev/null |
|
||||||
fi |
|
||||||
|
|
||||||
if [ -n "$_OLD_VIRTUAL_PS1" ] ; then |
|
||||||
PS1="$_OLD_VIRTUAL_PS1" |
|
||||||
export PS1 |
|
||||||
unset _OLD_VIRTUAL_PS1 |
|
||||||
fi |
|
||||||
|
|
||||||
unset VIRTUAL_ENV |
|
||||||
if [ ! "$1" = "nondestructive" ] ; then |
|
||||||
# Self destruct! |
|
||||||
unset -f deactivate |
|
||||||
fi |
|
||||||
} |
|
||||||
|
|
||||||
# unset irrelevant variables |
|
||||||
deactivate nondestructive |
|
||||||
|
|
||||||
VIRTUAL_ENV="/home/melvin/development/shortenmyurl/venv" |
|
||||||
export VIRTUAL_ENV |
|
||||||
|
|
||||||
_OLD_VIRTUAL_PATH="$PATH" |
|
||||||
PATH="$VIRTUAL_ENV/bin:$PATH" |
|
||||||
export PATH |
|
||||||
|
|
||||||
# unset PYTHONHOME if set |
|
||||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) |
|
||||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash |
|
||||||
if [ -n "$PYTHONHOME" ] ; then |
|
||||||
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" |
|
||||||
unset PYTHONHOME |
|
||||||
fi |
|
||||||
|
|
||||||
if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then |
|
||||||
_OLD_VIRTUAL_PS1="$PS1" |
|
||||||
if [ "x" != x ] ; then |
|
||||||
PS1="$PS1" |
|
||||||
else |
|
||||||
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then |
|
||||||
# special case for Aspen magic directories |
|
||||||
# see http://www.zetadev.com/software/aspen/ |
|
||||||
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" |
|
||||||
else |
|
||||||
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" |
|
||||||
fi |
|
||||||
fi |
|
||||||
export PS1 |
|
||||||
fi |
|
||||||
|
|
||||||
alias pydoc="python -m pydoc" |
|
||||||
|
|
||||||
# This should detect bash and zsh, which have a hash command that must |
|
||||||
# be called to get it to forget past commands. Without forgetting |
|
||||||
# past commands the $PATH changes we made may not be respected |
|
||||||
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then |
|
||||||
hash -r 2>/dev/null |
|
||||||
fi |
|
@ -1,42 +0,0 @@ |
|||||||
# This file must be used with "source bin/activate.csh" *from csh*. |
|
||||||
# You cannot run it directly. |
|
||||||
# Created by Davide Di Blasi <davidedb@gmail.com>. |
|
||||||
|
|
||||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' |
|
||||||
|
|
||||||
# Unset irrelevant variables. |
|
||||||
deactivate nondestructive |
|
||||||
|
|
||||||
setenv VIRTUAL_ENV "/home/melvin/development/shortenmyurl/venv" |
|
||||||
|
|
||||||
set _OLD_VIRTUAL_PATH="$PATH" |
|
||||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH" |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ("" != "") then |
|
||||||
set env_name = "" |
|
||||||
else |
|
||||||
if (`basename "$VIRTUAL_ENV"` == "__") then |
|
||||||
# special case for Aspen magic directories |
|
||||||
# see http://www.zetadev.com/software/aspen/ |
|
||||||
set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` |
|
||||||
else |
|
||||||
set env_name = `basename "$VIRTUAL_ENV"` |
|
||||||
endif |
|
||||||
endif |
|
||||||
|
|
||||||
# Could be in a non-interactive environment, |
|
||||||
# in which case, $prompt is undefined and we wouldn't |
|
||||||
# care about the prompt anyway. |
|
||||||
if ( $?prompt ) then |
|
||||||
set _OLD_VIRTUAL_PROMPT="$prompt" |
|
||||||
set prompt = "[$env_name] $prompt" |
|
||||||
endif |
|
||||||
|
|
||||||
unset env_name |
|
||||||
|
|
||||||
alias pydoc python -m pydoc |
|
||||||
|
|
||||||
rehash |
|
||||||
|
|
@ -1,74 +0,0 @@ |
|||||||
# This file must be used with "source bin/activate.fish" *from fish* (http://fishshell.com) |
|
||||||
# you cannot run it directly |
|
||||||
|
|
||||||
function deactivate -d "Exit virtualenv and return to normal shell environment" |
|
||||||
# reset old environment variables |
|
||||||
if test -n "$_OLD_VIRTUAL_PATH" |
|
||||||
set -gx PATH $_OLD_VIRTUAL_PATH |
|
||||||
set -e _OLD_VIRTUAL_PATH |
|
||||||
end |
|
||||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME" |
|
||||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME |
|
||||||
set -e _OLD_VIRTUAL_PYTHONHOME |
|
||||||
end |
|
||||||
|
|
||||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE" |
|
||||||
# set an empty local fish_function_path, so fish_prompt doesn't automatically reload |
|
||||||
set -l fish_function_path |
|
||||||
# erase the virtualenv's fish_prompt function, and restore the original |
|
||||||
functions -e fish_prompt |
|
||||||
functions -c _old_fish_prompt fish_prompt |
|
||||||
functions -e _old_fish_prompt |
|
||||||
set -e _OLD_FISH_PROMPT_OVERRIDE |
|
||||||
end |
|
||||||
|
|
||||||
set -e VIRTUAL_ENV |
|
||||||
if test "$argv[1]" != "nondestructive" |
|
||||||
# Self destruct! |
|
||||||
functions -e deactivate |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
# unset irrelevant variables |
|
||||||
deactivate nondestructive |
|
||||||
|
|
||||||
set -gx VIRTUAL_ENV "/home/melvin/development/shortenmyurl/venv" |
|
||||||
|
|
||||||
set -gx _OLD_VIRTUAL_PATH $PATH |
|
||||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH |
|
||||||
|
|
||||||
# unset PYTHONHOME if set |
|
||||||
if set -q PYTHONHOME |
|
||||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME |
|
||||||
set -e PYTHONHOME |
|
||||||
end |
|
||||||
|
|
||||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" |
|
||||||
# fish uses a function instead of an env var to generate the prompt. |
|
||||||
|
|
||||||
# copy the current fish_prompt function as the function _old_fish_prompt |
|
||||||
functions -c fish_prompt _old_fish_prompt |
|
||||||
|
|
||||||
# with the original prompt function copied, we can override with our own. |
|
||||||
function fish_prompt |
|
||||||
# Prompt override? |
|
||||||
if test -n "" |
|
||||||
printf "%s%s" "" (set_color normal) |
|
||||||
_old_fish_prompt |
|
||||||
return |
|
||||||
end |
|
||||||
# ...Otherwise, prepend env |
|
||||||
set -l _checkbase (basename "$VIRTUAL_ENV") |
|
||||||
if test $_checkbase = "__" |
|
||||||
# special case for Aspen magic directories |
|
||||||
# see http://www.zetadev.com/software/aspen/ |
|
||||||
printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) |
|
||||||
_old_fish_prompt |
|
||||||
else |
|
||||||
printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) |
|
||||||
_old_fish_prompt |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" |
|
||||||
end |
|
@ -1,34 +0,0 @@ |
|||||||
"""By using execfile(this_file, dict(__file__=this_file)) you will |
|
||||||
activate this virtualenv environment. |
|
||||||
|
|
||||||
This can be used when you must use an existing Python interpreter, not |
|
||||||
the virtualenv bin/python |
|
||||||
""" |
|
||||||
|
|
||||||
try: |
|
||||||
__file__ |
|
||||||
except NameError: |
|
||||||
raise AssertionError( |
|
||||||
"You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") |
|
||||||
import sys |
|
||||||
import os |
|
||||||
|
|
||||||
old_os_path = os.environ['PATH'] |
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path |
|
||||||
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
||||||
if sys.platform == 'win32': |
|
||||||
site_packages = os.path.join(base, 'Lib', 'site-packages') |
|
||||||
else: |
|
||||||
site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') |
|
||||||
prev_sys_path = list(sys.path) |
|
||||||
import site |
|
||||||
site.addsitedir(site_packages) |
|
||||||
sys.real_prefix = sys.prefix |
|
||||||
sys.prefix = base |
|
||||||
# Move the added items to the front of the path: |
|
||||||
new_sys_path = [] |
|
||||||
for item in list(sys.path): |
|
||||||
if item not in prev_sys_path: |
|
||||||
new_sys_path.append(item) |
|
||||||
sys.path.remove(item) |
|
||||||
sys.path[:0] = new_sys_path |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from setuptools.command.easy_install import main |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(main()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from setuptools.command.easy_install import main |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(main()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from gunicorn.app.wsgiapp import run |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(run()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from gunicorn.app.djangoapp import run |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(run()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from gunicorn.app.pasterapp import run |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(run()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from pip import main |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(main()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from pip import main |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(main()) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/home/melvin/development/shortenmyurl/venv/bin/python |
|
||||||
|
|
||||||
# -*- coding: utf-8 -*- |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
from pip import main |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
|
||||||
sys.exit(main()) |
|
Binary file not shown.
@ -1 +0,0 @@ |
|||||||
/usr/include/python2.7 |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/UserDict.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/_abcoll.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/_weakrefset.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/abc.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/codecs.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/copy_reg.py |
|
@ -1,101 +0,0 @@ |
|||||||
import os |
|
||||||
import sys |
|
||||||
import warnings |
|
||||||
import imp |
|
||||||
import opcode # opcode is not a virtualenv module, so we can use it to find the stdlib |
|
||||||
# Important! To work on pypy, this must be a module that resides in the |
|
||||||
# lib-python/modified-x.y.z directory |
|
||||||
|
|
||||||
dirname = os.path.dirname |
|
||||||
|
|
||||||
distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils') |
|
||||||
if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): |
|
||||||
warnings.warn( |
|
||||||
"The virtualenv distutils package at %s appears to be in the same location as the system distutils?") |
|
||||||
else: |
|
||||||
__path__.insert(0, distutils_path) |
|
||||||
real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ('', '', imp.PKG_DIRECTORY)) |
|
||||||
# Copy the relevant attributes |
|
||||||
try: |
|
||||||
__revision__ = real_distutils.__revision__ |
|
||||||
except AttributeError: |
|
||||||
pass |
|
||||||
__version__ = real_distutils.__version__ |
|
||||||
|
|
||||||
from distutils import dist, sysconfig |
|
||||||
|
|
||||||
try: |
|
||||||
basestring |
|
||||||
except NameError: |
|
||||||
basestring = str |
|
||||||
|
|
||||||
## patch build_ext (distutils doesn't know how to get the libs directory |
|
||||||
## path on windows - it hardcodes the paths around the patched sys.prefix) |
|
||||||
|
|
||||||
if sys.platform == 'win32': |
|
||||||
from distutils.command.build_ext import build_ext as old_build_ext |
|
||||||
class build_ext(old_build_ext): |
|
||||||
def finalize_options (self): |
|
||||||
if self.library_dirs is None: |
|
||||||
self.library_dirs = [] |
|
||||||
elif isinstance(self.library_dirs, basestring): |
|
||||||
self.library_dirs = self.library_dirs.split(os.pathsep) |
|
||||||
|
|
||||||
self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) |
|
||||||
old_build_ext.finalize_options(self) |
|
||||||
|
|
||||||
from distutils.command import build_ext as build_ext_module |
|
||||||
build_ext_module.build_ext = build_ext |
|
||||||
|
|
||||||
## distutils.dist patches: |
|
||||||
|
|
||||||
old_find_config_files = dist.Distribution.find_config_files |
|
||||||
def find_config_files(self): |
|
||||||
found = old_find_config_files(self) |
|
||||||
system_distutils = os.path.join(distutils_path, 'distutils.cfg') |
|
||||||
#if os.path.exists(system_distutils): |
|
||||||
# found.insert(0, system_distutils) |
|
||||||
# What to call the per-user config file |
|
||||||
if os.name == 'posix': |
|
||||||
user_filename = ".pydistutils.cfg" |
|
||||||
else: |
|
||||||
user_filename = "pydistutils.cfg" |
|
||||||
user_filename = os.path.join(sys.prefix, user_filename) |
|
||||||
if os.path.isfile(user_filename): |
|
||||||
for item in list(found): |
|
||||||
if item.endswith('pydistutils.cfg'): |
|
||||||
found.remove(item) |
|
||||||
found.append(user_filename) |
|
||||||
return found |
|
||||||
dist.Distribution.find_config_files = find_config_files |
|
||||||
|
|
||||||
## distutils.sysconfig patches: |
|
||||||
|
|
||||||
old_get_python_inc = sysconfig.get_python_inc |
|
||||||
def sysconfig_get_python_inc(plat_specific=0, prefix=None): |
|
||||||
if prefix is None: |
|
||||||
prefix = sys.real_prefix |
|
||||||
return old_get_python_inc(plat_specific, prefix) |
|
||||||
sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ |
|
||||||
sysconfig.get_python_inc = sysconfig_get_python_inc |
|
||||||
|
|
||||||
old_get_python_lib = sysconfig.get_python_lib |
|
||||||
def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): |
|
||||||
if standard_lib and prefix is None: |
|
||||||
prefix = sys.real_prefix |
|
||||||
return old_get_python_lib(plat_specific, standard_lib, prefix) |
|
||||||
sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ |
|
||||||
sysconfig.get_python_lib = sysconfig_get_python_lib |
|
||||||
|
|
||||||
old_get_config_vars = sysconfig.get_config_vars |
|
||||||
def sysconfig_get_config_vars(*args): |
|
||||||
real_vars = old_get_config_vars(*args) |
|
||||||
if sys.platform == 'win32': |
|
||||||
lib_dir = os.path.join(sys.real_prefix, "libs") |
|
||||||
if isinstance(real_vars, dict) and 'LIBDIR' not in real_vars: |
|
||||||
real_vars['LIBDIR'] = lib_dir # asked for all |
|
||||||
elif isinstance(real_vars, list) and 'LIBDIR' in args: |
|
||||||
real_vars = real_vars + [lib_dir] # asked for list |
|
||||||
return real_vars |
|
||||||
sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ |
|
||||||
sysconfig.get_config_vars = sysconfig_get_config_vars |
|
@ -1,6 +0,0 @@ |
|||||||
# This is a config file local to this virtualenv installation |
|
||||||
# You may include options that will be used by all distutils commands, |
|
||||||
# and by easy_install. For instance: |
|
||||||
# |
|
||||||
# [easy_install] |
|
||||||
# find_links = http://mylocalsite |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/encodings |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/fnmatch.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/genericpath.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/lib-dynload |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/linecache.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/locale.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/ntpath.py |
|
@ -1 +0,0 @@ |
|||||||
/usr |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/os.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/posixpath.py |
|
@ -1 +0,0 @@ |
|||||||
/usr/lib/python2.7/re.py |
|
@ -1,16 +0,0 @@ |
|||||||
try: |
|
||||||
import ast |
|
||||||
from _markerlib.markers import default_environment, compile, interpret |
|
||||||
except ImportError: |
|
||||||
if 'ast' in globals(): |
|
||||||
raise |
|
||||||
def default_environment(): |
|
||||||
return {} |
|
||||||
def compile(marker): |
|
||||||
def marker_fn(environment=None, override=None): |
|
||||||
# 'empty markers are True' heuristic won't install extra deps. |
|
||||||
return not marker.strip() |
|
||||||
marker_fn.__doc__ = marker |
|
||||||
return marker_fn |
|
||||||
def interpret(marker, environment=None, override=None): |
|
||||||
return compile(marker)() |
|
@ -1,119 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
"""Interpret PEP 345 environment markers. |
|
||||||
|
|
||||||
EXPR [in|==|!=|not in] EXPR [or|and] ... |
|
||||||
|
|
||||||
where EXPR belongs to any of those: |
|
||||||
|
|
||||||
python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) |
|
||||||
python_full_version = sys.version.split()[0] |
|
||||||
os.name = os.name |
|
||||||
sys.platform = sys.platform |
|
||||||
platform.version = platform.version() |
|
||||||
platform.machine = platform.machine() |
|
||||||
platform.python_implementation = platform.python_implementation() |
|
||||||
a free string, like '2.6', or 'win32' |
|
||||||
""" |
|
||||||
|
|
||||||
__all__ = ['default_environment', 'compile', 'interpret'] |
|
||||||
|
|
||||||
import ast |
|
||||||
import os |
|
||||||
import platform |
|
||||||
import sys |
|
||||||
import weakref |
|
||||||
|
|
||||||
_builtin_compile = compile |
|
||||||
|
|
||||||
try: |
|
||||||
from platform import python_implementation |
|
||||||
except ImportError: |
|
||||||
if os.name == "java": |
|
||||||
# Jython 2.5 has ast module, but not platform.python_implementation() function. |
|
||||||
def python_implementation(): |
|
||||||
return "Jython" |
|
||||||
else: |
|
||||||
raise |
|
||||||
|
|
||||||
|
|
||||||
# restricted set of variables |
|
||||||
_VARS = {'sys.platform': sys.platform, |
|
||||||
'python_version': '%s.%s' % sys.version_info[:2], |
|
||||||
# FIXME parsing sys.platform is not reliable, but there is no other |
|
||||||
# way to get e.g. 2.7.2+, and the PEP is defined with sys.version |
|
||||||
'python_full_version': sys.version.split(' ', 1)[0], |
|
||||||
'os.name': os.name, |
|
||||||
'platform.version': platform.version(), |
|
||||||
'platform.machine': platform.machine(), |
|
||||||
'platform.python_implementation': python_implementation(), |
|
||||||
'extra': None # wheel extension |
|
||||||
} |
|
||||||
|
|
||||||
for var in list(_VARS.keys()): |
|
||||||
if '.' in var: |
|
||||||
_VARS[var.replace('.', '_')] = _VARS[var] |
|
||||||
|
|
||||||
def default_environment(): |
|
||||||
"""Return copy of default PEP 385 globals dictionary.""" |
|
||||||
return dict(_VARS) |
|
||||||
|
|
||||||
class ASTWhitelist(ast.NodeTransformer): |
|
||||||
def __init__(self, statement): |
|
||||||
self.statement = statement # for error messages |
|
||||||
|
|
||||||
ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) |
|
||||||
# Bool operations |
|
||||||
ALLOWED += (ast.And, ast.Or) |
|
||||||
# Comparison operations |
|
||||||
ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) |
|
||||||
|
|
||||||
def visit(self, node): |
|
||||||
"""Ensure statement only contains allowed nodes.""" |
|
||||||
if not isinstance(node, self.ALLOWED): |
|
||||||
raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % |
|
||||||
(self.statement, |
|
||||||
(' ' * node.col_offset) + '^')) |
|
||||||
return ast.NodeTransformer.visit(self, node) |
|
||||||
|
|
||||||
def visit_Attribute(self, node): |
|
||||||
"""Flatten one level of attribute access.""" |
|
||||||
new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) |
|
||||||
return ast.copy_location(new_node, node) |
|
||||||
|
|
||||||
def parse_marker(marker): |
|
||||||
tree = ast.parse(marker, mode='eval') |
|
||||||
new_tree = ASTWhitelist(marker).generic_visit(tree) |
|
||||||
return new_tree |
|
||||||
|
|
||||||
def compile_marker(parsed_marker): |
|
||||||
return _builtin_compile(parsed_marker, '<environment marker>', 'eval', |
|
||||||
dont_inherit=True) |
|
||||||
|
|
||||||
_cache = weakref.WeakValueDictionary() |
|
||||||
|
|
||||||
def compile(marker): |
|
||||||
"""Return compiled marker as a function accepting an environment dict.""" |
|
||||||
try: |
|
||||||
return _cache[marker] |
|
||||||
except KeyError: |
|
||||||
pass |
|
||||||
if not marker.strip(): |
|
||||||
def marker_fn(environment=None, override=None): |
|
||||||
"""""" |
|
||||||
return True |
|
||||||
else: |
|
||||||
compiled_marker = compile_marker(parse_marker(marker)) |
|
||||||
def marker_fn(environment=None, override=None): |
|
||||||
"""override updates environment""" |
|
||||||
if override is None: |
|
||||||
override = {} |
|
||||||
if environment is None: |
|
||||||
environment = default_environment() |
|
||||||
environment.update(override) |
|
||||||
return eval(compiled_marker, environment) |
|
||||||
marker_fn.__doc__ = marker |
|
||||||
_cache[marker] = marker_fn |
|
||||||
return _cache[marker] |
|
||||||
|
|
||||||
def interpret(marker, environment=None): |
|
||||||
return compile(marker)(environment) |
|
@ -1,5 +0,0 @@ |
|||||||
"""Run the EasyInstall command""" |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
from setuptools.command.easy_install import main |
|
||||||
main() |
|
@ -1,50 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,73 +0,0 @@ |
|||||||
# -*- 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, {}) |
|
File diff suppressed because it is too large
Load Diff
@ -1,401 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,168 +0,0 @@ |
|||||||
# -*- 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)) |
|
@ -1,394 +0,0 @@ |
|||||||
# -*- 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, |
|
||||||
) |
|
@ -1,87 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,29 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,120 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,44 +0,0 @@ |
|||||||
# -*- 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')) |
|
@ -1,849 +0,0 @@ |
|||||||
# -*- 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) |
|
@ -1,243 +0,0 @@ |
|||||||
# -*- 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)) |
|
@ -1,45 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,42 +0,0 @@ |
|||||||
# -*- 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' |
|
@ -1,332 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,55 +0,0 @@ |
|||||||
# -*- 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') |
|
@ -1,143 +0,0 @@ |
|||||||
# -*- 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) |
|
@ -1,124 +0,0 @@ |
|||||||
# -*- 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() |
|
@ -1,246 +0,0 @@ |
|||||||
# -*- 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) |
|
@ -1,101 +0,0 @@ |
|||||||
# -*- 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 |
|
File diff suppressed because it is too large
Load Diff
@ -1,790 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,299 +0,0 @@ |
|||||||
# -*- 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 |
|
@ -1,24 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.deprecations |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Tests deprecation support. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
from flask.testsuite import FlaskTestCase, catch_warnings |
|
||||||
|
|
||||||
|
|
||||||
class DeprecationsTestCase(FlaskTestCase): |
|
||||||
"""not used currently""" |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
suite.addTest(unittest.makeSuite(DeprecationsTestCase)) |
|
||||||
return suite |
|
@ -1,38 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.examples |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Tests the examples. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
import os |
|
||||||
import unittest |
|
||||||
from flask.testsuite import add_to_path |
|
||||||
|
|
||||||
|
|
||||||
def setup_path(): |
|
||||||
example_path = os.path.join(os.path.dirname(__file__), |
|
||||||
os.pardir, os.pardir, 'examples') |
|
||||||
add_to_path(os.path.join(example_path, 'flaskr')) |
|
||||||
add_to_path(os.path.join(example_path, 'minitwit')) |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
setup_path() |
|
||||||
suite = unittest.TestSuite() |
|
||||||
try: |
|
||||||
from minitwit_tests import MiniTwitTestCase |
|
||||||
except ImportError: |
|
||||||
pass |
|
||||||
else: |
|
||||||
suite.addTest(unittest.makeSuite(MiniTwitTestCase)) |
|
||||||
try: |
|
||||||
from flaskr_tests import FlaskrTestCase |
|
||||||
except ImportError: |
|
||||||
pass |
|
||||||
else: |
|
||||||
suite.addTest(unittest.makeSuite(FlaskrTestCase)) |
|
||||||
return suite |
|
@ -1,134 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.ext |
|
||||||
~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Tests the extension import thing. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import sys |
|
||||||
import unittest |
|
||||||
try: |
|
||||||
from imp import reload as reload_module |
|
||||||
except ImportError: |
|
||||||
reload_module = reload |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
from flask._compat import PY2 |
|
||||||
|
|
||||||
class ExtImportHookTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def setup(self): |
|
||||||
# we clear this out for various reasons. The most important one is |
|
||||||
# that a real flaskext could be in there which would disable our |
|
||||||
# fake package. Secondly we want to make sure that the flaskext |
|
||||||
# import hook does not break on reloading. |
|
||||||
for entry, value in list(sys.modules.items()): |
|
||||||
if (entry.startswith('flask.ext.') or |
|
||||||
entry.startswith('flask_') or |
|
||||||
entry.startswith('flaskext.') or |
|
||||||
entry == 'flaskext') and value is not None: |
|
||||||
sys.modules.pop(entry, None) |
|
||||||
from flask import ext |
|
||||||
reload_module(ext) |
|
||||||
|
|
||||||
# reloading must not add more hooks |
|
||||||
import_hooks = 0 |
|
||||||
for item in sys.meta_path: |
|
||||||
cls = type(item) |
|
||||||
if cls.__module__ == 'flask.exthook' and \ |
|
||||||
cls.__name__ == 'ExtensionImporter': |
|
||||||
import_hooks += 1 |
|
||||||
self.assert_equal(import_hooks, 1) |
|
||||||
|
|
||||||
def teardown(self): |
|
||||||
from flask import ext |
|
||||||
for key in ext.__dict__: |
|
||||||
self.assert_not_in('.', key) |
|
||||||
|
|
||||||
def test_flaskext_new_simple_import_normal(self): |
|
||||||
from flask.ext.newext_simple import ext_id |
|
||||||
self.assert_equal(ext_id, 'newext_simple') |
|
||||||
|
|
||||||
def test_flaskext_new_simple_import_module(self): |
|
||||||
from flask.ext import newext_simple |
|
||||||
self.assert_equal(newext_simple.ext_id, 'newext_simple') |
|
||||||
self.assert_equal(newext_simple.__name__, 'flask_newext_simple') |
|
||||||
|
|
||||||
def test_flaskext_new_package_import_normal(self): |
|
||||||
from flask.ext.newext_package import ext_id |
|
||||||
self.assert_equal(ext_id, 'newext_package') |
|
||||||
|
|
||||||
def test_flaskext_new_package_import_module(self): |
|
||||||
from flask.ext import newext_package |
|
||||||
self.assert_equal(newext_package.ext_id, 'newext_package') |
|
||||||
self.assert_equal(newext_package.__name__, 'flask_newext_package') |
|
||||||
|
|
||||||
def test_flaskext_new_package_import_submodule_function(self): |
|
||||||
from flask.ext.newext_package.submodule import test_function |
|
||||||
self.assert_equal(test_function(), 42) |
|
||||||
|
|
||||||
def test_flaskext_new_package_import_submodule(self): |
|
||||||
from flask.ext.newext_package import submodule |
|
||||||
self.assert_equal(submodule.__name__, 'flask_newext_package.submodule') |
|
||||||
self.assert_equal(submodule.test_function(), 42) |
|
||||||
|
|
||||||
def test_flaskext_old_simple_import_normal(self): |
|
||||||
from flask.ext.oldext_simple import ext_id |
|
||||||
self.assert_equal(ext_id, 'oldext_simple') |
|
||||||
|
|
||||||
def test_flaskext_old_simple_import_module(self): |
|
||||||
from flask.ext import oldext_simple |
|
||||||
self.assert_equal(oldext_simple.ext_id, 'oldext_simple') |
|
||||||
self.assert_equal(oldext_simple.__name__, 'flaskext.oldext_simple') |
|
||||||
|
|
||||||
def test_flaskext_old_package_import_normal(self): |
|
||||||
from flask.ext.oldext_package import ext_id |
|
||||||
self.assert_equal(ext_id, 'oldext_package') |
|
||||||
|
|
||||||
def test_flaskext_old_package_import_module(self): |
|
||||||
from flask.ext import oldext_package |
|
||||||
self.assert_equal(oldext_package.ext_id, 'oldext_package') |
|
||||||
self.assert_equal(oldext_package.__name__, 'flaskext.oldext_package') |
|
||||||
|
|
||||||
def test_flaskext_old_package_import_submodule(self): |
|
||||||
from flask.ext.oldext_package import submodule |
|
||||||
self.assert_equal(submodule.__name__, 'flaskext.oldext_package.submodule') |
|
||||||
self.assert_equal(submodule.test_function(), 42) |
|
||||||
|
|
||||||
def test_flaskext_old_package_import_submodule_function(self): |
|
||||||
from flask.ext.oldext_package.submodule import test_function |
|
||||||
self.assert_equal(test_function(), 42) |
|
||||||
|
|
||||||
def test_flaskext_broken_package_no_module_caching(self): |
|
||||||
for x in range(2): |
|
||||||
with self.assert_raises(ImportError): |
|
||||||
import flask.ext.broken |
|
||||||
|
|
||||||
def test_no_error_swallowing(self): |
|
||||||
try: |
|
||||||
import flask.ext.broken |
|
||||||
except ImportError: |
|
||||||
exc_type, exc_value, tb = sys.exc_info() |
|
||||||
self.assert_true(exc_type is ImportError) |
|
||||||
if PY2: |
|
||||||
message = 'No module named missing_module' |
|
||||||
else: |
|
||||||
message = 'No module named \'missing_module\'' |
|
||||||
self.assert_equal(str(exc_value), message) |
|
||||||
self.assert_true(tb.tb_frame.f_globals is globals()) |
|
||||||
|
|
||||||
# reraise() adds a second frame so we need to skip that one too. |
|
||||||
# On PY3 we even have another one :( |
|
||||||
next = tb.tb_next.tb_next |
|
||||||
if not PY2: |
|
||||||
next = next.tb_next |
|
||||||
self.assert_in('flask_broken/__init__.py', next.tb_frame.f_code.co_filename) |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
suite.addTest(unittest.makeSuite(ExtImportHookTestCase)) |
|
||||||
return suite |
|
@ -1,593 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.helpers |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Various helpers. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import os |
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
from logging import StreamHandler |
|
||||||
from flask.testsuite import FlaskTestCase, catch_warnings, catch_stderr |
|
||||||
from werkzeug.http import parse_cache_control_header, parse_options_header |
|
||||||
from flask._compat import StringIO, text_type |
|
||||||
|
|
||||||
|
|
||||||
def has_encoding(name): |
|
||||||
try: |
|
||||||
import codecs |
|
||||||
codecs.lookup(name) |
|
||||||
return True |
|
||||||
except LookupError: |
|
||||||
return False |
|
||||||
|
|
||||||
|
|
||||||
class JSONTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_json_bad_requests(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/json', methods=['POST']) |
|
||||||
def return_json(): |
|
||||||
return flask.jsonify(foo=text_type(flask.request.get_json())) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.post('/json', data='malformed', content_type='application/json') |
|
||||||
self.assert_equal(rv.status_code, 400) |
|
||||||
|
|
||||||
def test_json_body_encoding(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.request.get_json() |
|
||||||
|
|
||||||
c = app.test_client() |
|
||||||
resp = c.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'), |
|
||||||
content_type='application/json; charset=iso-8859-15') |
|
||||||
self.assert_equal(resp.data, u'Hällo Wörld'.encode('utf-8')) |
|
||||||
|
|
||||||
def test_jsonify(self): |
|
||||||
d = dict(a=23, b=42, c=[1, 2, 3]) |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/kw') |
|
||||||
def return_kwargs(): |
|
||||||
return flask.jsonify(**d) |
|
||||||
@app.route('/dict') |
|
||||||
def return_dict(): |
|
||||||
return flask.jsonify(d) |
|
||||||
c = app.test_client() |
|
||||||
for url in '/kw', '/dict': |
|
||||||
rv = c.get(url) |
|
||||||
self.assert_equal(rv.mimetype, 'application/json') |
|
||||||
self.assert_equal(flask.json.loads(rv.data), d) |
|
||||||
|
|
||||||
def test_json_as_unicode(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
|
|
||||||
app.config['JSON_AS_ASCII'] = True |
|
||||||
with app.app_context(): |
|
||||||
rv = flask.json.dumps(u'\N{SNOWMAN}') |
|
||||||
self.assert_equal(rv, '"\\u2603"') |
|
||||||
|
|
||||||
app.config['JSON_AS_ASCII'] = False |
|
||||||
with app.app_context(): |
|
||||||
rv = flask.json.dumps(u'\N{SNOWMAN}') |
|
||||||
self.assert_equal(rv, u'"\u2603"') |
|
||||||
|
|
||||||
def test_json_attr(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/add', methods=['POST']) |
|
||||||
def add(): |
|
||||||
json = flask.request.get_json() |
|
||||||
return text_type(json['a'] + json['b']) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), |
|
||||||
content_type='application/json') |
|
||||||
self.assert_equal(rv.data, b'3') |
|
||||||
|
|
||||||
def test_template_escaping(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
render = flask.render_template_string |
|
||||||
with app.test_request_context(): |
|
||||||
rv = flask.json.htmlsafe_dumps('</script>') |
|
||||||
self.assert_equal(rv, u'"\\u003c/script\\u003e"') |
|
||||||
self.assert_equal(type(rv), text_type) |
|
||||||
rv = render('{{ "</script>"|tojson }}') |
|
||||||
self.assert_equal(rv, '"\\u003c/script\\u003e"') |
|
||||||
rv = render('{{ "<\0/script>"|tojson }}') |
|
||||||
self.assert_equal(rv, '"\\u003c\\u0000/script\\u003e"') |
|
||||||
rv = render('{{ "<!--<script>"|tojson }}') |
|
||||||
self.assert_equal(rv, '"\\u003c!--\\u003cscript\\u003e"') |
|
||||||
rv = render('{{ "&"|tojson }}') |
|
||||||
self.assert_equal(rv, '"\\u0026"') |
|
||||||
rv = render('{{ "\'"|tojson }}') |
|
||||||
self.assert_equal(rv, '"\\u0027"') |
|
||||||
rv = render("<a ng-data='{{ data|tojson }}'></a>", |
|
||||||
data={'x': ["foo", "bar", "baz'"]}) |
|
||||||
self.assert_equal(rv, |
|
||||||
'<a ng-data=\'{"x": ["foo", "bar", "baz\\u0027"]}\'></a>') |
|
||||||
|
|
||||||
def test_json_customization(self): |
|
||||||
class X(object): |
|
||||||
def __init__(self, val): |
|
||||||
self.val = val |
|
||||||
class MyEncoder(flask.json.JSONEncoder): |
|
||||||
def default(self, o): |
|
||||||
if isinstance(o, X): |
|
||||||
return '<%d>' % o.val |
|
||||||
return flask.json.JSONEncoder.default(self, o) |
|
||||||
class MyDecoder(flask.json.JSONDecoder): |
|
||||||
def __init__(self, *args, **kwargs): |
|
||||||
kwargs.setdefault('object_hook', self.object_hook) |
|
||||||
flask.json.JSONDecoder.__init__(self, *args, **kwargs) |
|
||||||
def object_hook(self, obj): |
|
||||||
if len(obj) == 1 and '_foo' in obj: |
|
||||||
return X(obj['_foo']) |
|
||||||
return obj |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
app.json_encoder = MyEncoder |
|
||||||
app.json_decoder = MyDecoder |
|
||||||
@app.route('/', methods=['POST']) |
|
||||||
def index(): |
|
||||||
return flask.json.dumps(flask.request.get_json()['x']) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.post('/', data=flask.json.dumps({ |
|
||||||
'x': {'_foo': 42} |
|
||||||
}), content_type='application/json') |
|
||||||
self.assertEqual(rv.data, b'"<42>"') |
|
||||||
|
|
||||||
def test_modified_url_encoding(self): |
|
||||||
class ModifiedRequest(flask.Request): |
|
||||||
url_charset = 'euc-kr' |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
app.request_class = ModifiedRequest |
|
||||||
app.url_map.charset = 'euc-kr' |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.request.args['foo'] |
|
||||||
|
|
||||||
rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr')) |
|
||||||
self.assert_equal(rv.status_code, 200) |
|
||||||
self.assert_equal(rv.data, u'정상처리'.encode('utf-8')) |
|
||||||
|
|
||||||
if not has_encoding('euc-kr'): |
|
||||||
test_modified_url_encoding = None |
|
||||||
|
|
||||||
def test_json_key_sorting(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
self.assert_equal(app.config['JSON_SORT_KEYS'], True) |
|
||||||
d = dict.fromkeys(range(20), 'foo') |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.jsonify(values=d) |
|
||||||
|
|
||||||
c = app.test_client() |
|
||||||
rv = c.get('/') |
|
||||||
lines = [x.strip() for x in rv.data.strip().decode('utf-8').splitlines()] |
|
||||||
self.assert_equal(lines, [ |
|
||||||
'{', |
|
||||||
'"values": {', |
|
||||||
'"0": "foo",', |
|
||||||
'"1": "foo",', |
|
||||||
'"2": "foo",', |
|
||||||
'"3": "foo",', |
|
||||||
'"4": "foo",', |
|
||||||
'"5": "foo",', |
|
||||||
'"6": "foo",', |
|
||||||
'"7": "foo",', |
|
||||||
'"8": "foo",', |
|
||||||
'"9": "foo",', |
|
||||||
'"10": "foo",', |
|
||||||
'"11": "foo",', |
|
||||||
'"12": "foo",', |
|
||||||
'"13": "foo",', |
|
||||||
'"14": "foo",', |
|
||||||
'"15": "foo",', |
|
||||||
'"16": "foo",', |
|
||||||
'"17": "foo",', |
|
||||||
'"18": "foo",', |
|
||||||
'"19": "foo"', |
|
||||||
'}', |
|
||||||
'}' |
|
||||||
]) |
|
||||||
|
|
||||||
|
|
||||||
class SendfileTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_send_file_regular(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
with app.test_request_context(): |
|
||||||
rv = flask.send_file('static/index.html') |
|
||||||
self.assert_true(rv.direct_passthrough) |
|
||||||
self.assert_equal(rv.mimetype, 'text/html') |
|
||||||
with app.open_resource('static/index.html') as f: |
|
||||||
rv.direct_passthrough = False |
|
||||||
self.assert_equal(rv.data, f.read()) |
|
||||||
rv.close() |
|
||||||
|
|
||||||
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') |
|
||||||
self.assert_true(rv.direct_passthrough) |
|
||||||
self.assert_in('x-sendfile', rv.headers) |
|
||||||
self.assert_equal(rv.headers['x-sendfile'], |
|
||||||
os.path.join(app.root_path, 'static/index.html')) |
|
||||||
self.assert_equal(rv.mimetype, 'text/html') |
|
||||||
rv.close() |
|
||||||
|
|
||||||
def test_send_file_object(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
with catch_warnings() as captured: |
|
||||||
with app.test_request_context(): |
|
||||||
f = open(os.path.join(app.root_path, 'static/index.html')) |
|
||||||
rv = flask.send_file(f) |
|
||||||
rv.direct_passthrough = False |
|
||||||
with app.open_resource('static/index.html') as f: |
|
||||||
self.assert_equal(rv.data, f.read()) |
|
||||||
self.assert_equal(rv.mimetype, 'text/html') |
|
||||||
rv.close() |
|
||||||
# mimetypes + etag |
|
||||||
self.assert_equal(len(captured), 2) |
|
||||||
|
|
||||||
app.use_x_sendfile = True |
|
||||||
with catch_warnings() as captured: |
|
||||||
with app.test_request_context(): |
|
||||||
f = open(os.path.join(app.root_path, 'static/index.html')) |
|
||||||
rv = flask.send_file(f) |
|
||||||
self.assert_equal(rv.mimetype, 'text/html') |
|
||||||
self.assert_in('x-sendfile', rv.headers) |
|
||||||
self.assert_equal(rv.headers['x-sendfile'], |
|
||||||
os.path.join(app.root_path, 'static/index.html')) |
|
||||||
rv.close() |
|
||||||
# mimetypes + etag |
|
||||||
self.assert_equal(len(captured), 2) |
|
||||||
|
|
||||||
app.use_x_sendfile = False |
|
||||||
with app.test_request_context(): |
|
||||||
with catch_warnings() as captured: |
|
||||||
f = StringIO('Test') |
|
||||||
rv = flask.send_file(f) |
|
||||||
rv.direct_passthrough = False |
|
||||||
self.assert_equal(rv.data, b'Test') |
|
||||||
self.assert_equal(rv.mimetype, 'application/octet-stream') |
|
||||||
rv.close() |
|
||||||
# etags |
|
||||||
self.assert_equal(len(captured), 1) |
|
||||||
with catch_warnings() as captured: |
|
||||||
f = StringIO('Test') |
|
||||||
rv = flask.send_file(f, mimetype='text/plain') |
|
||||||
rv.direct_passthrough = False |
|
||||||
self.assert_equal(rv.data, b'Test') |
|
||||||
self.assert_equal(rv.mimetype, 'text/plain') |
|
||||||
rv.close() |
|
||||||
# etags |
|
||||||
self.assert_equal(len(captured), 1) |
|
||||||
|
|
||||||
app.use_x_sendfile = True |
|
||||||
with catch_warnings() as captured: |
|
||||||
with app.test_request_context(): |
|
||||||
f = StringIO('Test') |
|
||||||
rv = flask.send_file(f) |
|
||||||
self.assert_not_in('x-sendfile', rv.headers) |
|
||||||
rv.close() |
|
||||||
# etags |
|
||||||
self.assert_equal(len(captured), 1) |
|
||||||
|
|
||||||
def test_attachment(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
with catch_warnings() as captured: |
|
||||||
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']) |
|
||||||
self.assert_equal(value, 'attachment') |
|
||||||
rv.close() |
|
||||||
# mimetypes + etag |
|
||||||
self.assert_equal(len(captured), 2) |
|
||||||
|
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_equal(options['filename'], 'index.html') |
|
||||||
rv = flask.send_file('static/index.html', as_attachment=True) |
|
||||||
value, options = parse_options_header(rv.headers['Content-Disposition']) |
|
||||||
self.assert_equal(value, 'attachment') |
|
||||||
self.assert_equal(options['filename'], 'index.html') |
|
||||||
rv.close() |
|
||||||
|
|
||||||
with app.test_request_context(): |
|
||||||
rv = flask.send_file(StringIO('Test'), as_attachment=True, |
|
||||||
attachment_filename='index.txt', |
|
||||||
add_etags=False) |
|
||||||
self.assert_equal(rv.mimetype, 'text/plain') |
|
||||||
value, options = parse_options_header(rv.headers['Content-Disposition']) |
|
||||||
self.assert_equal(value, 'attachment') |
|
||||||
self.assert_equal(options['filename'], 'index.txt') |
|
||||||
rv.close() |
|
||||||
|
|
||||||
def test_static_file(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
# default cache timeout is 12 hours |
|
||||||
with app.test_request_context(): |
|
||||||
# Test with static file handler. |
|
||||||
rv = app.send_static_file('index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 12 * 60 * 60) |
|
||||||
rv.close() |
|
||||||
# Test again with direct use of send_file utility. |
|
||||||
rv = flask.send_file('static/index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 12 * 60 * 60) |
|
||||||
rv.close() |
|
||||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600 |
|
||||||
with app.test_request_context(): |
|
||||||
# Test with static file handler. |
|
||||||
rv = app.send_static_file('index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 3600) |
|
||||||
rv.close() |
|
||||||
# Test again with direct use of send_file utility. |
|
||||||
rv = flask.send_file('static/index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 3600) |
|
||||||
rv.close() |
|
||||||
class StaticFileApp(flask.Flask): |
|
||||||
def get_send_file_max_age(self, filename): |
|
||||||
return 10 |
|
||||||
app = StaticFileApp(__name__) |
|
||||||
with app.test_request_context(): |
|
||||||
# Test with static file handler. |
|
||||||
rv = app.send_static_file('index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 10) |
|
||||||
rv.close() |
|
||||||
# Test again with direct use of send_file utility. |
|
||||||
rv = flask.send_file('static/index.html') |
|
||||||
cc = parse_cache_control_header(rv.headers['Cache-Control']) |
|
||||||
self.assert_equal(cc.max_age, 10) |
|
||||||
rv.close() |
|
||||||
|
|
||||||
|
|
||||||
class LoggingTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_logger_cache(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
logger1 = app.logger |
|
||||||
self.assert_true(app.logger is logger1) |
|
||||||
self.assert_equal(logger1.name, __name__) |
|
||||||
app.logger_name = __name__ + '/test_logger_cache' |
|
||||||
self.assert_true(app.logger is not logger1) |
|
||||||
|
|
||||||
def test_debug_log(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.debug = True |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
app.logger.warning('the standard library is dead') |
|
||||||
app.logger.debug('this is a debug statement') |
|
||||||
return '' |
|
||||||
|
|
||||||
@app.route('/exc') |
|
||||||
def exc(): |
|
||||||
1 // 0 |
|
||||||
|
|
||||||
with app.test_client() as c: |
|
||||||
with catch_stderr() as err: |
|
||||||
c.get('/') |
|
||||||
out = err.getvalue() |
|
||||||
self.assert_in('WARNING in helpers [', out) |
|
||||||
self.assert_in(os.path.basename(__file__.rsplit('.', 1)[0] + '.py'), out) |
|
||||||
self.assert_in('the standard library is dead', out) |
|
||||||
self.assert_in('this is a debug statement', out) |
|
||||||
|
|
||||||
with catch_stderr() as err: |
|
||||||
try: |
|
||||||
c.get('/exc') |
|
||||||
except ZeroDivisionError: |
|
||||||
pass |
|
||||||
else: |
|
||||||
self.assert_true(False, 'debug log ate the exception') |
|
||||||
|
|
||||||
def test_debug_log_override(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.debug = True |
|
||||||
app.logger_name = 'flask_tests/test_debug_log_override' |
|
||||||
app.logger.level = 10 |
|
||||||
self.assert_equal(app.logger.level, 10) |
|
||||||
|
|
||||||
def test_exception_logging(self): |
|
||||||
out = StringIO() |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.logger_name = 'flask_tests/test_exception_logging' |
|
||||||
app.logger.addHandler(StreamHandler(out)) |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
1 // 0 |
|
||||||
|
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.status_code, 500) |
|
||||||
self.assert_in(b'Internal Server Error', rv.data) |
|
||||||
|
|
||||||
err = out.getvalue() |
|
||||||
self.assert_in('Exception on / [GET]', err) |
|
||||||
self.assert_in('Traceback (most recent call last):', err) |
|
||||||
self.assert_in('1 // 0', err) |
|
||||||
self.assert_in('ZeroDivisionError:', err) |
|
||||||
|
|
||||||
def test_processor_exceptions(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.before_request |
|
||||||
def before_request(): |
|
||||||
if trigger == 'before': |
|
||||||
1 // 0 |
|
||||||
@app.after_request |
|
||||||
def after_request(response): |
|
||||||
if trigger == 'after': |
|
||||||
1 // 0 |
|
||||||
return response |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return 'Foo' |
|
||||||
@app.errorhandler(500) |
|
||||||
def internal_server_error(e): |
|
||||||
return 'Hello Server Error', 500 |
|
||||||
for trigger in 'before', 'after': |
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.status_code, 500) |
|
||||||
self.assert_equal(rv.data, b'Hello Server Error') |
|
||||||
|
|
||||||
def test_url_for_with_anchor(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return '42' |
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_equal(flask.url_for('index', _anchor='x y'), |
|
||||||
'/#x%20y') |
|
||||||
|
|
||||||
def test_url_for_with_scheme(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return '42' |
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_equal(flask.url_for('index', |
|
||||||
_external=True, |
|
||||||
_scheme='https'), |
|
||||||
'https://localhost/') |
|
||||||
|
|
||||||
def test_url_for_with_scheme_not_external(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return '42' |
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_raises(ValueError, |
|
||||||
flask.url_for, |
|
||||||
'index', |
|
||||||
_scheme='https') |
|
||||||
|
|
||||||
def test_url_with_method(self): |
|
||||||
from flask.views import MethodView |
|
||||||
app = flask.Flask(__name__) |
|
||||||
class MyView(MethodView): |
|
||||||
def get(self, id=None): |
|
||||||
if id is None: |
|
||||||
return 'List' |
|
||||||
return 'Get %d' % id |
|
||||||
def post(self): |
|
||||||
return 'Create' |
|
||||||
myview = MyView.as_view('myview') |
|
||||||
app.add_url_rule('/myview/', methods=['GET'], |
|
||||||
view_func=myview) |
|
||||||
app.add_url_rule('/myview/<int:id>', methods=['GET'], |
|
||||||
view_func=myview) |
|
||||||
app.add_url_rule('/myview/create', methods=['POST'], |
|
||||||
view_func=myview) |
|
||||||
|
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_equal(flask.url_for('myview', _method='GET'), |
|
||||||
'/myview/') |
|
||||||
self.assert_equal(flask.url_for('myview', id=42, _method='GET'), |
|
||||||
'/myview/42') |
|
||||||
self.assert_equal(flask.url_for('myview', _method='POST'), |
|
||||||
'/myview/create') |
|
||||||
|
|
||||||
|
|
||||||
class NoImportsTestCase(FlaskTestCase): |
|
||||||
"""Test Flasks are created without import. |
|
||||||
|
|
||||||
Avoiding ``__import__`` helps create Flask instances where there are errors |
|
||||||
at import time. Those runtime errors will be apparent to the user soon |
|
||||||
enough, but tools which build Flask instances meta-programmatically benefit |
|
||||||
from a Flask which does not ``__import__``. Instead of importing to |
|
||||||
retrieve file paths or metadata on a module or package, use the pkgutil and |
|
||||||
imp modules in the Python standard library. |
|
||||||
""" |
|
||||||
|
|
||||||
def test_name_with_import_error(self): |
|
||||||
try: |
|
||||||
flask.Flask('importerror') |
|
||||||
except NotImplementedError: |
|
||||||
self.fail('Flask(import_name) is importing import_name.') |
|
||||||
|
|
||||||
|
|
||||||
class StreamingTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_streaming_with_context(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
def generate(): |
|
||||||
yield 'Hello ' |
|
||||||
yield flask.request.args['name'] |
|
||||||
yield '!' |
|
||||||
return flask.Response(flask.stream_with_context(generate())) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.get('/?name=World') |
|
||||||
self.assertEqual(rv.data, b'Hello World!') |
|
||||||
|
|
||||||
def test_streaming_with_context_as_decorator(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
@flask.stream_with_context |
|
||||||
def generate(): |
|
||||||
yield 'Hello ' |
|
||||||
yield flask.request.args['name'] |
|
||||||
yield '!' |
|
||||||
return flask.Response(generate()) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.get('/?name=World') |
|
||||||
self.assertEqual(rv.data, b'Hello World!') |
|
||||||
|
|
||||||
def test_streaming_with_context_and_custom_close(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
called = [] |
|
||||||
class Wrapper(object): |
|
||||||
def __init__(self, gen): |
|
||||||
self._gen = gen |
|
||||||
def __iter__(self): |
|
||||||
return self |
|
||||||
def close(self): |
|
||||||
called.append(42) |
|
||||||
def __next__(self): |
|
||||||
return next(self._gen) |
|
||||||
next = __next__ |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
def generate(): |
|
||||||
yield 'Hello ' |
|
||||||
yield flask.request.args['name'] |
|
||||||
yield '!' |
|
||||||
return flask.Response(flask.stream_with_context( |
|
||||||
Wrapper(generate()))) |
|
||||||
c = app.test_client() |
|
||||||
rv = c.get('/?name=World') |
|
||||||
self.assertEqual(rv.data, b'Hello World!') |
|
||||||
self.assertEqual(called, [42]) |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
if flask.json_available: |
|
||||||
suite.addTest(unittest.makeSuite(JSONTestCase)) |
|
||||||
suite.addTest(unittest.makeSuite(SendfileTestCase)) |
|
||||||
suite.addTest(unittest.makeSuite(LoggingTestCase)) |
|
||||||
suite.addTest(unittest.makeSuite(NoImportsTestCase)) |
|
||||||
suite.addTest(unittest.makeSuite(StreamingTestCase)) |
|
||||||
return suite |
|
@ -1,116 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.regression |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Tests regressions. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import os |
|
||||||
import gc |
|
||||||
import sys |
|
||||||
import flask |
|
||||||
import threading |
|
||||||
import unittest |
|
||||||
from werkzeug.exceptions import NotFound |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
|
|
||||||
|
|
||||||
_gc_lock = threading.Lock() |
|
||||||
|
|
||||||
|
|
||||||
class _NoLeakAsserter(object): |
|
||||||
|
|
||||||
def __init__(self, testcase): |
|
||||||
self.testcase = testcase |
|
||||||
|
|
||||||
def __enter__(self): |
|
||||||
gc.disable() |
|
||||||
_gc_lock.acquire() |
|
||||||
loc = flask._request_ctx_stack._local |
|
||||||
|
|
||||||
# Force Python to track this dictionary at all times. |
|
||||||
# This is necessary since Python only starts tracking |
|
||||||
# dicts if they contain mutable objects. It's a horrible, |
|
||||||
# horrible hack but makes this kinda testable. |
|
||||||
loc.__storage__['FOOO'] = [1, 2, 3] |
|
||||||
|
|
||||||
gc.collect() |
|
||||||
self.old_objects = len(gc.get_objects()) |
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb): |
|
||||||
if not hasattr(sys, 'getrefcount'): |
|
||||||
gc.collect() |
|
||||||
new_objects = len(gc.get_objects()) |
|
||||||
if new_objects > self.old_objects: |
|
||||||
self.testcase.fail('Example code leaked') |
|
||||||
_gc_lock.release() |
|
||||||
gc.enable() |
|
||||||
|
|
||||||
|
|
||||||
class MemoryTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def assert_no_leak(self): |
|
||||||
return _NoLeakAsserter(self) |
|
||||||
|
|
||||||
def test_memory_consumption(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template('simple_template.html', whiskey=42) |
|
||||||
|
|
||||||
def fire(): |
|
||||||
with app.test_client() as c: |
|
||||||
rv = c.get('/') |
|
||||||
self.assert_equal(rv.status_code, 200) |
|
||||||
self.assert_equal(rv.data, b'<h1>42</h1>') |
|
||||||
|
|
||||||
# Trigger caches |
|
||||||
fire() |
|
||||||
|
|
||||||
# This test only works on CPython 2.7. |
|
||||||
if sys.version_info >= (2, 7) and \ |
|
||||||
not hasattr(sys, 'pypy_translation_info'): |
|
||||||
with self.assert_no_leak(): |
|
||||||
for x in range(10): |
|
||||||
fire() |
|
||||||
|
|
||||||
def test_safe_join_toplevel_pardir(self): |
|
||||||
from flask.helpers import safe_join |
|
||||||
with self.assert_raises(NotFound): |
|
||||||
safe_join('/foo', '..') |
|
||||||
|
|
||||||
|
|
||||||
class ExceptionTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_aborting(self): |
|
||||||
class Foo(Exception): |
|
||||||
whatever = 42 |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.testing = True |
|
||||||
@app.errorhandler(Foo) |
|
||||||
def handle_foo(e): |
|
||||||
return str(e.whatever) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
raise flask.abort(flask.redirect(flask.url_for('test'))) |
|
||||||
@app.route('/test') |
|
||||||
def test(): |
|
||||||
raise Foo() |
|
||||||
|
|
||||||
with app.test_client() as c: |
|
||||||
rv = c.get('/') |
|
||||||
self.assertEqual(rv.headers['Location'], 'http://localhost/test') |
|
||||||
rv = c.get('/test') |
|
||||||
self.assertEqual(rv.data, b'42') |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
if os.environ.get('RUN_FLASK_MEMORY_TESTS') == '1': |
|
||||||
suite.addTest(unittest.makeSuite(MemoryTestCase)) |
|
||||||
suite.addTest(unittest.makeSuite(ExceptionTestCase)) |
|
||||||
return suite |
|
@ -1,185 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.reqctx |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Tests the request context. |
|
||||||
|
|
||||||
:copyright: (c) 2012 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
try: |
|
||||||
from greenlet import greenlet |
|
||||||
except ImportError: |
|
||||||
greenlet = None |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
|
|
||||||
|
|
||||||
class RequestContextTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_teardown_on_pop(self): |
|
||||||
buffer = [] |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.teardown_request |
|
||||||
def end_of_request(exception): |
|
||||||
buffer.append(exception) |
|
||||||
|
|
||||||
ctx = app.test_request_context() |
|
||||||
ctx.push() |
|
||||||
self.assert_equal(buffer, []) |
|
||||||
ctx.pop() |
|
||||||
self.assert_equal(buffer, [None]) |
|
||||||
|
|
||||||
def test_proper_test_request_context(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.config.update( |
|
||||||
SERVER_NAME='localhost.localdomain:5000' |
|
||||||
) |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return None |
|
||||||
|
|
||||||
@app.route('/', subdomain='foo') |
|
||||||
def sub(): |
|
||||||
return None |
|
||||||
|
|
||||||
with app.test_request_context('/'): |
|
||||||
self.assert_equal(flask.url_for('index', _external=True), 'http://localhost.localdomain:5000/') |
|
||||||
|
|
||||||
with app.test_request_context('/'): |
|
||||||
self.assert_equal(flask.url_for('sub', _external=True), 'http://foo.localhost.localdomain:5000/') |
|
||||||
|
|
||||||
try: |
|
||||||
with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}): |
|
||||||
pass |
|
||||||
except Exception as e: |
|
||||||
self.assert_true(isinstance(e, ValueError)) |
|
||||||
self.assert_equal(str(e), "the server name provided " + |
|
||||||
"('localhost.localdomain:5000') does not match the " + \ |
|
||||||
"server name from the WSGI environment ('localhost')") |
|
||||||
|
|
||||||
try: |
|
||||||
app.config.update(SERVER_NAME='localhost') |
|
||||||
with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}): |
|
||||||
pass |
|
||||||
except ValueError as e: |
|
||||||
raise ValueError( |
|
||||||
"No ValueError exception should have been raised \"%s\"" % e |
|
||||||
) |
|
||||||
|
|
||||||
try: |
|
||||||
app.config.update(SERVER_NAME='localhost:80') |
|
||||||
with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}): |
|
||||||
pass |
|
||||||
except ValueError as e: |
|
||||||
raise ValueError( |
|
||||||
"No ValueError exception should have been raised \"%s\"" % e |
|
||||||
) |
|
||||||
|
|
||||||
def test_context_binding(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return 'Hello %s!' % flask.request.args['name'] |
|
||||||
@app.route('/meh') |
|
||||||
def meh(): |
|
||||||
return flask.request.url |
|
||||||
|
|
||||||
with app.test_request_context('/?name=World'): |
|
||||||
self.assert_equal(index(), 'Hello World!') |
|
||||||
with app.test_request_context('/meh'): |
|
||||||
self.assert_equal(meh(), 'http://localhost/meh') |
|
||||||
self.assert_true(flask._request_ctx_stack.top is None) |
|
||||||
|
|
||||||
def test_context_test(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
self.assert_false(flask.request) |
|
||||||
self.assert_false(flask.has_request_context()) |
|
||||||
ctx = app.test_request_context() |
|
||||||
ctx.push() |
|
||||||
try: |
|
||||||
self.assert_true(flask.request) |
|
||||||
self.assert_true(flask.has_request_context()) |
|
||||||
finally: |
|
||||||
ctx.pop() |
|
||||||
|
|
||||||
def test_manual_context_binding(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return 'Hello %s!' % flask.request.args['name'] |
|
||||||
|
|
||||||
ctx = app.test_request_context('/?name=World') |
|
||||||
ctx.push() |
|
||||||
self.assert_equal(index(), 'Hello World!') |
|
||||||
ctx.pop() |
|
||||||
try: |
|
||||||
index() |
|
||||||
except RuntimeError: |
|
||||||
pass |
|
||||||
else: |
|
||||||
self.assert_true(0, 'expected runtime error') |
|
||||||
|
|
||||||
def test_greenlet_context_copying(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
greenlets = [] |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
reqctx = flask._request_ctx_stack.top.copy() |
|
||||||
def g(): |
|
||||||
self.assert_false(flask.request) |
|
||||||
self.assert_false(flask.current_app) |
|
||||||
with reqctx: |
|
||||||
self.assert_true(flask.request) |
|
||||||
self.assert_equal(flask.current_app, app) |
|
||||||
self.assert_equal(flask.request.path, '/') |
|
||||||
self.assert_equal(flask.request.args['foo'], 'bar') |
|
||||||
self.assert_false(flask.request) |
|
||||||
return 42 |
|
||||||
greenlets.append(greenlet(g)) |
|
||||||
return 'Hello World!' |
|
||||||
|
|
||||||
rv = app.test_client().get('/?foo=bar') |
|
||||||
self.assert_equal(rv.data, b'Hello World!') |
|
||||||
|
|
||||||
result = greenlets[0].run() |
|
||||||
self.assert_equal(result, 42) |
|
||||||
|
|
||||||
def test_greenlet_context_copying_api(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
greenlets = [] |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
reqctx = flask._request_ctx_stack.top.copy() |
|
||||||
@flask.copy_current_request_context |
|
||||||
def g(): |
|
||||||
self.assert_true(flask.request) |
|
||||||
self.assert_equal(flask.current_app, app) |
|
||||||
self.assert_equal(flask.request.path, '/') |
|
||||||
self.assert_equal(flask.request.args['foo'], 'bar') |
|
||||||
return 42 |
|
||||||
greenlets.append(greenlet(g)) |
|
||||||
return 'Hello World!' |
|
||||||
|
|
||||||
rv = app.test_client().get('/?foo=bar') |
|
||||||
self.assert_equal(rv.data, b'Hello World!') |
|
||||||
|
|
||||||
result = greenlets[0].run() |
|
||||||
self.assert_equal(result, 42) |
|
||||||
|
|
||||||
# Disable test if we don't have greenlets available |
|
||||||
if greenlet is None: |
|
||||||
test_greenlet_context_copying = None |
|
||||||
test_greenlet_context_copying_api = None |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
suite.addTest(unittest.makeSuite(RequestContextTestCase)) |
|
||||||
return suite |
|
@ -1,153 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.signals |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Signalling. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
|
|
||||||
|
|
||||||
class SignalsTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_template_rendered(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template('simple_template.html', whiskey=42) |
|
||||||
|
|
||||||
recorded = [] |
|
||||||
def record(sender, template, context): |
|
||||||
recorded.append((template, context)) |
|
||||||
|
|
||||||
flask.template_rendered.connect(record, app) |
|
||||||
try: |
|
||||||
app.test_client().get('/') |
|
||||||
self.assert_equal(len(recorded), 1) |
|
||||||
template, context = recorded[0] |
|
||||||
self.assert_equal(template.name, 'simple_template.html') |
|
||||||
self.assert_equal(context['whiskey'], 42) |
|
||||||
finally: |
|
||||||
flask.template_rendered.disconnect(record, app) |
|
||||||
|
|
||||||
def test_request_signals(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
calls = [] |
|
||||||
|
|
||||||
def before_request_signal(sender): |
|
||||||
calls.append('before-signal') |
|
||||||
|
|
||||||
def after_request_signal(sender, response): |
|
||||||
self.assert_equal(response.data, b'stuff') |
|
||||||
calls.append('after-signal') |
|
||||||
|
|
||||||
@app.before_request |
|
||||||
def before_request_handler(): |
|
||||||
calls.append('before-handler') |
|
||||||
|
|
||||||
@app.after_request |
|
||||||
def after_request_handler(response): |
|
||||||
calls.append('after-handler') |
|
||||||
response.data = 'stuff' |
|
||||||
return response |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
calls.append('handler') |
|
||||||
return 'ignored anyway' |
|
||||||
|
|
||||||
flask.request_started.connect(before_request_signal, app) |
|
||||||
flask.request_finished.connect(after_request_signal, app) |
|
||||||
|
|
||||||
try: |
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.data, b'stuff') |
|
||||||
|
|
||||||
self.assert_equal(calls, ['before-signal', 'before-handler', |
|
||||||
'handler', 'after-handler', |
|
||||||
'after-signal']) |
|
||||||
finally: |
|
||||||
flask.request_started.disconnect(before_request_signal, app) |
|
||||||
flask.request_finished.disconnect(after_request_signal, app) |
|
||||||
|
|
||||||
def test_request_exception_signal(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
recorded = [] |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
1 // 0 |
|
||||||
|
|
||||||
def record(sender, exception): |
|
||||||
recorded.append(exception) |
|
||||||
|
|
||||||
flask.got_request_exception.connect(record, app) |
|
||||||
try: |
|
||||||
self.assert_equal(app.test_client().get('/').status_code, 500) |
|
||||||
self.assert_equal(len(recorded), 1) |
|
||||||
self.assert_true(isinstance(recorded[0], ZeroDivisionError)) |
|
||||||
finally: |
|
||||||
flask.got_request_exception.disconnect(record, app) |
|
||||||
|
|
||||||
def test_appcontext_signals(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
recorded = [] |
|
||||||
def record_push(sender, **kwargs): |
|
||||||
recorded.append('push') |
|
||||||
def record_pop(sender, **kwargs): |
|
||||||
recorded.append('push') |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return 'Hello' |
|
||||||
|
|
||||||
flask.appcontext_pushed.connect(record_push, app) |
|
||||||
flask.appcontext_popped.connect(record_pop, app) |
|
||||||
try: |
|
||||||
with app.test_client() as c: |
|
||||||
rv = c.get('/') |
|
||||||
self.assert_equal(rv.data, b'Hello') |
|
||||||
self.assert_equal(recorded, ['push']) |
|
||||||
self.assert_equal(recorded, ['push', 'pop']) |
|
||||||
finally: |
|
||||||
flask.appcontext_pushed.disconnect(record_push, app) |
|
||||||
flask.appcontext_popped.disconnect(record_pop, app) |
|
||||||
|
|
||||||
def test_flash_signal(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.config['SECRET_KEY'] = 'secret' |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
flask.flash('This is a flash message', category='notice') |
|
||||||
return flask.redirect('/other') |
|
||||||
|
|
||||||
recorded = [] |
|
||||||
def record(sender, message, category): |
|
||||||
recorded.append((message, category)) |
|
||||||
|
|
||||||
flask.message_flashed.connect(record, app) |
|
||||||
try: |
|
||||||
client = app.test_client() |
|
||||||
with client.session_transaction(): |
|
||||||
client.get('/') |
|
||||||
self.assert_equal(len(recorded), 1) |
|
||||||
message, category = recorded[0] |
|
||||||
self.assert_equal(message, 'This is a flash message') |
|
||||||
self.assert_equal(category, 'notice') |
|
||||||
finally: |
|
||||||
flask.message_flashed.disconnect(record, app) |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
if flask.signals_available: |
|
||||||
suite.addTest(unittest.makeSuite(SignalsTestCase)) |
|
||||||
return suite |
|
@ -1 +0,0 @@ |
|||||||
<h1>Hello World!</h1> |
|
@ -1,46 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.subclassing |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Test that certain behavior of flask can be customized by |
|
||||||
subclasses. |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
from logging import StreamHandler |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
from flask._compat import StringIO |
|
||||||
|
|
||||||
|
|
||||||
class FlaskSubclassingTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_suppressed_exception_logging(self): |
|
||||||
class SuppressedFlask(flask.Flask): |
|
||||||
def log_exception(self, exc_info): |
|
||||||
pass |
|
||||||
|
|
||||||
out = StringIO() |
|
||||||
app = SuppressedFlask(__name__) |
|
||||||
app.logger_name = 'flask_tests/test_suppressed_exception_logging' |
|
||||||
app.logger.addHandler(StreamHandler(out)) |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
1 // 0 |
|
||||||
|
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.status_code, 500) |
|
||||||
self.assert_in(b'Internal Server Error', rv.data) |
|
||||||
|
|
||||||
err = out.getvalue() |
|
||||||
self.assert_equal(err, '') |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
suite.addTest(unittest.makeSuite(FlaskSubclassingTestCase)) |
|
||||||
return suite |
|
@ -1 +0,0 @@ |
|||||||
{% macro hello(name) %}Hello {{ name }}!{% endmacro %} |
|
@ -1 +0,0 @@ |
|||||||
<p>{{ value }}|{{ injected_value }} |
|
@ -1,6 +0,0 @@ |
|||||||
{{ text }} |
|
||||||
{{ html }} |
|
||||||
{% autoescape false %}{{ text }} |
|
||||||
{{ html }}{% endautoescape %} |
|
||||||
{% autoescape true %}{{ text }} |
|
||||||
{{ html }}{% endautoescape %} |
|
@ -1 +0,0 @@ |
|||||||
{{ foo}} Mail |
|
@ -1 +0,0 @@ |
|||||||
I'm nested |
|
@ -1 +0,0 @@ |
|||||||
<h1>{{ whiskey }}</h1> |
|
@ -1 +0,0 @@ |
|||||||
{{ value|super_reverse }} |
|
@ -1,3 +0,0 @@ |
|||||||
{% if value is boolean %} |
|
||||||
Success! |
|
||||||
{% endif %} |
|
@ -1,302 +0,0 @@ |
|||||||
# -*- coding: utf-8 -*- |
|
||||||
""" |
|
||||||
flask.testsuite.templating |
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||||
|
|
||||||
Template functionality |
|
||||||
|
|
||||||
:copyright: (c) 2011 by Armin Ronacher. |
|
||||||
:license: BSD, see LICENSE for more details. |
|
||||||
""" |
|
||||||
|
|
||||||
import flask |
|
||||||
import unittest |
|
||||||
from flask.testsuite import FlaskTestCase |
|
||||||
|
|
||||||
|
|
||||||
class TemplatingTestCase(FlaskTestCase): |
|
||||||
|
|
||||||
def test_context_processing(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.context_processor |
|
||||||
def context_processor(): |
|
||||||
return {'injected_value': 42} |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template('context_template.html', value=23) |
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.data, b'<p>23|42') |
|
||||||
|
|
||||||
def test_original_win(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template_string('{{ config }}', config=42) |
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.data, b'42') |
|
||||||
|
|
||||||
def test_request_less_rendering(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.config['WORLD_NAME'] = 'Special World' |
|
||||||
@app.context_processor |
|
||||||
def context_processor(): |
|
||||||
return dict(foo=42) |
|
||||||
|
|
||||||
with app.app_context(): |
|
||||||
rv = flask.render_template_string('Hello {{ config.WORLD_NAME }} ' |
|
||||||
'{{ foo }}') |
|
||||||
self.assert_equal(rv, 'Hello Special World 42') |
|
||||||
|
|
||||||
def test_standard_context(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
app.secret_key = 'development key' |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
flask.g.foo = 23 |
|
||||||
flask.session['test'] = 'aha' |
|
||||||
return flask.render_template_string(''' |
|
||||||
{{ request.args.foo }} |
|
||||||
{{ g.foo }} |
|
||||||
{{ config.DEBUG }} |
|
||||||
{{ session.test }} |
|
||||||
''') |
|
||||||
rv = app.test_client().get('/?foo=42') |
|
||||||
self.assert_equal(rv.data.split(), [b'42', b'23', b'False', b'aha']) |
|
||||||
|
|
||||||
def test_escaping(self): |
|
||||||
text = '<p>Hello World!' |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template('escaping_template.html', text=text, |
|
||||||
html=flask.Markup(text)) |
|
||||||
lines = app.test_client().get('/').data.splitlines() |
|
||||||
self.assert_equal(lines, [ |
|
||||||
b'<p>Hello World!', |
|
||||||
b'<p>Hello World!', |
|
||||||
b'<p>Hello World!', |
|
||||||
b'<p>Hello World!', |
|
||||||
b'<p>Hello World!', |
|
||||||
b'<p>Hello World!' |
|
||||||
]) |
|
||||||
|
|
||||||
def test_no_escaping(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
with app.test_request_context(): |
|
||||||
self.assert_equal(flask.render_template_string('{{ foo }}', |
|
||||||
foo='<test>'), '<test>') |
|
||||||
self.assert_equal(flask.render_template('mail.txt', foo='<test>'), |
|
||||||
'<test> Mail') |
|
||||||
|
|
||||||
def test_macros(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
with app.test_request_context(): |
|
||||||
macro = flask.get_template_attribute('_macro.html', 'hello') |
|
||||||
self.assert_equal(macro('World'), 'Hello World!') |
|
||||||
|
|
||||||
def test_template_filter(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_filter() |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
app.add_template_filter(my_reverse) |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_filter('strrev') |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
app.add_template_filter(my_reverse, 'strrev') |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_filter() |
|
||||||
def super_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
@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_template(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def super_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
app.add_template_filter(super_reverse) |
|
||||||
@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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_filter('super_reverse') |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
@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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def my_reverse(s): |
|
||||||
return s[::-1] |
|
||||||
app.add_template_filter(my_reverse, 'super_reverse') |
|
||||||
@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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_test() |
|
||||||
def boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
self.assert_in('boolean', app.jinja_env.tests.keys()) |
|
||||||
self.assert_equal(app.jinja_env.tests['boolean'], boolean) |
|
||||||
self.assert_true(app.jinja_env.tests['boolean'](False)) |
|
||||||
|
|
||||||
def test_add_template_test(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
app.add_template_test(boolean) |
|
||||||
self.assert_in('boolean', app.jinja_env.tests.keys()) |
|
||||||
self.assert_equal(app.jinja_env.tests['boolean'], boolean) |
|
||||||
self.assert_true(app.jinja_env.tests['boolean'](False)) |
|
||||||
|
|
||||||
def test_template_test_with_name(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_test('boolean') |
|
||||||
def is_boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def is_boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
app.add_template_test(is_boolean, 'boolean') |
|
||||||
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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_test() |
|
||||||
def boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
@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_template(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
app.add_template_test(boolean) |
|
||||||
@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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_test('boolean') |
|
||||||
def is_boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
@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): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
def is_boolean(value): |
|
||||||
return isinstance(value, bool) |
|
||||||
app.add_template_test(is_boolean, 'boolean') |
|
||||||
@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_global(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.template_global() |
|
||||||
def get_stuff(): |
|
||||||
return 42 |
|
||||||
self.assert_in('get_stuff', app.jinja_env.globals.keys()) |
|
||||||
self.assert_equal(app.jinja_env.globals['get_stuff'], get_stuff) |
|
||||||
self.assert_true(app.jinja_env.globals['get_stuff'](), 42) |
|
||||||
with app.app_context(): |
|
||||||
rv = flask.render_template_string('{{ get_stuff() }}') |
|
||||||
self.assert_equal(rv, '42') |
|
||||||
|
|
||||||
def test_custom_template_loader(self): |
|
||||||
class MyFlask(flask.Flask): |
|
||||||
def create_global_jinja_loader(self): |
|
||||||
from jinja2 import DictLoader |
|
||||||
return DictLoader({'index.html': 'Hello Custom World!'}) |
|
||||||
app = MyFlask(__name__) |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template('index.html') |
|
||||||
c = app.test_client() |
|
||||||
rv = c.get('/') |
|
||||||
self.assert_equal(rv.data, b'Hello Custom World!') |
|
||||||
|
|
||||||
def test_iterable_loader(self): |
|
||||||
app = flask.Flask(__name__) |
|
||||||
@app.context_processor |
|
||||||
def context_processor(): |
|
||||||
return {'whiskey': 'Jameson'} |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return flask.render_template( |
|
||||||
['no_template.xml', # should skip this one |
|
||||||
'simple_template.html', # should render this |
|
||||||
'context_template.html'], |
|
||||||
value=23) |
|
||||||
|
|
||||||
rv = app.test_client().get('/') |
|
||||||
self.assert_equal(rv.data, b'<h1>Jameson</h1>') |
|
||||||
|
|
||||||
|
|
||||||
def suite(): |
|
||||||
suite = unittest.TestSuite() |
|
||||||
suite.addTest(unittest.makeSuite(TemplatingTestCase)) |
|
||||||
return suite |
|
@ -1,7 +0,0 @@ |
|||||||
from flask import Flask |
|
||||||
|
|
||||||
app = Flask(__name__) |
|
||||||
from blueprintapp.apps.admin import admin |
|
||||||
from blueprintapp.apps.frontend import frontend |
|
||||||
app.register_blueprint(admin) |
|
||||||
app.register_blueprint(frontend) |
|
@ -1,15 +0,0 @@ |
|||||||
from flask import Blueprint, render_template |
|
||||||
|
|
||||||
admin = Blueprint('admin', __name__, url_prefix='/admin', |
|
||||||
template_folder='templates', |
|
||||||
static_folder='static') |
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/') |
|
||||||
def index(): |
|
||||||
return render_template('admin/index.html') |
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/index2') |
|
||||||
def index2(): |
|
||||||
return render_template('./admin/index.html') |
|
@ -1 +0,0 @@ |
|||||||
/* nested file */ |
|
@ -1 +0,0 @@ |
|||||||
Admin File |
|
@ -1 +0,0 @@ |
|||||||
Hello from the Admin |
|
@ -1,8 +0,0 @@ |
|||||||
from flask import Blueprint, render_template |
|
||||||
|
|
||||||
frontend = Blueprint('frontend', __name__, template_folder='templates') |
|
||||||
|
|
||||||
|
|
||||||
@frontend.route('/') |
|
||||||
def index(): |
|
||||||
return render_template('frontend/index.html') |
|
@ -1 +0,0 @@ |
|||||||
Hello from the Frontend |
|
@ -1,4 +0,0 @@ |
|||||||
import os |
|
||||||
import flask |
|
||||||
here = os.path.abspath(os.path.dirname(__file__)) |
|
||||||
app = flask.Flask(__name__) |
|
@ -1,4 +0,0 @@ |
|||||||
import os |
|
||||||
import flask |
|
||||||
here = os.path.abspath(os.path.dirname(__file__)) |
|
||||||
app = flask.Flask(__name__) |
|
@ -1,2 +0,0 @@ |
|||||||
import flask.ext.broken.b |
|
||||||
import missing_module |
|
@ -1 +0,0 @@ |
|||||||
ext_id = 'newext_package' |
|
@ -1,2 +0,0 @@ |
|||||||
def test_function(): |
|
||||||
return 42 |
|
@ -1 +0,0 @@ |
|||||||
ext_id = 'newext_simple' |
|
@ -1 +0,0 @@ |
|||||||
ext_id = 'oldext_package' |
|
@ -1,2 +0,0 @@ |
|||||||
def test_function(): |
|
||||||
return 42 |
|
@ -1 +0,0 @@ |
|||||||
ext_id = 'oldext_simple' |
|
@ -1,2 +0,0 @@ |
|||||||
# NoImportsTestCase |
|
||||||
raise NotImplementedError |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue