构建过程中使用的诗歌脚本应该如何存储在项目中?

Dia*_*o10 10 python directory-structure package build-script python-poetry

假设您正在使用 Poetry 来管理 Python PyPI 包。目前,您的项目有一个Makefile包含管理项目安装、单元测试和 linting 的过程。然而,由于这违背了 Poetry 的精神,即理想情况下您有一个配置文件来统治所有这些 ( pyproject.toml),并且因为 saidMakefile在 Windows 构建上可能很烦人,所以您希望Makefile通过以下方式直接将功能移至由 Poetry 管理:利用Poetry 对setuptools脚本的支持。

\n

这是这样一个示例Makefile

\n
PYMODULE := awesome_sauce\nTESTS := tests\nINSTALL_STAMP := .install.stamp\nPOETRY := $(shell command -v poetry 2> /dev/null)\nMYPY := $(shell command -v mypy 2> /dev/null)\n\n.DEFAULT_GOAL := help\n\n.PHONY: all\nall: install lint test\n\n.PHONY: help\nhelp:\n    @echo "Please use \'make <target>\', where <target> is one of"\n    @echo ""\n    @echo "  install     install packages and prepare environment"\n    @echo "  lint        run the code linters"\n    @echo "  test        run all the tests"\n    @echo "  all         install, lint, and test the project"\n    @echo "  clean       remove all temporary files listed in .gitignore"\n    @echo ""\n    @echo "Check the Makefile to know exactly what each target is doing."\n    @echo "Most actions are configured in \'pyproject.toml\'."\n\ninstall: $(INSTALL_STAMP)\n$(INSTALL_STAMP): pyproject.toml\n    @if [ -z $(POETRY) ]; then echo "Poetry could not be found. See https://python-poetry.org/docs/"; exit 2; fi\n    $(POETRY) run pip install --upgrade pip setuptools\n    $(POETRY) install\n    touch $(INSTALL_STAMP)\n\n.PHONY: lint\nlint: $(INSTALL_STAMP)\n    # Configured in pyproject.toml\n    # Skips mypy if not installed\n    @if [ -z $(MYPY) ]; then echo "Mypy not found, skipping..."; else echo "Running Mypy..."; $(POETRY) run mypy $(PYMODULE) $(TESTS); fi\n    @echo "Running Flake8..."; $(POETRY) run pflake8 # This is not a typo\n    @echo "Running Pylint..."; $(POETRY) run pylint $(PYMODULE)\n\n.PHONY: test\ntest: $(INSTALL_STAMP)\n    # Configured in pyproject.toml\n    $(POETRY) run pytest\n\n.PHONY: clean\nclean:\n    # Delete all files in .gitignore\n    git clean -Xdf\n
Run Code Online (Sandbox Code Playgroud)\n

但接下来,你又面临另一个问题;由于这些脚本的运行方式,它们应该位于自己的包或模块中,因此第一个想法是将脚本简单地包含为项目包本身的一部分:

\n
awesome_sauce\n \xe2\x94\xa3 awesome_sauce\n \xe2\x94\x83 \xe2\x94\xa3 poetry_scripts\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\xa3 run_unit_tests.py\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\x97 run_linters.py\n \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x97 sauce.py\n \xe2\x94\xa3 docs\n \xe2\x94\xa3 tests\n \xe2\x94\xa3 .gitignore\n \xe2\x94\xa3 poetry.lock\n \xe2\x94\x97 pyproject.toml\n
Run Code Online (Sandbox Code Playgroud)\n

在这种情况下,您可以添加以下内容pyproject.toml

\n
[tool.poetry.scripts]\ntests = \'awesome_sauce.poetry_scripts.run_unit_tests:main\'\nlinters = \'awesome_sauce.poetry_scripts.run_linters:main\'\n
Run Code Online (Sandbox Code Playgroud)\n

它们可以运行为

\n
PYMODULE := awesome_sauce\nTESTS := tests\nINSTALL_STAMP := .install.stamp\nPOETRY := $(shell command -v poetry 2> /dev/null)\nMYPY := $(shell command -v mypy 2> /dev/null)\n\n.DEFAULT_GOAL := help\n\n.PHONY: all\nall: install lint test\n\n.PHONY: help\nhelp:\n    @echo "Please use \'make <target>\', where <target> is one of"\n    @echo ""\n    @echo "  install     install packages and prepare environment"\n    @echo "  lint        run the code linters"\n    @echo "  test        run all the tests"\n    @echo "  all         install, lint, and test the project"\n    @echo "  clean       remove all temporary files listed in .gitignore"\n    @echo ""\n    @echo "Check the Makefile to know exactly what each target is doing."\n    @echo "Most actions are configured in \'pyproject.toml\'."\n\ninstall: $(INSTALL_STAMP)\n$(INSTALL_STAMP): pyproject.toml\n    @if [ -z $(POETRY) ]; then echo "Poetry could not be found. See https://python-poetry.org/docs/"; exit 2; fi\n    $(POETRY) run pip install --upgrade pip setuptools\n    $(POETRY) install\n    touch $(INSTALL_STAMP)\n\n.PHONY: lint\nlint: $(INSTALL_STAMP)\n    # Configured in pyproject.toml\n    # Skips mypy if not installed\n    @if [ -z $(MYPY) ]; then echo "Mypy not found, skipping..."; else echo "Running Mypy..."; $(POETRY) run mypy $(PYMODULE) $(TESTS); fi\n    @echo "Running Flake8..."; $(POETRY) run pflake8 # This is not a typo\n    @echo "Running Pylint..."; $(POETRY) run pylint $(PYMODULE)\n\n.PHONY: test\ntest: $(INSTALL_STAMP)\n    # Configured in pyproject.toml\n    $(POETRY) run pytest\n\n.PHONY: clean\nclean:\n    # Delete all files in .gitignore\n    git clean -Xdf\n
Run Code Online (Sandbox Code Playgroud)\n

但这并不理想;现在,您的构建配置是软件包的一部分,并且可能会直接投入生产,除非您在 CI/CD 过程中完成了一些奇特的事情。它对最终用户来说是沉重的负担,并且不必要地向包命名空间添加内容。

\n

然后,另一个想法是将所有内容都放在顶级脚本中:

\n
awesome_sauce\n \xe2\x94\xa3 awesome_sauce\n \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x97 sauce.py\n \xe2\x94\xa3 docs\n \xe2\x94\xa3 tests\n \xe2\x94\xa3 .gitignore\n \xe2\x94\xa3 poetry.lock\n \xe2\x94\xa3 poetry_scripts.py\n \xe2\x94\x97 pyproject.toml\n
Run Code Online (Sandbox Code Playgroud)\n

这会将行更改pyproject.toml

\n
[tool.poetry.scripts]\ntests = \'poetry_scripts:run_unit_tests\'\nlinters = \'poetry_scripts:run_linters\'\n
Run Code Online (Sandbox Code Playgroud)\n

但这也不一定是理想的,因为现在您可能在项目根目录中有一个相对较长的脚本,并且主观上它可能很丑陋。尽管至少现在它不应该成为生产包的一部分,但这是一个净收益。

\n

这两种方法可以组合起来,第一个版本中的脚本包将简单地移动到根目录,这意味着该项目现在由两个不同的包组成。

\n
awesome_sauce\n \xe2\x94\xa3 awesome_sauce\n \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x97 sauce.py\n \xe2\x94\xa3 docs\n \xe2\x94\xa3 poetry_scripts\n \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\xa3 run_unit_tests.py\n \xe2\x94\x83 \xe2\x94\x97 run_linters.py\n \xe2\x94\xa3 tests\n \xe2\x94\xa3 .gitignore\n \xe2\x94\xa3 poetry.lock\n \xe2\x94\x97 pyproject.toml\n
Run Code Online (Sandbox Code Playgroud)\n
[tool.poetry.scripts]\ntests = \'poetry_scripts.run_unit_tests:main\'\nlinters = \'poetry_scripts.run_linters:main\'\n
Run Code Online (Sandbox Code Playgroud)\n

但尚不清楚这是否是更好的方法。

\n

setuptools对于处理给定项目中的诗歌/脚本是否存在共识?如果是这样,我不认为以前有过公开记录。

\n

Nat*_*Nat 4

我不认为对此有明确的共识。

\n

然而,我一直在研究一个名为Poe the Poet 的解决方案,它比make更适合大多数项目;它与诗歌完美结合,跨平台无缝工作(取决于您如何定义任务),并且它是自我记录的。

\n

首先,重要的是要意识到我认为[tool.poetry.scripts]这根本不是您想要的,因为您在那里定义的任何内容实际上都会安装到安装软件包的任何环境的 PATH 中!

\n

Pothepoet 允许您做的是在 pyproject.toml 中定义任务,如下所示:

\n
[tool.poe.tasks]\ntest = "pytest -v"\n
Run Code Online (Sandbox Code Playgroud)\n

然后可以使用 poe CLI 工具调用,例如

\n
[tool.poe.tasks]\ntest = "pytest -v"\n
Run Code Online (Sandbox Code Playgroud)\n

或者,如果您想在 python 模块中定义任务逻辑(并在执行任务时使任务自我记录),您可以执行以下操作:

\n
[tool.poe.tasks.lint]\nscript = "poetry_scripts.run_linters:main"\nhelp   = "Check code style and such"\n
Run Code Online (Sandbox Code Playgroud)\n

不带任何参数运行poe会打印文档,包括可用任务及其帮助消息。

\n

至于项目布局,我建议将所有 python 代码放在下面./src以获得如下内容:

\n
awesome_sauce\n \xe2\x94\xa3 src\n \xe2\x94\x83 \xe2\x94\xa3 awesome_sauce\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\x97 sauce.py\n \xe2\x94\x83 \xe2\x94\xa3 scripts\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\xa3 __init__.py\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\xa3 run_unit_tests.py\n \xe2\x94\x83 \xe2\x94\x83 \xe2\x94\x97 run_linters.py\n \xe2\x94\xa3 docs\n \xe2\x94\xa3 tests\n \xe2\x94\xa3 .gitignore\n \xe2\x94\xa3 poetry.lock\n \xe2\x94\x97 pyproject.toml\n
Run Code Online (Sandbox Code Playgroud)\n

然后在 pyproject.toml 的诗歌部分中设置以下内容,以便 Awesome_sauce 包含在您的构建中,但 src 中的其他所有内容都将被忽略poetry build

\n
packages = [{include = "awesome_sauce", from = "src"}]\n
Run Code Online (Sandbox Code Playgroud)\n

它也可以用作诗歌插件。

\n

  • 说实话,我并不完全同意这个建议。首先,它需要添加尚未被验证对构建过程安全的第三方依赖项,这在对依赖项(包括我的)进行大量审核的公司项目中可能不可行。其次,我认为在现代 Python 库中使用 `src` 作为项目目录是非标准或过时的。 (3认同)
  • 如果你的工具集受到限制,那么事情就会变得更加困难......可能从你的 Makefile 调用命令或 python 脚本是你能做的最好的事情。至于使用 src 布局,我不知道它是非标准还是过时的,但[pytest推荐](https://docs.pytest.org/en/6.2.x/goodpractices.html )、[支持](https://python-poetry.org/docs/cli/) 和[使用](https://github.com/python-poetry/poetry),并具有[一些具体的优点] (https://hynek.me/articles/testing-packaging/)。我相信这对您的情况有所帮助。 (3认同)