Skip to content

Commit dd93dc9

Browse files
committed
feat: fallback to gather metadata via pep517 if reading as Poetry project raises RuntimeError
1 parent d2abf92 commit dd93dc9

File tree

7 files changed

+171
-43
lines changed

7 files changed

+171
-43
lines changed

poetry/inspection/info.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,12 @@ def _get_poetry_package(path): # type: (Path) -> Optional[ProjectPackage]
437437
# Note: we ignore any setup.py file at this step
438438
# TODO: add support for handling non-poetry PEP-517 builds
439439
if PyProjectTOML(path.joinpath("pyproject.toml")).is_poetry_project():
440-
return Factory().create_poetry(path).package
440+
try:
441+
return Factory().create_poetry(path).package
442+
except RuntimeError:
443+
return None
444+
445+
return None
441446

442447
@classmethod
443448
def _pep517_metadata(cls, path): # type (Path) -> PackageInfo

poetry/installation/executor.py

+26-21
Original file line numberDiff line numberDiff line change
@@ -513,34 +513,39 @@ def _install_directory(self, operation):
513513
legacy_pip = self._env.pip_version < self._env.pip_version.__class__(
514514
19, 0, 0
515515
)
516-
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
517516

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

521-
# This is a Poetry package in editable mode
522-
# we can use the EditableBuilder without going through pip
523-
# to install it, unless it has a build script.
524-
builder = EditableBuilder(package_poetry, self._env, NullIO())
525-
builder.build()
522+
if package_poetry is not None:
523+
if package.develop and not package_poetry.package.build_script:
524+
from poetry.masonry.builders.editable import EditableBuilder
526525

527-
return 0
528-
elif legacy_pip or package_poetry.package.build_script:
529-
from poetry.core.masonry.builders.sdist import SdistBuilder
526+
# This is a Poetry package in editable mode
527+
# we can use the EditableBuilder without going through pip
528+
# to install it, unless it has a build script.
529+
builder = EditableBuilder(package_poetry, self._env, NullIO())
530+
builder.build()
531+
532+
return 0
533+
elif legacy_pip or package_poetry.package.build_script:
534+
from poetry.core.masonry.builders.sdist import SdistBuilder
530535

531-
# We need to rely on creating a temporary setup.py
532-
# file since the version of pip does not support
533-
# build-systems
534-
# We also need it for non-PEP-517 packages
535-
builder = SdistBuilder(package_poetry)
536+
# We need to rely on creating a temporary setup.py
537+
# file since the version of pip does not support
538+
# build-systems
539+
# We also need it for non-PEP-517 packages
540+
builder = SdistBuilder(package_poetry)
536541

537-
with builder.setup_py():
538-
if package.develop:
539-
args.append("-e")
542+
with builder.setup_py():
543+
if package.develop:
544+
args.append("-e")
540545

541-
args.append(req)
546+
args.append(req)
542547

543-
return self.run_pip(*args)
548+
return self.run_pip(*args)
544549

545550
if package.develop:
546551
args.append("-e")

poetry/installation/pip_installer.py

+26-21
Original file line numberDiff line numberDiff line change
@@ -202,34 +202,39 @@ def install_directory(self, package):
202202
legacy_pip = self._env.pip_version < self._env.pip_version.__class__(
203203
19, 0, 0
204204
)
205-
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
206205

207-
if package.develop and not package_poetry.package.build_script:
208-
from poetry.masonry.builders.editable import EditableBuilder
206+
try:
207+
package_poetry = Factory().create_poetry(pyproject.file.path.parent)
208+
except RuntimeError:
209+
package_poetry = None
210+
211+
if package_poetry is not None:
212+
if package.develop and not package_poetry.package.build_script:
213+
from poetry.masonry.builders.editable import EditableBuilder
209214

210-
# This is a Poetry package in editable mode
211-
# we can use the EditableBuilder without going through pip
212-
# to install it, unless it has a build script.
213-
builder = EditableBuilder(package_poetry, self._env, NullIO())
214-
builder.build()
215+
# This is a Poetry package in editable mode
216+
# we can use the EditableBuilder without going through pip
217+
# to install it, unless it has a build script.
218+
builder = EditableBuilder(package_poetry, self._env, NullIO())
219+
builder.build()
215220

216-
return 0
217-
elif legacy_pip or package_poetry.package.build_script:
218-
from poetry.core.masonry.builders.sdist import SdistBuilder
221+
return 0
222+
elif legacy_pip or package_poetry.package.build_script:
223+
from poetry.core.masonry.builders.sdist import SdistBuilder
219224

220-
# We need to rely on creating a temporary setup.py
221-
# file since the version of pip does not support
222-
# build-systems
223-
# We also need it for non-PEP-517 packages
224-
builder = SdistBuilder(package_poetry)
225+
# We need to rely on creating a temporary setup.py
226+
# file since the version of pip does not support
227+
# build-systems
228+
# We also need it for non-PEP-517 packages
229+
builder = SdistBuilder(package_poetry)
225230

226-
with builder.setup_py():
227-
if package.develop:
228-
args.append("-e")
231+
with builder.setup_py():
232+
if package.develop:
233+
args.append("-e")
229234

230-
args.append(req)
235+
args.append(req)
231236

232-
return self.run(*args)
237+
return self.run(*args)
233238

234239
if package.develop:
235240
args.append("-e")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tool.poetry]
2+
name = "demo-poetry"
3+
version = "0.1.0"
4+
description = ""
5+
authors = ["John Doe <[email protected]>"]
6+
7+
[tool.poetry.dependencies]
8+
python = "^3.10"
9+
pendulum = "*"
10+
11+
[tool.poetry.dev-dependencies]
12+
13+
[build-system]
14+
requires = ["poetry-core>=1.0.0"]
15+
build-backend = "poetry.core.masonry.api"

tests/inspection/test_info.py

+16
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ def test_info_from_poetry_directory():
127127
demo_check_info(info)
128128

129129

130+
def test_info_from_poetry_directory_fallback_on_poetry_create_error(mocker):
131+
mock_create_poetry = mocker.patch(
132+
"poetry.inspection.info.Factory.create_poetry", side_effect=RuntimeError
133+
)
134+
mock_get_poetry_package = mocker.spy(PackageInfo, "_get_poetry_package")
135+
mock_get_pep517_metadata = mocker.patch(
136+
"poetry.inspection.info.PackageInfo._pep517_metadata"
137+
)
138+
139+
PackageInfo.from_directory(FIXTURE_DIR_INSPECTIONS / "demo_poetry_package")
140+
141+
assert mock_create_poetry.call_count == 1
142+
assert mock_get_poetry_package.call_count == 1
143+
assert mock_get_pep517_metadata.call_count == 1
144+
145+
130146
def test_info_from_requires_txt():
131147
info = PackageInfo.from_metadata(
132148
FIXTURE_DIR_INSPECTIONS / "demo_only_requires_txt.egg-info"

tests/installation/test_executor.py

+50
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,53 @@ def test_executor_should_use_cached_link_and_hash(
358358
)
359359

360360
assert archive == link_cached
361+
362+
363+
def test_executer_fallback_on_poetry_create_error(
364+
mocker, config, pool, io, tmp_dir, mock_file_downloads,
365+
):
366+
import poetry.installation.executor
367+
368+
mock_pip_install = mocker.patch.object(
369+
poetry.installation.executor.Executor, "run_pip"
370+
)
371+
mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder")
372+
mock_editable_builder = mocker.patch(
373+
"poetry.masonry.builders.editable.EditableBuilder"
374+
)
375+
mock_create_poetry = mocker.patch(
376+
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
377+
)
378+
379+
config.merge({"cache-dir": tmp_dir})
380+
381+
env = MockEnv(path=Path(tmp_dir))
382+
executor = Executor(env, pool, config, io)
383+
384+
directory_package = Package(
385+
"simple-project",
386+
"1.2.3",
387+
source_type="directory",
388+
source_url=Path(__file__)
389+
.parent.parent.joinpath("fixtures/simple_project")
390+
.resolve()
391+
.as_posix(),
392+
)
393+
394+
return_code = executor.execute([Install(directory_package)])
395+
396+
expected = """
397+
Package operations: 1 install, 0 updates, 0 removals
398+
• Installing simple-project (1.2.3 {source_url})
399+
""".format(
400+
source_url=directory_package.source_url
401+
)
402+
403+
expected = set(expected.splitlines())
404+
output = set(io.fetch_output().splitlines())
405+
assert output == expected
406+
assert return_code == 0
407+
assert mock_create_poetry.call_count == 1
408+
assert mock_sdist_builder.call_count == 0
409+
assert mock_editable_builder.call_count == 0
410+
assert mock_pip_install.call_count == 1

tests/installation/test_pip_installer.py

+32
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,35 @@ def copy_only(source, dest):
216216
# any command in the virtual environment should trigger the error message
217217
output = tmp_venv.run("python", "-m", "site")
218218
assert "Error processing line 1 of {}".format(pth_file_candidate) not in output
219+
220+
221+
def test_install_directory_fallback_on_poetry_create_error(mocker, tmp_venv, pool):
222+
import poetry.installation.pip_installer
223+
224+
mock_create_poetry = mocker.patch(
225+
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
226+
)
227+
mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder")
228+
mock_editable_builder = mocker.patch(
229+
"poetry.masonry.builders.editable.EditableBuilder"
230+
)
231+
mock_pip_install = mocker.patch.object(
232+
poetry.installation.pip_installer.PipInstaller, "run"
233+
)
234+
235+
package = Package(
236+
"demo",
237+
"1.0.0",
238+
source_type="directory",
239+
source_url=str(
240+
Path(__file__).parent.parent / "fixtures/inspection/demo_poetry_package"
241+
),
242+
)
243+
244+
installer = PipInstaller(tmp_venv, NullIO(), pool)
245+
installer.install_directory(package)
246+
247+
assert mock_create_poetry.call_count == 1
248+
assert mock_sdist_builder.call_count == 0
249+
assert mock_editable_builder.call_count == 0
250+
assert mock_pip_install.call_count == 1

0 commit comments

Comments
 (0)