Commit e8c7e9ea by finswimmer Committed by GitHub

feat: fallback to gather metadata via pep517 if reading as Poetry project raises…

feat: fallback to gather metadata via pep517 if reading as Poetry project raises RuntimeError (#5834)

* feat: fallback to gather metadata via pep517 if reading as Poetry project raises RuntimeError

* test: added tests for fallback to gather metadata via pep517
parent 8df4614d
......@@ -463,7 +463,9 @@ class PackageInfo:
# Note: we ignore any setup.py file at this step
# TODO: add support for handling non-poetry PEP-517 builds
if PyProjectTOML(path.joinpath("pyproject.toml")).is_poetry_project():
return Factory().create_poetry(path).package
with contextlib.suppress(RuntimeError):
return Factory().create_poetry(path).package
return None
@classmethod
......
......@@ -553,32 +553,37 @@ class Executor:
self._env.pip_version
< self._env.pip_version.__class__.from_parts(19, 0, 0)
)
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
builder: Builder
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
if package.develop:
return self.pip_install(req, upgrade=True, editable=True)
return self.pip_install(req, upgrade=True)
try:
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
except RuntimeError:
package_poetry = None
if package_poetry is not None:
builder: Builder
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
if package.develop:
return self.pip_install(req, upgrade=True, editable=True)
return self.pip_install(req, upgrade=True)
if package.develop:
return self.pip_install(req, upgrade=True, editable=True)
......
......@@ -231,39 +231,44 @@ class PipInstaller(BaseInstaller):
# so we need to check the version of pip to know
# if we can rely on the build system
legacy_pip = self._env.pip_version < Version.from_parts(19, 0, 0)
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
builder: Builder
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
if package.develop:
try:
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
except RuntimeError:
package_poetry = None
if package_poetry is not None:
builder: Builder
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
if package.develop:
return pip_install(
path=req,
environment=self._env,
upgrade=True,
editable=True,
)
return pip_install(
path=req,
environment=self._env,
upgrade=True,
editable=True,
path=req, environment=self._env, deps=False, upgrade=True
)
return pip_install(
path=req, environment=self._env, deps=False, upgrade=True
)
if package.develop:
return pip_install(
......
[tool.poetry]
name = "demo-poetry"
version = "0.1.0"
description = ""
authors = ["John Doe <john@example.com.com>"]
[tool.poetry.dependencies]
python = "^3.10"
pendulum = "*"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
......@@ -131,6 +131,24 @@ def test_info_from_poetry_directory():
demo_check_info(info)
def test_info_from_poetry_directory_fallback_on_poetry_create_error(
mocker: MockerFixture,
):
mock_create_poetry = mocker.patch(
"poetry.inspection.info.Factory.create_poetry", side_effect=RuntimeError
)
mock_get_poetry_package = mocker.spy(PackageInfo, "_get_poetry_package")
mock_get_pep517_metadata = mocker.patch(
"poetry.inspection.info.get_pep517_metadata"
)
PackageInfo.from_directory(FIXTURE_DIR_INSPECTIONS / "demo_poetry_package")
assert mock_create_poetry.call_count == 1
assert mock_get_poetry_package.call_count == 1
assert mock_get_pep517_metadata.call_count == 1
def test_info_from_requires_txt():
info = PackageInfo.from_metadata(
FIXTURE_DIR_INSPECTIONS / "demo_only_requires_txt.egg-info"
......
......@@ -704,3 +704,59 @@ def test_executor_should_be_initialized_with_correct_workers(
executor = Executor(tmp_venv, pool, config, io)
assert executor._max_workers == expected_workers
def test_executer_fallback_on_poetry_create_error(
mocker: MockerFixture,
config: Config,
pool: Pool,
io: BufferedIO,
tmp_dir: str,
mock_file_downloads: None,
env: MockEnv,
):
mock_pip_install = mocker.patch("poetry.installation.executor.pip_install")
mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder")
mock_editable_builder = mocker.patch(
"poetry.masonry.builders.editable.EditableBuilder"
)
mock_create_poetry = mocker.patch(
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
)
config.merge({"cache-dir": tmp_dir})
executor = Executor(env, pool, config, io)
directory_package = Package(
"simple-project",
"1.2.3",
source_type="directory",
source_url=Path(__file__)
.parent.parent.joinpath("fixtures/simple_project")
.resolve()
.as_posix(),
)
return_code = executor.execute(
[
Install(directory_package),
]
)
expected = f"""
Package operations: 1 install, 0 updates, 0 removals
• Installing simple-project (1.2.3 {directory_package.source_url})
"""
expected = set(expected.splitlines())
output = set(io.fetch_output().splitlines())
assert output == expected
assert return_code == 0
assert mock_create_poetry.call_count == 1
assert mock_sdist_builder.call_count == 0
assert mock_editable_builder.call_count == 0
assert mock_pip_install.call_count == 1
assert mock_pip_install.call_args[1].get("upgrade") is True
assert mock_pip_install.call_args[1].get("editable") is False
......@@ -266,3 +266,35 @@ def test_install_with_trusted_host(config: Config):
assert "--trusted-host" in cmd
cert_index = cmd.index("--trusted-host")
assert cmd[cert_index + 1] == "foo.bar"
def test_install_directory_fallback_on_poetry_create_error(
mocker: MockerFixture, tmp_venv: VirtualEnv, pool: Pool
):
mock_create_poetry = mocker.patch(
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
)
mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder")
mock_editable_builder = mocker.patch(
"poetry.masonry.builders.editable.EditableBuilder"
)
mock_pip_install = mocker.patch("poetry.installation.pip_installer.pip_install")
package = Package(
"demo",
"1.0.0",
source_type="directory",
source_url=str(
Path(__file__).parent.parent / "fixtures/inspection/demo_poetry_package"
),
)
installer = PipInstaller(tmp_venv, NullIO(), pool)
installer.install_directory(package)
assert mock_create_poetry.call_count == 1
assert mock_sdist_builder.call_count == 0
assert mock_editable_builder.call_count == 0
assert mock_pip_install.call_count == 1
assert mock_pip_install.call_args[1].get("deps") is False
assert mock_pip_install.call_args[1].get("upgrade") is True
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment