@TOC
flask中Manager源码如下所示:
class Manager(object):
"""
Controller class for handling a set of commands.
Typical usage::
class Print(Command):
def run(self):
print "hello"
app = Flask(__name__)
manager = Manager(app)
manager.add_command("print", Print())
if __name__ == "__main__":
manager.run()
On command line::
python manage.py print
> hello
从上面我们我可以看到, 我们定义了一个类继承Command, 并且重写了run方法。就可以在命令行执行了, 也就是说Command为我们提供一个接口, run方法中内容都会被命令行所执行。
那么,现在看下Command又为我们做了什么, 源码如下所示:
class Command(object):
"""
Base class for creating commands.
:param func: Initialize this command by introspecting the function.
"""
option_list = ()
help_args = None
def __init__(self, func=None):
if func is None:
if not self.option_list:
self.option_list = []
return
args, varargs, keywords, defaults = inspect.getargspec(func)
if inspect.ismethod(func):
args = args[1:]
options = []
# first arg is always "app" : ignore
defaults = defaults or []
kwargs = dict(izip(*[reversed(l) for l in (args, defaults)]))
for arg in args:
if arg in kwargs:
default = kwargs[arg]
if isinstance(default, bool):
options.append(Option('-%s' % arg[0],
'--%s' % arg,
action="store_true",
dest=arg,
required=False,
default=default))
else:
options.append(Option('-%s' % arg[0],
'--%s' % arg,
dest=arg,
type=text_type,
required=False,
default=default))
else:
options.append(Option(arg, type=text_type))
self.run = func
self.__doc__ = func.__doc__
self.option_list = options
@property
def description(self):
description = self.__doc__ or ''
return description.strip()
def add_option(self, option):
"""
Adds Option to option list.
"""
self.option_list.append(option)
def get_options(self):
"""
By default, returns self.option_list. Override if you
need to do instance-specific configuration.
"""
return self.option_list
def create_parser(self, *args, **kwargs):
func_stack = kwargs.pop('func_stack',())
parent = kwargs.pop('parent',None)
parser = argparse.ArgumentParser(*args, add_help=False, **kwargs)
help_args = self.help_args
while help_args is None and parent is not None:
help_args = parent.help_args
parent = getattr(parent,'parent',None)
if help_args:
from flask_script import add_help
add_help(parser,help_args)
for option in self.get_options():
if isinstance(option, Group):
if option.exclusive:
group = parser.add_mutually_exclusive_group(
required=option.required,
)
else:
group = parser.add_argument_group(
title=option.title,
description=option.description,
)
for opt in option.get_options():
group.add_argument(*opt.args, **opt.kwargs)
else:
parser.add_argument(*option.args, **option.kwargs)
parser.set_defaults(func_stack=func_stack+(self,))
self.parser = parser
self.parent = parent
return parser
def __call__(self, app=None, *args, **kwargs):
"""
Handles the command with the given app.
Default behaviour is to call ``self.run`` within a test request context.
"""
with app.test_request_context():
return self.run(*args, **kwargs)
def run(self):
"""
Runs a command. This must be implemented by the subclass. Should take
arguments as configured by the Command options.
"""
raise NotImplementedError
下面分析下执行过程:
1、其他函数是对options[列表]的内容进行增删查操作
2、create_parser函数创建了命令行解析对象parser = argparse.ArgumentParser(*args, add_help=False, **kwargs)
,获取options中获取并保存options中数据和help_args中数据,以及parser.set_defaults(func_stack=func_stack+(self,))
将Command自己添加到parser中参数中。
3,在flask应用代码中我们添加例如manager.add_command("db", Print())
的代码,传入了Command的实例对象, 而add_command创建了Command的实例对象并保存在slef._commands的namespace中或者key_value值中。
备注>>>在flask应用代码中我们添加例如manager.add_command("db", MigrateCommand)
的代码,传入了Manager的实例对象MigrateCommand- - - -另外一个Manager对象(此对象,已经添加了添加了迁移等命令, 后面会和当前这个flask应用中manager对象建立关联self.parent)
4,call方法中显示,当Command的实例对象被调用的时候,就会被执行(此时, 引入app实例的上下文, 并执行了run方法)。那么此时,我们就只要去寻找Command实例是何时被调用的
。
此时Manage对象包装app,并执行了自己的run方法.如下所示.
run方法中通过sys.argv接收了命令行参数,并把参数提交给slef.handle
执行。
而handle方法中创建app_parser = self.create_parser(prog)
(此函数获取到Commad对象),获取了所有的app_parser的信息(func和 args和config)。
parser什么时候被执行?
此时而app_parser依然是argparse中ArgumentParser
对象。
依然是在Manage的create_parser方法中,执行了app_namespace, remaining_args = app_parser.parse_known_args(args)
, 方法内又调用了_parse_known_args
此处是重点:_parse_known_args中,内部函数consume_optional先调用self._option_string_actions通过string映射action类,
另一方面调用了内部函数take_action,创建action的实例对象(如上面Command的实例对象就是此时被创建)。
app_namespace.dict中可以获取到func_stack,
最后遍历for handle in func_stack:
,并执行handle, handle可能是Command实例对象,调用后,执行call方法,执行run方法,也可能是func函数,直接被执行。
def run(self, commands=None, default_command=None):
"""
Prepares manager to receive command line input. Usually run
inside "if __name__ == "__main__" block in a Python script.
:param commands: optional dict of commands. Appended to any commands
added using add_command().
:param default_command: name of default command to run if no
arguments passed.
"""
if commands:
self._commands.update(commands)
# Make sure all of this is Unicode
argv = list(text_type(arg) for arg in sys.argv)
if default_command is not None and len(argv) == 1:
argv.append(default_command)
try:
result = self.handle(argv[0], argv[1:])
except SystemExit as e:
result = e.code
sys.exit(result or 0)