Chapter 06 gf_gen
个人觉着,工作流的最大魅力在于将所有工作都组件化和流程化了,同时它也会造成代码样板化,而样板化意味着可能包含着重复的劳动,解决重复劳动的一种方案就是采用代码生成器。gf_gen就是这样的一种生成器,它能够依据设置好的模板来快速的生成代码。
通过运行gf_gen --help来查看它的选项,可以看到它只包含三个有效选项:
-t --template 要使用的代码模板
-p --path 生成代码的路径
--show-args 查看模板所支持的参数
只需要三个参数,因为主要的生成工作是由具体的代码模板来做的,gf_gen本身只是做了一点微小的工作:对这些模板进行包装执行。
使用workflow模板
我们会用到越来越多的插件,要记忆每个插件的参数是不可能的事情,因此我们需要一个脚手架来做这件事,girlfriend自带的workflow模板就是这样一个脚手架。
gf_gen -t :workflow --show-args
:workflow表示是从entry_point注册表中加载模板。workflow模板只有两个参数,一个是-f,用来指定生成文件的路径,可以不指定,默认会在当前目录生成一个workflow.py,一个是--no-highlight,用于取消代码展示时的高亮,当你的终端颜色配置与生成器代码高亮方案产生冲突时,可以用该选项。
直接运行gf_gen -t :workflow
会出现一个交互界面,通过在这个交互界面中输入指令来向代码中添加新的结构
查看所有的指令
输入help可以查看所有的指令。另外支持使用tab对指令来进行自动补全。
展示当前代码
使用show命令可以查看当前已经生成的代码结构。
(Cmd) show
#coding: utf-8
from girlfriend.workflow.gfworkflow import Job, Decision
logger = None
logger_level = "info"
def workflow(options):
work_units = (
)
return work_units
添加插件类型的Job
plugin_job 单元名称 [插件名称] [参数函数名称]
通常只需要指定一个单元名称即可,插件名称默认与单元名称相同。如果你希望使用函数动态生成参数,那么可以指定一个生成参数的函数名称
(Cmd) plugin_job crawl crawl gen_crawl_args
(Cmd) show
# coding: utf-8
"""
Docs goes here
"""
from girlfriend.workflow.gfworkflow import Job, Decision
logger = None
logger_level = "info"
def workflow(options):
work_units = (
# crawl
Job(
name="crawl",
plugin="crawl",
args=gen_crawl_args
),
)
return work_units
def gen_crawl_args(context):
pass
添加函数类型的Job
caller_job 单元名称 [调用函数名称]
(Cmd) caller_job hello_world greeting
(Cmd) show
# coding: utf-8
"""
Docs goes here
"""
from girlfriend.workflow.gfworkflow import Job, Decision
logger = None
logger_level = "info"
def workflow(options):
work_units = (
# hello_world
Job(
name="hello_world",
caller=greeting,
),
)
return work_units
def greeting(context):
pass
添加Decision单元
decision 单元名称 [决策函数]
(Cmd) decision need_write_excel
(Cmd) show
# coding: utf-8
"""
Docs goes here
"""
from girlfriend.workflow.gfworkflow import Job, Decision
logger = None
logger_level = "info"
def workflow(options):
work_units = (
# need_write_excel
Decision(
"need_write_excel",
need_write_excel
),
)
return work_units
def need_write_excel(context):
pass
移动单元
使用move命令对已经添加的工作单元进行移动
将工作单元A移动到第2个位置处:move A 1
将工作单元A移到工作单元B之前:move A before B
将工作单元B移到工作单元A之后 move B after A
删除工作单元
remove 工作单元名称
添加cmd_parser
如果你的工作流需要接收命令行参数,那么可以通过cmd_parser
指令来添加解析器对象:
(Cmd) cmd_parser
(Cmd) show
# coding: utf-8
"""
Docs goes here
"""
from argparse import ArgumentParser
from girlfriend.workflow.gfworkflow import Job, Decision
cmd_parser = ArgumentParser(description=__doc__.decode("utf-8"))
cmd_parser.add_argument("--option", "-o", dest="option",
default="", action="store", help="")
logger = None
logger_level = "info"
def workflow(options):
work_units = (
)
return work_units
(Cmd)
管理环境
gf_workflow的一大特色是允许开发者针对不同的执行环境使用不同的参数策略,workflow模板通过管理环境指令来更加方便这一功能:
添加环境:
(Cmd) env test
(Cmd) show
# coding: utf-8
"""
Docs goes here
"""
from girlfriend.workflow.gfworkflow import Job, Decision
from girlfriend.workflow.protocol import Env
logger = None
logger_level = "info"
def _test_env_args(options):
return {}
def _test_env_config(options):
return {}
env = (
Env("test", _test_env_args, _test_env_config),
)
def workflow(options):
work_units = (
)
return work_units
移除环境:
remove_env 环境名称
扩展workflow模板
workflow带有所有内置插件的代码模板,也许你会思考,如果自己编写的插件,能集成到workflow模板中吗?答案是肯定的,你只需要添加一个名为girlfriend.plugin_code_meta的entry_point即可,例如:
"girlfriend.plugin_code_meta": [
(
"builtin = "
"girlfriend.tools.code_template.plugin_code_meta:all_meta"
)
]
其中的all_meta对象是一个字典,结构如下:
from girlfriend.tools.code_template.workflow_template import PluginCodeMeta
all_meta = {
# csv series
"read_csv": PluginCodeMeta(
plugin_name="read_csv",
args_template="""[
CSVR(
path="filepath",
record_handler=None,
record_filter=None,
result_wrapper=TableWrapper(
"table_name",
titles=[]
),
dialect="excel",
variable=None
),
]""",
auto_imports=[
"from girlfriend.plugin.csv import CSVR",
"from girlfriend.data.table import TableWrapper"
]
),
...
}
每个插件的代码模板用一个PluginCodeMeta对象来表示,args_template即参数模板,auto_imports为使用该插件时需要自动导入的包。
开发自己的模板
你可以根据自己的开发习惯来定制代码生成模板,gf_gen对代码模板只有一个约定,只要包含一个gen函数的模块即可,所有生成代码的逻辑均有开发者自己在gen函数中实现:
def gen(path, options):
# path 为工程创建目录
# options 为解析后的命令行参数
WorkflowGenerator(options.file_name, options).cmdloop()
你可以通过源码文件直接运行一个模板:
gf_gen -t ./template.py
也可以使用entry_point来注册:
"girlfriend.code_template": [
"mycodetemplate= girlfriend.tools.code_template.mycodetemplate",
],
这样就可以随处通过gf_gen -t :mycodetemplate
来使用模板了。