我正在编写一个Python库,并计划将sdist(.tar.gz)和wheel上传到PyPI。 构建文档说正在运行
python -m build
我从源树创建了 sdist ,并从 sdist 创建了 wheel ,这很好,因为我在这里“免费”测试了 sdist 。现在我想使用多个 python 版本对轮子运行测试(pytest)。最简单的方法是什么?
我一直在使用 tox,我看到有一个选项 将包设置为“wheel”:
[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
但这并没有说明轮子是如何生产的;我不确定是不是 a) 直接从源代码树创建轮子
b) 从 sdist 创建wheel,它是从源树创建的,其方式与相同python -m build
c) 从 sdist 创建wheel,它是从源树创建的,其方式不同于
python -m build
即使答案是c),tox测试的轮子也不会是上传的轮子,所以它没有测试正确的东西。 我很可能应该以某种方式将轮子作为 tox / test runner 的参数。 问题
轮运行测试的惯用方法是什么?我可以使用tox吗?
的 Tox 4.12.2 文档说明可以定义 external
包选项。外部
package选项意味着你设置
[testenv]
...
package = external
除此之外,还必须创建一个名为
[.pkg_external]
的部分(如果您编辑了具有别名
<package_env>_external
的 package_env,则为
isolated_build_env
)。在本节中,我们应该定义至少
package_glob
,它告诉毒物在哪里安装轮子。如果您还想创建轮子,您可以在
commands
的[.pkg_external]
选项中进行操作。简单的方法(多次构建)
[testenv:.pkg_external]
deps =
build==1.1.1
commands =
python -c 'import shutil; shutil.rmtree("{toxinidir}/dist", ignore_errors=True)'
python -m build -o {toxinidir}/dist
package_glob = {toxinidir}{/}dist{/}wakepy-*-py3-none-any.whl
让 tox 4.14.2 只构建一次轮子。从tox执行顺序(在附录中)可以看出,可用于此目的的一个钩子是“.pkg_external”(“requires”或“deps”)的tox_on_install
。我用它来放置一个虚拟文件(
/dist/.TOX-ASKS-REBUILD
),这意味着应该完成构建。如果 .TOX-ASKS-REBUILD
存在,则运行构建脚本时,将删除 /dist
文件夹及其所有内容,并创建包含 .tar.gz 和 .whl 文件的新 /dist
文件夹。
优点:
tox -e py311
skip_install=True
)
挂钩
tests/tox_utils/tox_hooks.py
from __future__ import annotations
import typing
from pathlib import Path
from typing import Any
from tox.plugin import impl
if typing.TYPE_CHECKING:
from tox.tox_env.api import ToxEnv
dist_dir = Path(__file__).resolve().parent.parent.parent / "dist"
tox_asks_rebuild = dist_dir / ".TOX-ASKS-REBUILD"
@impl
def tox_on_install(tox_env: ToxEnv, arguments: Any, section: str, of_type: str):
if (tox_env.name != ".pkg_external") or (of_type != "requires"):
return
# This signals to the build script that the package should be built.
tox_asks_rebuild.parent.mkdir(parents=True, exist_ok=True)
tox_asks_rebuild.touch()
pyproject.toml
[project.entry-points.tox]
mypkg_tox_hooks = "tests.tox_utils.tox_hooks"
build_mypkg.py
/tests/tox_utils/build_mypkg.py
import shutil
import subprocess
from pathlib import Path
dist_dir = Path(__file__).resolve().parent.parent.parent / "dist"
def build():
if not (dist_dir / ".TOX-ASKS-REBUILD").exists():
print("Build already done. skipping.")
return
print(f"Building sdist and wheel into {dist_dir}")
# Cleanup. Remove all older builds; the /dist folder and its contents.
# Note that tox would crash if there were two files with .whl extension.
# This also resets the TOX-ASKS-REBUILD so we build only once.
shutil.rmtree(dist_dir, ignore_errors=True)
out = subprocess.run(
f"python -m build -o {dist_dir}", capture_output=True, shell=True
)
if out.stderr:
raise RuntimeError(out.stderr.decode("utf-8"))
print(out.stdout.decode("utf-8"))
if __name__ == "__main__":
build()
tox.ini
[testenv]
; The following makes the packaging use the external builder defined in
; [testenv:.pkg_external] instead of using tox to create sdist/wheel.
; https://tox.wiki/en/latest/config.html#external-package-builder
package = external
[testenv:.pkg_external]
; This is a special environment which is used to build the sdist and wheel
; After running this environment, the *.whl and *.tar.gz are available in the
; dist/ folder.
deps =
; The build package from PyPA. See: https://build.pypa.io/en/stable/
build==1.1.1
commands =
python tests/tox_utils/build_mypkg.py
; This determines which files tox may use to install mypkg in the test
; environments. This should match with the file created during running the
; command defined in this section.
package_glob = {toxinidir}{/}dist{/}mypkg-*-py3-none-any.whl
附录
tox_print_hooks.py
) 中定义的虚拟挂钩文件和
系统概述中有关执行顺序的项目符号列表来对 tox 中的执行顺序进行逆向工程。请注意,我已经设置了
package = external
,这对输出有一些影响。这是 tox 的作用:1) CONFIGURATION
tox_register_tox_env
tox_add_core_config
tox_add_env_config (N+2 times[1])
2) ENVIRONMENT (for each environment)
tox_on_install (envname, deps)
envname: install_deps (if not cached)
If not all(skip_install) AND first time: [2]
tox_on_install (.pkg_external, requires)
.pkg_external: install_requires (if not cached)
tox_on_install (.pkg_external, deps)
.pkg_external: install_deps (if not cached)
If not skip_install:
.pkg_external: commands
tox_on_install (envname, package)
envname: install_package [3]
tox_before_run_commands (envname)
envname: commands
tox_after_run_commands (envname)
tox_env_teardown (envname)
N = tox 配置文件中的环境数量。 “2”来自 .pkg_external 和 .pkg_external_sdist_meta
[2]
“第一次”的意思是:第一次参与此毒害呼叫。仅当至少有一个选定的环境没有 skip_install=True
时才会执行此操作。
package = external
,它将从 package_glob
中 [testenv:.pkg_external]
定义的位置获取轮子
tox_print_hooks.py
from typing import Any
from tox.config.sets import ConfigSet, EnvConfigSet
from tox.execute.api import Outcome
from tox.plugin import impl
from tox.session.state import State
from tox.tox_env.api import ToxEnv
from tox.tox_env.register import ToxEnvRegister
@impl
def tox_register_tox_env(register: ToxEnvRegister) -> None:
print("tox_register_tox_env", register)
@impl
def tox_add_core_config(core_conf: ConfigSet, state: State) -> None:
print("tox_add_core_config", core_conf, state)
@impl
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
print("tox_add_env_config", env_conf, state)
@impl
def tox_on_install(tox_env: ToxEnv, arguments: Any, section: str, of_type: str):
print("tox_on_install", tox_env, arguments, section, of_type)
@impl
def tox_before_run_commands(tox_env: ToxEnv):
print("tox_before_run_commands", tox_env)
@impl
def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outcome]):
print("tox_after_run_commands", tox_env, exit_code, outcomes)
@impl
def tox_env_teardown(tox_env: ToxEnv):
print("tox_env_teardown", tox_env)