如何打包插件#

本节主要介绍如何 打包 AiiDA 扩展(插件),以便它们可以被测试、发布并最终被其他人重用。

参见

有关编写特定扩展的指南,请参阅 如何为外部代码编写插件增加对自定义数据类型的支持开发插件开发插件

创建插件包#

AiiDA 插件可以捆绑在 Python package 中发布,为 AiiDA 提供一系列扩展。

备注

Python 社区对 ‘package’ 一词的使用相当宽松。根据上下文,它可能仅指包含单个 Python 模块的文件夹,也可能包括构建和安装通过 Python Package Index (PyPI) 发布的软件包所需的文件。

快速入门#

启动 AiiDA 插件包的最快方法是使用 AiiDA plugin cutter,以模板化基本文件夹结构。

只需访问 AiiDA plugin cutter 并按照使用说明操作即可。另请参阅 aiida-diff 演示插件包,深入了解插件切割器生成的文件和文件夹。

下面,我们将解释 AiiDA 插件切割器的一些约定。

选择名称#

AiiDA 插件包的命名规则是:PyPI 上的插件发布包为 aiida-mycode,相应的 python 包为 aiida_mycode,因此文件夹结构如下::

aiida-mycode/
   aiida_mycode/
      __init__.py

备注

Python 软件包名称不能包含破折号,因此要使用下划线。

如果您打算最终发布您的插件包,请访问 AiiDA plugin registry 并选择一个尚未被使用的名称。我们也鼓励您预先注册您的软件包(注册表上提供了说明),这样既可以保留您的插件名称,也可以告知他人您正在进行的开发。

文件夹结构#

插件的整体文件夹结构由你决定,但遵循一套基本的约定是有用的。下面是一个 AiiDA 插件的文件夹结构示例,说明了不同层次的嵌套(另见 aiida-diff demo plugin)::

aiida-mycode/           - distribution folder
   aiida_mycode/        - top-level package (from aiida_mycode import ..)
      __init__.py
      calculations/
         __init__.py
         mycode.py      - contains MycodeCalculation
      parsers/
         __init__.py
         basic.py       - contains BasicMycodeParser
         full.py        - contains FullMycodeParser
      data/
         __init__.py    - contains code-specific MyData data format
      commands.py       - contains verdi subcommand for visualizing MyData
      workflows/
         __init__.py
         basic.py       - contains a basic workflow using mycode
      ...
   LICENSE              - license of your plugin
   MANIFEST.in          - lists non-python files to be installed, such as LICENSE
   README.md            - project description for github and PyPI
   pyproject.toml       - plugin metadata: installation requirements, author, entry points, etc.
   ...

一个最小的插件包可能是这样的:…..:

aiida-minimal/
   aiida_minimal/
      __init__.py
   pyproject.toml

通过 entry points 注册插件#

AiiDA 插件是 AiiDA 的一个扩展,它通过一个新的 entry point 来宣布自己(详情请参阅 什么是 entry point?)。添加一个新的 entry point 包括以下步骤:

  1. 决定名称。我们*强烈建议每个 entry point 的名称以插件包的名称开头(省略 aiida-``前缀)。对于软件包 ``aiida-mycode,通常指 \``mycode.<something>\``

  2. 查找正确的 entry point 组。你可以通过 verdi plugin list 列出 AiiDA 定义的 entry point 组。有关组的文档,请参见 AiiDA entry point 小组

  3. pyproject.toml 文件的 entry_points 字段中添加 entry point::

    ...
    [project.entry-points."aiida.calculations"]
    "mycode.<something>" = "aiida_mycode.calcs.some:MysomethingCalculation"
    ...
    

新的 entry point 现在应该显示在 verdi plugin list aiida.calculations 中。

备注

以名称为 aiida-diff 的软件包为例,pip install aiida-diff 的作用是什么?

  • 它会解析并安装其他 python 软件包的依赖关系

  • 它会创建 aiida_diff.egg-info/ 文件夹,其中包含软件包的元数据

  • 如果给出 -e``选项,则会从 python 软件包搜索路径创建一个指向 ``aiida-diff 目录的符号链接,并将 .egg-info 文件夹放在该目录下。对 源代码 的修改将被 python 接收,无需重新安装(重启解释器时),但对 元数据 的修改不会。

更多详情,请参见 Python packaging user guide

测试插件包#

为你的AiiDA插件编写测试,并使用免费平台如`GitHub Actions <ghactions>`_运行持续集成测试,是确保你的插件工作并在开发过程中持续工作的最好方法。我们推荐使用 pytest 框架来测试 AiiDA 插件。

有关如何编写测试和如何设置持续集成的示例,请参阅 aiida-diff 演示插件包。

文件夹结构#

我们建议在 AiiDA 插件包中包含测试时使用以下文件夹结构::

aiida-mycode/           - distribution folder
   aiida_mycode/        - plugin package
   tests/               - tests directory (possibly with subdirectories)

备注

将测试放在插件包之外,可以减少插件包的分发量。

AiiDA 的固定装置#

许多测试需要在测试开始前建立完整的 AiiDA 环境,例如一些 AiiDA 数据 nodes。pytest 库有 fixtures 的概念,用来封装你想在测试开始前运行的代码。AiiDA 在 aiida.manage.tests.pytest_fixtures 中附带了许多固定装置,可以为你设置测试环境(更多详情,请参阅 插件测试夹具)。

为了在测试中使用这些固定装置,请在插件包的根级别创建 conftest.py (另见 pytest docs),如下所示::

import pytest
pytest_plugins = ['aiida.manage.tests.pytest_fixtures']  # make AiiDA's fixtures available
# tip: look inside aiida.manage.tests.pytest_fixtures to see which fixtures are provided

@pytest.fixture(scope='function')  # a fixture that will run once per test function that requests it
def integer_input():
    """Integer input for test run."""
    from aiida.orm import Int
    input_value = Int(5)
    return input_value

@pytest.fixture(scope='function', autouse=True)  # a fixture that automatically runs once per test function
def clear_database_auto(clear_database):  # request AiiDA's "clear_database" fixture
    """Automatically clear database in between tests."""
    pass

现在您可以开始编写测试,例如在 tests/test_calculations.py 文件中::

# No need to import fixtures here - they are added by pytest "automagically"

def test_qe_calculation(aiida_local_code_factory, integer_input):  # requesting "aiida_local_code_factory" and "integer_input" fixtures
    """Test running a calculation using a CalcJob plugin."""
    from aiida.engine import run
    from aiida.plugins import CalculationFactory

    # search for 'pw.x' executable in PATH, set up an AiiDA code for it and return it
    code = aiida_local_code_factory(entry_point='quantumespresso.pw', executable='pw.x')
    # ...
    inputs = { 'code': code, 'int_input': integer_input, ... }  # use "integer_input" fixture

    # run a calculation using this code ...
    result = run(CalculationFactory('quantumespresso.pw'), **inputs)

    # check outputs of calculation
    assert result['...'] == ...

pytest 会自动发现并执行以 test 开头的文件、类和函数名。

记录插件包#

AiiDA 外挂程序包是 python 程序包,适用于一般 best practises for writing python documentation

在下文中,我们会提到一些特别适用于 AiiDA 插件的提示。

存储库级文档#

由于大多数 AiiDA 插件的源代码都托管在 GitHub 上,新用户与插件包的第一次接触很可能就是 GitHub 仓库的登陆页面。

  • 确保提供有用的 README.md,说明您的插件的作用和安装方法。

  • 留下联系邮箱和添加许可证也是一个好主意。

  • 确保 pyproject.toml 文件中的信息是正确和最新的(尤其是版本号),因为这些信息用于在 AiiDA 插件注册表中宣传你的软件包。

源代码级文档#

源代码级别的文档对于使用插件的 python API 的用户,尤其是对于吸引其他人的贡献都很重要。

添加新的计算或 workflow 时,请确保使用 docstrings,并使用 help 参数记录输入端口和输出端口。这样,插件的用户就可以通过 verdi cli 直接查看计算/workflow 期望的输入和输出。例如,请尝试::

verdi plugin list aiida.calculations core.arithmetic.add

文件网站#

对于简单的插件而言,编写一份完善的 README.md 可以是一个良好的开端。一旦 README 的篇幅过长,您可能需要考虑创建一个专门的文档网站。

Sphinx 工具可让您轻松创建 python 软件包的文档网站,而 ReadTheDocs 服务则可免费在线托管您的 sphinx 文档。aiida-diff demo plugin 附带基于 sphinx 文档的完整模板,包括手动编写的页面和自动生成的插件 python API 文档。有关如何构建的说明,请参见 developer guide of aiida-diff

AiiDA 提供了一个 sphinx 扩展,用于在你的 sphinx 文档中插入自动生成的 Process 类文档(计算和 workflow)(类似于 verdi plugin list 显示的信息)。在 docs/conf.py 文件的 extensions 列表中添加 aiida.sphinxext 以启用扩展。现在,您可以在 ReST 文件中使用 aiida-processaiida-calcjobaiida-workchain 指令,如::

.. aiida-workchain:: MyWorkChain
    :module: my_plugin
    :hide-nondb-inputs:

给你

  • MyWorkChain 是要记录的工作链名称。

  • :module: 是 python 模块,工作链可通过该模块进行 import。

  • :hide-unstored-inputs: 隐藏未存储在数据库中的工作链输入(默认显示)。

备注

aiida-workchain 指令与 sphinx.ext.autodoc 挂钩,也就是说,当应用于工作链类时,通用的 automoduleautoclass 指令会自动使用该指令。

发布插件包#

AiiDA 插件包发布在 AiiDA plugin registrypython package index (PyPI) 上。

在发布您的插件之前,请确保您的插件自带:

  • 包含插件元数据的 pyproject.toml 文件,用于通过 pip 安装插件

  • 执照

有关这些文件的示例,请参见 aiida-diff demo plugin

在插件注册表上发布#

AiiDA plugin registry 旨在成为所有公开可用的 AiiDA 插件的家园。它收集的信息包括你的软件包提供的插件类型、与哪些 AiiDA 版本兼容等。

要注册您的插件包,只需访问 plugin registry 并按照 README 中的说明操作即可。

备注

插件注册表会从插件库中的 pyproject.toml 文件读取插件的元数据。

我们鼓励您 尽快列出您的插件包 ,以便保留插件名称,并告知其他人正在进行的开发。

在 PyPI 上发布#

对于分发 AiiDA 插件包,我们建议遵循 guidelines for packaging python projects,其中包括在 python package index 上提供插件。这使得用户可以简单地 pip install aiida-myplugin