diff --git a/craft_parts/plugins/poetry_plugin.py b/craft_parts/plugins/poetry_plugin.py index b4f81225d..3306ab97e 100644 --- a/craft_parts/plugins/poetry_plugin.py +++ b/craft_parts/plugins/poetry_plugin.py @@ -40,6 +40,16 @@ class PoetryPluginProperties(PluginProperties, frozen=True): title="Optional dependency groups", description="optional dependency groups to include when installing.", ) + poetry_export_extra_args: list[str] = pydantic.Field( + default_factory=list, + title="Extra arguments for poetry export", + description="extra arguments to pass to poetry export when creating requirements.txt.", + ) + poetry_pip_extra_args: list[str] = pydantic.Field( + default_factory=list, + title="Extra arguments for pip install", + description="extra arguments to pass to pip install installing dependencies.", + ) # part properties required by the plugin source: str # pyright: ignore[reportGeneralTypeIssues] @@ -115,6 +125,7 @@ def _get_poetry_export_commands(self, requirements_path: pathlib.Path) -> list[s export_command.append( f"--with={','.join(sorted(self._options.poetry_with))}", ) + export_command.extend(self._options.poetry_export_extra_args) return [shlex.join(export_command)] @@ -128,10 +139,11 @@ def _get_pip_install_commands(self, requirements_path: pathlib.Path) -> list[str :returns: A list of strings forming the install script. """ pip = self._get_pip() + pip_extra_args = shlex.join(self._options.poetry_pip_extra_args) return [ # These steps need to be separate because poetry export defaults to including # hashes, which don't work with installing from a directory. - f"{pip} install --requirement={requirements_path}", + f"{pip} install {pip_extra_args} --requirement={requirements_path}", # All dependencies should be installed through the requirements file made by # poetry. f"{pip} install --no-deps .", diff --git a/docs/common/craft-parts/reference/plugins/poetry_plugin.rst b/docs/common/craft-parts/reference/plugins/poetry_plugin.rst index 2a7f6bd3e..92bece70a 100644 --- a/docs/common/craft-parts/reference/plugins/poetry_plugin.rst +++ b/docs/common/craft-parts/reference/plugins/poetry_plugin.rst @@ -16,6 +16,18 @@ well as those for :ref:`sources `. Additionally, this plugin provides the plugin-specific keywords defined in the following sections. +poetry-export-extra-args: +~~~~~~~~~~~~~~~~~~~~~~~~~ +**Type:** list of strings + +Extra arguments to pass at the end of the poetry `export command`_. + +poetry-pip-extra-args: +~~~~~~~~~~~~~~~~~~~~~~ +**Type:** list of strings + +Extra arguments to pass to ``pip install`` when installing dependencies. + poetry-with: ~~~~~~~~~~~~ **Type:** list of strings @@ -105,4 +117,5 @@ During the build step, the plugin performs the following actions: .. _Poetry: https://python-poetry.org .. _dependency groups: https://python-poetry.org/docs/managing-dependencies#dependency-groups +.. _export command: https://python-poetry.org/docs/cli/#export .. _environment variables to configure Poetry: https://python-poetry.org/docs/configuration/#using-environment-variables diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index dde9f46de..5d02b74f9 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -9,6 +9,8 @@ New features: - Add a :doc:`uv plugin` for projects that use the uv build system. +- Add new ``poetry-export-extra-args`` and ``poetry-pip-extra-args`` keys + to the :doc:`poetry plugin` Bug fixes: diff --git a/tests/integration/plugins/test_poetry.py b/tests/integration/plugins/test_poetry.py index 42ca29775..7b6a4e063 100644 --- a/tests/integration/plugins/test_poetry.py +++ b/tests/integration/plugins/test_poetry.py @@ -43,7 +43,12 @@ def source_directory(request): @pytest.fixture def poetry_part(source_directory): - return {"source": str(source_directory), "plugin": "poetry"} + return { + "source": str(source_directory), + "plugin": "poetry", + "poetry-export-extra-args": ["--without-hashes"], + "poetry-pip-extra-args": ["--no-deps"], + } @pytest.fixture diff --git a/tests/unit/plugins/test_poetry_plugin.py b/tests/unit/plugins/test_poetry_plugin.py index 7d36c365a..6b95b54d5 100644 --- a/tests/unit/plugins/test_poetry_plugin.py +++ b/tests/unit/plugins/test_poetry_plugin.py @@ -136,20 +136,38 @@ def get_build_commands( @pytest.mark.parametrize( - ("optional_groups", "export_addendum"), + ("optional_groups", "poetry_extra_args", "export_addendum"), [ - (set(), ""), - ({"dev"}, " --with=dev"), - ({"toml", "yaml", "silly-walks"}, " --with=silly-walks,toml,yaml"), + (set(), [], ""), + ({"dev"}, [], " --with=dev"), + ({"toml", "yaml", "silly-walks"}, [], " --with=silly-walks,toml,yaml"), + (set(), ["--no-binary=:all:"], " --no-binary=:all:"), ], ) -def test_get_build_commands(new_dir, optional_groups, export_addendum): +@pytest.mark.parametrize( + ("pip_extra_args", "pip_addendum"), + [ + ([], ""), + (["--no-binary=:all:"], "--no-binary=:all:"), + (["--pre", "-U"], "--pre -U"), + ], +) +def test_get_build_commands( + new_dir, + optional_groups, + poetry_extra_args, + export_addendum, + pip_extra_args, + pip_addendum, +): info = ProjectInfo(application_name="test", cache_dir=new_dir) part_info = PartInfo(project_info=info, part=Part("p1", {})) properties = PoetryPlugin.properties_class.unmarshal( { "source": ".", "poetry-with": optional_groups, + "poetry-export-extra-args": poetry_extra_args, + "poetry-pip-extra-args": pip_extra_args, } ) @@ -160,7 +178,7 @@ def test_get_build_commands(new_dir, optional_groups, export_addendum): f'PARTS_PYTHON_VENV_INTERP_PATH="{new_dir}/parts/p1/install/bin/${{PARTS_PYTHON_INTERPRETER}}"', f"poetry export --format=requirements.txt --output={new_dir}/parts/p1/build/requirements.txt --with-credentials" + export_addendum, - f"{new_dir}/parts/p1/install/bin/pip install --requirement={new_dir}/parts/p1/build/requirements.txt", + f"{new_dir}/parts/p1/install/bin/pip install {pip_addendum} --requirement={new_dir}/parts/p1/build/requirements.txt", f"{new_dir}/parts/p1/install/bin/pip install --no-deps .", f"{new_dir}/parts/p1/install/bin/pip check", *get_build_commands(new_dir),