Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.1] feat: fallback to gather metadata via pep517 if reading as Poetry project raises RuntimeError #6206

Merged
merged 1 commit into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion poetry/inspection/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,12 @@ def _get_poetry_package(path): # type: (Path) -> Optional[ProjectPackage]
# 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
try:
return Factory().create_poetry(path).package
except RuntimeError:
return None

return None

@classmethod
def _pep517_metadata(cls, path): # type (Path) -> PackageInfo
Expand Down
47 changes: 26 additions & 21 deletions poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,34 +513,39 @@ def _install_directory(self, operation):
legacy_pip = self._env.pip_version < self._env.pip_version.__class__(
19, 0, 0
)
package_poetry = Factory().create_poetry(pyproject.file.path.parent)

if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
try:
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
except RuntimeError:
package_poetry = None

# 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()
if package_poetry is not None:
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder

return 0
elif legacy_pip or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# 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)
# 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:
args.append("-e")
with builder.setup_py():
if package.develop:
args.append("-e")

args.append(req)
args.append(req)

return self.run_pip(*args)
return self.run_pip(*args)

if package.develop:
args.append("-e")
Expand Down
47 changes: 26 additions & 21 deletions poetry/installation/pip_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,34 +202,39 @@ def install_directory(self, package):
legacy_pip = self._env.pip_version < self._env.pip_version.__class__(
19, 0, 0
)
package_poetry = Factory().create_poetry(pyproject.file.path.parent)

if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
try:
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
except RuntimeError:
package_poetry = None

if package_poetry is not None:
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()
# 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
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)
# 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:
args.append("-e")
with builder.setup_py():
if package.develop:
args.append("-e")

args.append(req)
args.append(req)

return self.run(*args)
return self.run(*args)

if package.develop:
args.append("-e")
Expand Down
15 changes: 15 additions & 0 deletions tests/fixtures/inspection/demo_poetry_package/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[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"
16 changes: 16 additions & 0 deletions tests/inspection/test_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ def test_info_from_poetry_directory():
demo_check_info(info)


def test_info_from_poetry_directory_fallback_on_poetry_create_error(mocker):
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.PackageInfo._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"
Expand Down
50 changes: 50 additions & 0 deletions tests/installation/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,53 @@ def test_executor_should_use_cached_link_and_hash(
)

assert archive == link_cached


def test_executer_fallback_on_poetry_create_error(
mocker, config, pool, io, tmp_dir, mock_file_downloads,
):
import poetry.installation.executor

mock_pip_install = mocker.patch.object(
poetry.installation.executor.Executor, "run_pip"
)
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})

env = MockEnv(path=Path(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 = """
Package operations: 1 install, 0 updates, 0 removals
• Installing simple-project (1.2.3 {source_url})
""".format(
source_url=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
32 changes: 32 additions & 0 deletions tests/installation/test_pip_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,35 @@ def copy_only(source, dest):
# any command in the virtual environment should trigger the error message
output = tmp_venv.run("python", "-m", "site")
assert "Error processing line 1 of {}".format(pth_file_candidate) not in output


def test_install_directory_fallback_on_poetry_create_error(mocker, tmp_venv, pool):
import poetry.installation.pip_installer

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.object(
poetry.installation.pip_installer.PipInstaller, "run"
)

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