Flask框架初始化原始碼解析

關鍵程式碼展示

def __init__( self, import_name, static_url_path=None, static_folder=“static”, static_host=None, host_matching=False, subdomain_matching=False, template_folder=“templates”, instance_path=None, instance_relative_config=False, root_path=None, ): _PackageBoundObject。__init__( self, import_name, template_folder=template_folder, root_path=root_path ) self。static_url_path = static_url_path self。static_folder = static_folder if instance_path is None: instance_path = self。auto_find_instance_path() elif not os。path。isabs(instance_path): raise ValueError( “If an instance path is provided it must be absolute。” “ A relative path was given instead。” )

可以看到flask框架在初始化的過程中可以傳入10個引數,除了第一個引數必填以外其他的都是有預設引數的非必填選項。下面我將按照順序對這10個引數做介紹:

import_name

從flask的原始碼中可以看出,import_name主要是傳遞給了_PackageBoundObject的init方法,我們來看看這個方法

def __init__(self, import_name, template_folder=None, root_path=None): self。import_name = import_name self。template_folder = template_folder if root_path is None: root_path = get_root_path(self。import_name) self。root_path = root_path self。_static_folder = None self。_static_url_path = None # circular import from 。cli import AppGroup

可以看到,最終是將import_name透過get_root_path方法轉化為root_path屬性儲存起來。我們來看一下轉換過程

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) mod = sys。modules[import_name] filepath = getattr(mod, “__file__”, None) # If we don’t have a filepath it might be because we are a # namespace package。 In this case we pick the root path from the # first module that is contained in our package。 if filepath is None: raise RuntimeError( “No root path can be found for the provided ” ‘module “%s”。 This can happen because the ’ “module came from an import hook that does ” “not provide file name information or because ” “it‘s a namespace package。 In this case ” “the root path needs to be explicitly ” “provided。” % import_name ) # filepath is import_name。py for a module, or __init__。py for a package。 return os。path。dirname(os。path。abspath(filepath))

從這個指令碼可以看出,最終透過這個import引數來獲得web伺服器的根路徑。

static_url_path

可以透過引數註解來大概瞭解一下他的作用

:param static_url_path: can be used to specify a different path for the static files on the web。 Defaults to the name of the `static_folder` folder。

也就是說透過這個引數,我們可以指定靜態資源的路徑,這個路徑最終指向了static_folder設定的資料夾位置

static_folder

可以透過引數註解來大概瞭解一下他的作用

:param static_folder: The folder with static files that is served at ``static_url_path``。 Relative to the application ``root_path`` or an absolute path。 Defaults to ``’static‘``。

也就是說透過這個引數,我們可以指定靜態資源的的檔名,預設情況下叫static

static_host

可以透過引數註解來大概瞭解一下他的作用

:param static_host: the host to use when adding the static route。 Defaults to None。 Required when using ``host_matching=True`` with a ``static_folder`` configured。

也就是說,當我們的靜態資原始檔和服務端指令碼不在一塊時,我們可以透過配置這個引數來實現遠端資源的載入。前提是我們配置host_matching=True

host_matching

作用參照上面

subdomain_matching

可以透過引數註解大概瞭解一下他的作用

:param subdomain_matching: consider the subdomain relative to :data:`SERVER_NAME` when matching routes。 Defaults to False。

遵循子域名的匹配,預設為false,在原始碼中可以看到,subdomain_matching主要是用於bind_to_environ方法中

if request is not None: # If subdomain matching is disabled (the default), use the # default subdomain in all cases。 This should be the default # in Werkzeug but it currently does not have that feature。 subdomain = ( (self。url_map。default_subdomain or None) if not self。subdomain_matching else None ) return self。url_map。bind_to_environ( request。environ, server_name=self。config[“SERVER_NAME”], subdomain=subdomain, )

進入bind_to_environ方法中,可以看到subdomain主要是為了配合uwsgi時拼接子域名

def bind_to_environ(self, environ, server_name=None, subdomain=None): environ = _get_environ(environ) wsgi_server_name = get_host(environ)。lower() if server_name is None: server_name = wsgi_server_name else: server_name = server_name。lower() if subdomain is None and not self。host_matching: cur_server_name = wsgi_server_name。split(“。”) real_server_name = server_name。split(“。”) offset = -len(real_server_name) if cur_server_name[offset:] != real_server_name: # This can happen even with valid configs if the server was # accesssed directly by IP address under some situations。 # Instead of raising an exception like in Werkzeug 0。7 or # earlier we go by an invalid subdomain which will result # in a 404 error on matching。 subdomain = “” else: subdomain = “。”。join(filter(None, cur_server_name[:offset]))

template_folder

可以透過引數註解來大概瞭解下他的作用

:param template_folder: the folder that contains the templates that should be used by the application。 Defaults to ``’templates‘`` folder in the root path of the application。

透過註解可以看出,template_folder的主要作用是放靜態模板頁面的地方,預設放在templates資料夾下,但可以透過修改這個變數來修改靜態模板的存放位置

instance_path

可以透過引數註解來大概瞭解一下他的作用

:param instance_path: An alternative instance path for the application。 By default the folder ``’instance‘`` next to the package or module is assumed to be the instance path。

可以看出,我們可以透過instance_path來設定匯入包的路徑,這樣可以隱藏比較敏感的配置

instance_relative_config

可以透過引數註解來大概瞭解一下他的作用

:param instance_relative_config: if set to ``True`` relative filenames for loading the config are assumed to be relative to the instance path instead of the application root。

主要是和instan_path來實現匯入包路徑的替換,相當於一個開關

root_path

可以透過引數註解來了解一下他的大概作用

:param root_path: Flask by default will automatically calculate the path to the root of the application。 In certain situations this cannot be achieved (for instance if the package is a Python 3 namespace package) and needs to be manually defined。

可以看出,透過修改這個root_path來修改服務端指令碼的根路徑。這個在import包的時候很重要。import包的時候flask會在前面拼接root_path來找到這個包