跳转到主要内容

Bottle的依赖注入。

项目描述

Bottle依赖注入

Bottle框架已经在某种程度上实现了依赖注入:您的路由的URL参数被注入到处理函数的关键字参数中。其他一些插件(实际上,大多数插件)也这样做:它们注入数据库连接、认证上下文、会话对象等。此插件使您无需为每个要注入的依赖项编写新的插件即可使用此概念。它还可以在您允许的情况下,以根本的方式改变您使用Bottle和编写应用程序的方式。如果做得正确,依赖注入可以极大地减少应用程序的复杂性,并提高测试性和可读性。但让我们从简单的事情开始,从一个简单的例子开始

app = Bottle()
injector = app.install(bottle.ext.inject.Plugin())

@injector.provider('db')
def get_db_handle():
    return my_database_connection.cursor()

@app.route('/random_quote')
def random_quote(db):
    row = db.execute('SELECT quote FROM quotes ORDER BY RANDOM() LIMIT 1').fetchone()
    return row['quote']

前两行没有什么新东西。我们只是创建了一个Bottle应用程序并将其安装到其中。下一个块更有趣。与Bottle将处理函数绑定到URL路径的方式类似,注入器将提供者绑定到注入点。在这种情况下,我们将提供者‘get_db_handle’绑定到名为‘db’的注入点。每当通过我们的注入器调用函数并且有一个同名参数时,它将从我们的提供者那里接收一个新的数据库游标。您可以在下面的几行中看到这一点。因为所有处理程序回调都由我们的注入器插件管理,所以您只需要接受一个‘db’参数,它就会被插件自动注入。如果您定义一个不接受‘db’参数的路由,则不会发生任何事情。该路由永远不会为该路由创建数据库游标。

这个小小的例子很好地展示了依赖注入的好处

  • 您可以直接通过传递伪造或测试数据库对象来单元测试‘random_quote()’函数。无需为测试设置整个应用程序。

  • 没有使用全局变量或全局状态。该函数可以在不同的上下文中再次使用而无需麻烦。

  • 您不需要在每个定义bottle应用路由的模块中导入get_db_handle

  • 您可以更改‘get_db_handle’的实现,这将影响您应用程序的每个路由。无需搜索/替换您的代码库。

  • 少打字。在重要的事情上偷懒。

高级用法

值、提供者和解析器

依赖值在每次注入时都重复使用,并被视为单例。

每次需要依赖项时,都会用无参数调用provider。每次需要依赖项时都会调用provider。

resolver返回一个可缓存的provider,可能接受注入点特定的配置。解析器通常在每个注入点只调用一次,返回值将被缓存。它必须返回一个(可调用的)provider。

注入点

待办事项:描述inject()函数及其使用方法。

def my_func(
    db                 # Positional arguments are always recognized as injection points with the same name.
    a = 'value'        # Keyword arguments with default values are not recognized.
    b = inject('db')   # Here we explicitly define an injection point. The name of the argument is no longer
                       #  important.
    c: inject('db')    # In Python 3 you can use the annotation syntax. (recommended)
):
    pass
# Python 2
def func(name = inject('param', key='value'),
         file = inject('file', field='upload')):
    pass

# Python 3
def func(name: inject('param', key='name'),
         file: inject('file', field='upload')):
    pass

待办事项:描述显式(使用inject()注解)和隐式(未注解)注入点的区别。简而言之:如果解析器缺失,显式注入点将立即失败,而隐式注入点只有在实际解析时才会失败。

待办事项:您可以禁用对未注解参数的注入(也许)。

递归依赖注入

待办事项:描述递归注入(将内容注入到提供者和解析器中),这已经可以工作了。

默认注入点

该插件附带一组预定义的提供者。您可以直接使用它们,或者如果您不想使用它们,可以取消注册。

注入点

类型

范围

描述

request, req, rq

bottle.Request

局部

response, res, rs

bottle.Response

局部

injector

注入器

app

注入器本身。可用于运行时检查可注入值,例如,由其他插件执行。

params

bottle.FormsDict

局部

未实现。

param[name]

str

局部

未实现。

什么是“依赖注入”?

“依赖注入”这个术语只是简单概念的华丽名称:代码的调用者应该提供代码运行所需的全部依赖项。换句话说:一个函数或对象不应该需要伸手,而是提供它所需的一切。

一个小例子可能最能说明。以下代码遵循依赖注入范式

db = my_database_connection.cursor()

def do_stuff():
    db.execute('...')

do_stuff()

现在,使用依赖注入

def do_stuff(db):
    db.execute('...')

do_stuff(my_database_connection.cursor())

唯一的区别是我们现在明确地将数据库连接句柄传递给函数,而不是让函数从全局命名空间中获取它。基本上就是这样。现在您可以轻松地通过传递一个假数据库连接或测试数据库的连接来测试do_stuff,在具有不同数据库的其他上下文中重复使用它,并且可能的副作用不再隐藏在代码中。

缺点是您需要打更多的字,需要传递很多东西,但这正是该插件为您做的事情:它管理依赖项并在需要的地方注入它们。

词汇表

注入器

管理依赖项提供者解析器的对象,并可要求将其所需依赖项注入函数调用。

注入点

注入依赖项的地方。此插件通常在函数调用参数中注入。

消费者

定义依赖项的函数或可调用对象,以便注入器可以注入它们。

依赖项

可以注入的对象或资源。

提供者

创建依赖项的函数或可调用对象,或根据需要提供依赖项。

解析器

根据注入点特定配置创建单个提供者的函数或可调用对象。(是的,您可以称其为“依赖提供者提供者”,但这听起来很糟糕)

变更

路线图

  • 做些事情…

变更 0.1

  • 初始版本

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源代码分发

Bottle-Inject-0.1.3.tar.gz (8.1 kB 查看哈希值)

上传时间 源代码

构建分发

Bottle_Inject-0.1.3-py3-none-any.whl (11.1 kB 查看哈希值)

上传时间 Python 3

Bottle_Inject-0.1.3-py2-none-any.whl (11.1 kB 查看哈希值)

上传时间 Python 2