flask 导入的模块
from __future__ import with_statement
import os
import sys
from threading import local
from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase, \
LocalStack, LocalProxy, create_environ, cached_property, \
SharedDataMiddleware
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.contrib.securecookie import SecureCookie
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
from werkzeug import abort, redirect
from jinja2 import Markup, escape
# use pkg_resource if that works, otherwise fall back to cwd. The
# current working directory is generally not reliable with the notable
# exception of google appengine.
try:
import pkg_resources
pkg_resources.resource_stream
except (ImportError, AttributeError):
pkg_resources = None
1.class Request
增加了endpoint
和view_args
两个参数,用来接收reuqest的url,父类在werkzeug中实现,用来包装environ
,方便我们操作
class Request(RequestBase): # 这里的RequestBase是werkzeug定义的
def __init__(self, environ):
RequestBase.__init__(self, environ) # 直接写父类的名称`RequestBase`不推荐,最好用super()代替
self.endpoint = None
self.view_args = None
2.class Response
增加了返回的默认的mimetype
类型
class Response(ResponseBase):
default_mimetype = 'text/html'
3.class _RequestGlobals
4.class _RequestContext
包含了所有请求的信息
请求进来时被创建,然后加入_request_ctx_stack
,请求结束时删除.
class _RequestContext(object): # 使用with语句,拿到栈顶的request进行处理
def __init__(self, app, environ):
self.app = app # 当前的app
self.url_adapter = app.url_map.bind_to_environ(environ) # 主要用来处理URL,因为绑定了environ以后就不需要再往match里传递参数了
self.request = app.request_class(environ) # 当前的请求信息
self.session = app.open_session(self.request) # 当前的会话信息
self.g = _RequestGlobals() # ?
self.flashes = None
def __enter__(self): # 使用with语句
_request_ctx_stack.push(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.
if tb is None or not self.app.debug:
_request_ctx_stack.pop() # 将往前请求推出栈
5.def url_for
为endpoint
(url的id,一般就是视图函数),
def url_for(endpoint, **values):
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
6.def flash(message)
,发送一个message到下一个请求中
def flash(message):
session['_flashes'] = (session.get('_flashes', [])) + [message
7.def get_flashed_messages
def get_flashed_messages():
"""Pulls all flashed messages from the session and returns them.
Further calls in the same request to the function will return
the same messages.
"""
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes = \
session.pop('_flashes', [])
return flashes
8.def render_template
def render_template(template_name, **context):
"""Renders a template from the template folder with the given
context.
:param template_name: the name of the template to be rendered
:param context: the variables that should be available in the
context of the template.
"""
current_app.update_template_context(context)
return current_app.jinja_env.get_template(template_name).render(context
9.def render_template_string
def render_template_string(source, **context):
"""Renders a template from the given template source string
with the given context.
:param template_name: the sourcecode of the template to be
rendered
:param context: the variables that should be available in the
context of the template.
"""
current_app.update_template_context(context)
return current_app.jinja_env.from_string(source).render(context
10.def _default_template_ctx_processor
def _default_template_ctx_processor():
"""Default template context processor. Injects `request`,
`session` and `g`.
"""
reqctx = _request_ctx_stack.top
return dict(
request=reqctx.request,
session=reqctx.session,
g=reqctx.g
)
11.def _get_package_path(name)
获得app模块的路径app = Flask(__name__)
,默认都是使用__main__
def _get_package_path(name):
"""Returns the path to a package or cwd if that cannot be found."""
try:
return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
except (KeyError, AttributeError):
return os.getcwd()
12.class Flask
class Flask(object):
request_class = Request
response_class = Response
static_path = '/static' # path for the static files
secret_key = None # cryptographic components can use this to sign cookies and other things
session_cookie_name = 'session' # name of the session cookie
jinja_options = dict( # jinja2 的配置
autoescape=True,
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
)
def __init__(self, package_name): # `app = Flask(__name__)`
self.debug = False
self.package_name = package_name
self.root_path = _get_package_path(self.package_name) #: where is the app root located?
self.view_functions = {} # a dictionary of all view functions registered(`route` decorator)
self.error_handlers = {} # a dictionary of all registered error handlers,`errorhandler`decorator
self.before_request_funcs = [] # a list of functions that should be called at the beginning f the request
self.after_request_funcs = [] # a list of functions that are called at the end of the request.
self.template_context_processors = [_default_template_ctx_processor] #: to populate the template context.
self.url_map = Map()
if self.static_path is not None: # 若果静态文件存在,则通过ShareDatMiddleware设置静态文件夹
self.url_map.add(Rule(self.static_path + '/<filename>',
build_only=True, endpoint='static')) # 添加静态文件url
if pkg_resources is not None: # 如果是package不是主程序的话,要从包的路径找`static`
target = (self.package_name, 'static')
else:
target = os.path.join(self.root_path, 'static') # 如果是主程序则拼贴`static`的路径
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
self.static_path: target
})
self.jinja_env = Environment(loader=self.create_jinja_loader(),**self.jinja_options) # 配置jinjia2
self.jinja_env.globals.update(
url_for=url_for,
get_flashed_messages=get_flashed_messages
)
def create_jinja_loader(self): # 添加jinja2的载入,默认是`templates`
if pkg_resources is None:
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
return PackageLoader(self.package_name)
def update_template_context(self, context): #更新template 上下文
reqctx = _request_ctx_stack.top
for func in self.template_context_processors:
context.update(func())
def run(self, host='localhost', port=5000, **options):
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options)
def test_client(self): # 创建一个客户端,主要用来测试
from werkzeug import Client
return Client(self, self.response_class, use_cookies=True)
def open_resource(self, resource): # 打开一个源文件,主要是用来测试
if pkg_resources is None:
return open(os.path.join(self.root_path, resource), 'rb')
return pkg_resources.resource_stream(self.package_name, resource)
def open_session(self, request): # 会话,通过cookie来实现
key = self.secret_key
if key is not None:
return SecureCookie.load_cookie(request, self.session_cookie_name,
secret_key=key)
def save_session(self, session, response):
if session is not None:
session.save_cookie(response, self.session_cookie_name)
# 处理请求
def add_url_rule(self, rule, endpoint, **options): # 添加URL_RULE
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
def route(self, rule, **options): # 路由处理
def decorator(f):
self.add_url_rule(rule, f.__name__, **options) # 给url起名字
self.view_functions[f.__name__] = f # 给view_function起相同的名字
return f
return decorator
def errorhandler(self, code):
def decorator(f):
self.error_handlers[code] = f
return f
return decorator
def before_request(self, f):
self.before_request_funcs.append(f)
return f
def after_request(self, f):
self.after_request_funcs.append(f)
return f
def context_processor(self, f): # 添加context_processors
self.template_context_processors.append(f)
return f
def match_request(self):
rv = _request_ctx_stack.top.url_adapter.match() # 通过调用rule.match(path, method))从而返回rule的endpoint
request.endpoint, request.view_args = rv # 将值赋值给request,request是代买最下面的localproxy
return rv
def dispatch_request(self):
try:
endpoint, values = self.match_request() # 根据url获得endpoint和values
return self.view_functions[endpoint](**values) # 调用相应的方法
except HTTPException, e: # 如果错误则传入handler
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
def make_response(self, rv): # 返回内容
if isinstance(rv, self.response_class):
return rv
if isinstance(rv, basestring):
return self.response_class(rv)
if isinstance(rv, tuple):
return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
def preprocess_request(self): # 对请求进行预处理,如果最后rv不为空,则返回(有可能经过预处理rv就没了,比如说拦截)
for func in self.before_request_funcs: # 调用预处理函数,是个列表 before_request_funcs
rv = func()
if rv is not None:
return rv
def process_response(self, response):
session = _request_ctx_stack.top.session
if session is not None:
self.save_session(session, response)
for handler in self.after_request_funcs:
response = handler(response)
return response
def wsgi_app(self, environ, start_response): # 真实的WSGI application,在__call__中调用
with self.request_context(environ):
rv = self.preprocess_request() # 首先调用预处理请求函数
if rv is None: # 如果预处理后,返回为None,??
rv = self.dispatch_request() # 这时可能返回字符串,也可能是responseclass
response = self.make_response(rv) # 进一步处理,比如如果是
response = self.process_response(response) # 保存session
return response(environ, start_response)
def request_context(self, environ):
return _RequestContext(self, environ)
def test_request_context(self, *args, **kwargs):
return self.request_context(create_environ(*args, **kwargs))
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
# context locals
_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)