新的预命令和后命令插件钩子
随着最新的 conda 版本发布(在撰写本文时为 23.7.2
),引入了定义两个新插件钩子的功能:“预命令”和“后命令”。这两个新的插件钩子让插件作者能够在 conda 命令运行之前和之后执行代码。在这篇博文中,我们将更详细地介绍如何以及为什么可以使用它们来扩展 conda 的默认行为。
有关如何在实践中使用这些插件钩子的完整功能示例,请查看 conda-protect 项目。
为了解释如何使用这些插件钩子,我们将介绍两个示例。
Conda protect 和“预命令”钩子
上面链接的项目称为 conda-protect
,它通过添加功能来扩展 conda,以“保护”conda 环境免受更改。有时用户可能希望这样做,以保护自己免受 无意修改其基本环境。
使用“预命令”插件钩子,我们首先可以指定要保护的命令,然后运行在执行命令之前调用的函数。通过这样做,我们可以找出环境是否受到保护,如果受到保护,则尽早退出。以下是此类插件钩子的定义方式
from conda import plugins
def conda_protect_pre_commands_action(command: str) -> None:
"""Checks to see if the current environment is protected"""
environment = get_current_environment()
if is_guarded(environment):
raise CondaError(
f"Current environment is protect. Run `conda guard {environment}` to "
"remove protection."
)
@plugins.hookimpl
def conda_pre_commands():
yield plugins.CondaPreCommand(
name=f"conda_protect_pre_command",
action=conda_protect_pre_commands_action,
run_for={"install", "remove", "update", "env_update", "env_remove"},
)
在上面的示例中,我们首先通过定义名为 conda_pre_commands
的函数来注册我们的插件钩子,然后使用 conda 提供的 hookimpl
函数对其进行装饰。在钩子函数本身内部,我们返回一个 CondaPreCommand
对象,它定义了以下内容
- name:此插件钩子在 conda 内部将如何引用
- action:在
run_for
中定义的命令执行之前将调用的可调用对象 - run_for:此插件钩子应与之一起使用的命令的
set
回到我们的示例,如果我们想要创建一个插件,该插件可以防止我们错误地修改受保护的环境,那么将 run_for
定义为所有可能修改环境的 conda 命令(例如 install
、remove
等)是有意义的。
为了实际防止我们修改受保护的环境,action
函数会检查当前环境是否受到保护,然后如果受到保护,则抛出 CondaError
,以确保程序尽早退出,并向用户显示有意义的错误消息。
有关完整实现,请参阅 conda-protect 项目。
使用“后命令”钩子的简单命令计数器
“后命令”钩子的功能与“预命令”完全相同,只是它在 conda 命令成功完成运行后被调用。需要注意的是,如果命令由于任何原因过早退出,则不会调用此插件钩子。
为了说明如何使用它,我们将创建一个简单的插件来计算运行特定 conda 命令的次数。我们以后可以使用它来分析我们对 conda 的使用情况 🤓 📊
以下是如何使该插件看起来的代码片段
from conda.cli.conda_argparse import BUILTIN_COMMANDS
from conda import plugins
ENV_COMMANDS = {
"env_config", "env_create",
"env_export", "env_list",
"env_remove", "env_update"
}
def conda_stats_post_commands_action(command: str):
"""Counts how many times we have run a particular conda command"""
database = get_database()
database.add_command(command)
@plugins.hookimpl
def conda_post_commands():
yield plugins.CondaPostCommand(
name=f"conda_stats_post_command",
action=conda_stats_post_commands_action,
run_for=BUILTIN_COMMANDS.union(ENV_COMMANDS),
)
上面的示例通过定义名为 conda_post_commands
的函数来注册我们的“后命令”钩子。与“预命令”钩子非常相似,它返回一个 CondaPostCommand
对象,该对象定义了 name
、action
和 run_for
。action
函数在每次运行命令时计算命令的使用次数,并且 run_for
属性被配置为针对每个内置 conda 命令运行,包括 conda env *
命令。
数据库的实现故意从示例中省略,因为它超出了这篇博文的范围(自己动手实现它,很有趣!😅)。
总结
如果你想开始使用这些新的插件,请查看 conda-protect 项目。此项目也可以用作插件的起始模板。
有关 conda 目前可用的所有插件钩子的更多信息,请访问 相关文档页面。
与往常一样,欢迎您访问我们的 Matrix 聊天室,提出任何问题或提供反馈。
祝您编码愉快 ✌️