diff --git a/src/poetry/inspection/info.py b/src/poetry/inspection/info.py index 39557261dbf..fd999a50f05 100644 --- a/src/poetry/inspection/info.py +++ b/src/poetry/inspection/info.py @@ -1,5 +1,6 @@ from __future__ import annotations +import functools import glob import logging import os @@ -449,76 +450,6 @@ def _get_poetry_package(path: Path) -> ProjectPackage | None: return Factory().create_poetry(path).package return None - @classmethod - def _pep517_metadata(cls, path: Path) -> PackageInfo: - """ - Helper method to use PEP-517 library to build and read package metadata. - - :param path: Path to package source to build and read metadata for. - """ - info = None - try: - info = cls.from_setup_files(path) - if all([info.version, info.name, info.requires_dist]): - return info - except PackageInfoError: - pass - - with ephemeral_environment( - flags={"no-pip": False, "no-setuptools": False, "no-wheel": False} - ) as venv: - # TODO: cache PEP 517 build environment corresponding to each project venv - dest_dir = venv.path.parent / "dist" - dest_dir.mkdir() - - pep517_meta_build_script = PEP517_META_BUILD.format( - source=path.as_posix(), dest=dest_dir.as_posix() - ) - - try: - venv.run_pip( - "install", - "--disable-pip-version-check", - "--ignore-installed", - *PEP517_META_BUILD_DEPS, - ) - venv.run( - "python", - "-", - input_=pep517_meta_build_script, - ) - return cls.from_metadata(dest_dir) - except EnvCommandError as e: - # something went wrong while attempting pep517 metadata build - # fallback to egg_info if setup.py available - cls._log(f"PEP517 build failed: {e}", level="debug") - setup_py = path / "setup.py" - if not setup_py.exists(): - raise PackageInfoError( - path, - e, - "No fallback setup.py file was found to generate egg_info.", - ) - - cwd = Path.cwd() - os.chdir(path.as_posix()) - try: - venv.run("python", "setup.py", "egg_info") - return cls.from_metadata(path) - except EnvCommandError as fbe: - raise PackageInfoError( - path, "Fallback egg_info generation failed.", fbe - ) - finally: - os.chdir(cwd.as_posix()) - - if info: - cls._log(f"Falling back to parsed setup.py file for {path}", "debug") - return info - - # if we reach here, everything has failed and all hope is lost - raise PackageInfoError(path, "Exhausted all core metadata sources.") - @classmethod def from_directory(cls, path: Path, disable_build: bool = False) -> PackageInfo: """ @@ -542,7 +473,7 @@ def from_directory(cls, path: Path, disable_build: bool = False) -> PackageInfo: if disable_build: info = cls.from_setup_files(path) else: - info = cls._pep517_metadata(path) + info = get_pep517_metadata(path) except PackageInfoError: if not info: raise @@ -609,3 +540,74 @@ def from_path(cls, path: Path) -> PackageInfo: return cls.from_bdist(path=path) except PackageInfoError: return cls.from_sdist(path=path) + + +@functools.lru_cache(maxsize=None) +def get_pep517_metadata(path: Path) -> PackageInfo: + """ + Helper method to use PEP-517 library to build and read package metadata. + + :param path: Path to package source to build and read metadata for. + """ + info = None + try: + info = PackageInfo.from_setup_files(path) + if all([info.version, info.name, info.requires_dist]): + return info + except PackageInfoError: + pass + + with ephemeral_environment( + flags={"no-pip": False, "no-setuptools": False, "no-wheel": False} + ) as venv: + # TODO: cache PEP 517 build environment corresponding to each project venv + dest_dir = venv.path.parent / "dist" + dest_dir.mkdir() + + pep517_meta_build_script = PEP517_META_BUILD.format( + source=path.as_posix(), dest=dest_dir.as_posix() + ) + + try: + venv.run_pip( + "install", + "--disable-pip-version-check", + "--ignore-installed", + *PEP517_META_BUILD_DEPS, + ) + venv.run( + "python", + "-", + input_=pep517_meta_build_script, + ) + info = PackageInfo.from_metadata(dest_dir) + except EnvCommandError as e: + # something went wrong while attempting pep517 metadata build + # fallback to egg_info if setup.py available + logger.debug("PEP517 build failed: %s", e) + setup_py = path / "setup.py" + if not setup_py.exists(): + raise PackageInfoError( + path, + e, + "No fallback setup.py file was found to generate egg_info.", + ) + + cwd = Path.cwd() + os.chdir(path.as_posix()) + try: + venv.run("python", "setup.py", "egg_info") + info = PackageInfo.from_metadata(path) + except EnvCommandError as fbe: + raise PackageInfoError( + path, "Fallback egg_info generation failed.", fbe + ) + finally: + os.chdir(cwd.as_posix()) + + if info: + logger.debug("Falling back to parsed setup.py file for %s", path) + return info + + # if we reach here, everything has failed and all hope is lost + raise PackageInfoError(path, "Exhausted all core metadata sources.") diff --git a/tests/conftest.py b/tests/conftest.py index 5515651419a..6176b3e9ff2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -233,15 +233,14 @@ def download_mock(mocker: MockerFixture) -> None: @pytest.fixture(autouse=True) def pep517_metadata_mock(mocker: MockerFixture) -> None: - @classmethod - def _pep517_metadata(cls: PackageInfo, path: Path) -> PackageInfo: + def get_pep517_metadata(path: Path) -> PackageInfo: with suppress(PackageInfoError): return PackageInfo.from_setup_files(path) return PackageInfo(name="demo", version="0.1.2") mocker.patch( - "poetry.inspection.info.PackageInfo._pep517_metadata", - _pep517_metadata, + "poetry.inspection.info.get_pep517_metadata", + get_pep517_metadata, ) diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 3cb0df75074..ea1b552c907 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -144,7 +144,7 @@ def test_search_for_vcs_read_setup_raises_error_if_no_version( provider: Provider, mocker: MockerFixture ): mocker.patch( - "poetry.inspection.info.PackageInfo._pep517_metadata", + "poetry.inspection.info.get_pep517_metadata", return_value=PackageInfo(name="demo", version=None), )