插件
Girlfriend通过将可复用的工作抽象为插件来减少代码量。Girlfriend的插件机制非常灵活,类、模块甚至一个函数都可以被包装成插件。本着“自带电池”的原则,Girlfriend内置了很多插件,开发者也可以定义自己的插件,如果你觉着某件事情是在做重复劳动,或者你解决了很多特定的问题后发现这些问题实际上是属于一类问题,那么你就应该考虑把它做成插件了。
很多框架都支持插件,但是开发插件在这些框架中往往都是属于高级话题,Girlfriend不一样,在这里,开发插件是一种日常。
插件的开发
尽管可以通过多种方式来开发插件,但是通常只建议采用定义类的方式来开发,使用类的好处是便于今后通过继承进行扩展。
通过定义类来编写插件很简单,无需继承任何莫名其妙的东西或者指定什么元类,普普通通的一个类就可以了,比如我们内置的CSV读取插件:
class CSVReaderPlugin(object):
name = "read_csv"
def execute(self, context, *csv_readers):
return [reader(context) for reader in csv_readers]
class CSVR(AbstractDataReader):
"""
CSV读单元,以单个CSV文件为单位
"""
@args2fields()
def __init__(self, path=None, content=None,
record_handler=None, record_filter=None, result_wrapper=None,
dialect="excel", variable=None):
"""
:param path csv文件路径,可以是文件路径,也可以是url地址
:param content csv数据内容,指定了content就不需要指定了path了,反之亦然
:param record_handler 记录处理器,接受解析后的行对象
:param record_filter 对解析后的行对象进行过滤
:param result_wrapper 对最终结果的包装器
:param dialect csv方言,可以是字符串也可以是csv.Dialect对象
:param variable context中的引用变量名
"""
pass
def __call__(self, context):
# get Dialect object
if isinstance(self._dialect, types.StringTypes):
dialect = csv.get_dialect(self._dialect)
# get content
content = self._content
if self._content is None:
if self._path.startswith(HTTP_SCHEMA):
content = requests.get(self._path).text
else:
with open(self._path, "r") as f:
content = f.read()
if isinstance(content, types.StringTypes):
content = StringIO.StringIO(content)
result = []
csv_reader = csv.reader(content, dialect=dialect)
for row in csv_reader:
self._handle_record(row, result.append)
return self._handle_result(context, result)
约定的规则只有这些:
- 必须要有一个叫做name的类属性,该属性指定了插件的名称,我们在Job中就是通过这个名称去引用插件。
- 必须要有一个execute的实例方法,该方法包含了插件的执行逻辑,它接受一个上下文对象参数,还有自己的参数,这些参数跟我们在Job中通过args指定的参数相对应。
- 可以选择实现sys_prepare方法和sys_cleanup方法,这两个方法都会接受一个Config对象作为参数,Config对象的接口跟普通的dict对象没什么两样,你可以通过它获取配置文件的参数,sys_prepare方法用来初始化插件,比如初始化数据库连接,sys_cleanup执行清理动作,比如关闭连接。这两个方法由插件管理器负责进行回调。
编写完插件之后,在setup.py中将其注册到相应的entry_point即可,例如:
"girlfriend.plugin": [
# db plugin
"orm_query = girlfriend.plugin.orm:OrmQueryPlugin",
# table plugin
"table_adapter = girlfriend.plugin.table:TableAdapterPlugin",
"column2title = girlfriend.plugin.table:TableColumn2TitlePlugin",
"print_table = girlfriend.plugin.table:PrintTablePlugin",
"concat_table = girlfriend.plugin.table:ConcatTablePlugin",
"join_table = girlfriend.plugin.table:JoinTablePlugin",
"split_table = girlfriend.plugin.table:SplitTablePlugin",
"html_table = girlfriend.plugin.table:HTMLTablePlugin",
]
执行setup.py,这些插件就算安装了,插件管理器会根据entry_point去加载这些插件。
插件管理器
插件管理器是一个容器,Job通过这个容器来获得插件对象。girlfriend内置了基于girlfriend.plugin
这个注册点的插件管理器,你也可以参照girlfriend.plugin.PluginManager
类来自己定义管理器。