Commit fee81fd1 by Randy Döring

refactor: split off functions to get direct origin dependencies from provider (#7693)

parent 3248fe17
from __future__ import annotations
import functools
import os
import tempfile
import urllib.parse
from pathlib import Path
from typing import TYPE_CHECKING
from poetry.inspection.info import PackageInfo
from poetry.inspection.info import PackageInfoError
from poetry.utils.helpers import download_file
from poetry.utils.helpers import get_file_hash
from poetry.vcs.git import Git
if TYPE_CHECKING:
from poetry.core.packages.package import Package
@functools.lru_cache(maxsize=None)
def _get_package_from_git(
url: str,
branch: str | None = None,
tag: str | None = None,
rev: str | None = None,
subdirectory: str | None = None,
source_root: Path | None = None,
) -> Package:
source = Git.clone(
url=url,
source_root=source_root,
branch=branch,
tag=tag,
revision=rev,
clean=False,
)
revision = Git.get_revision(source)
path = Path(source.path)
if subdirectory:
path = path.joinpath(subdirectory)
package = DirectOrigin.get_package_from_directory(path)
package._source_type = "git"
package._source_url = url
package._source_reference = rev or tag or branch or "HEAD"
package._source_resolved_reference = revision
package._source_subdirectory = subdirectory
return package
class DirectOrigin:
@classmethod
def get_package_from_file(cls, file_path: Path) -> Package:
try:
package = PackageInfo.from_path(path=file_path).to_package(
root_dir=file_path
)
except PackageInfoError:
raise RuntimeError(
f"Unable to determine package info from path: {file_path}"
)
return package
@classmethod
def get_package_from_directory(cls, directory: Path) -> Package:
return PackageInfo.from_directory(path=directory).to_package(root_dir=directory)
@classmethod
def get_package_from_url(cls, url: str) -> Package:
file_name = os.path.basename(urllib.parse.urlparse(url).path)
with tempfile.TemporaryDirectory() as temp_dir:
dest = Path(temp_dir) / file_name
download_file(url, dest)
package = cls.get_package_from_file(dest)
package.files = [
{"file": file_name, "hash": "sha256:" + get_file_hash(dest)}
]
package._source_type = "url"
package._source_url = url
return package
@staticmethod
def get_package_from_vcs(
vcs: str,
url: str,
branch: str | None = None,
tag: str | None = None,
rev: str | None = None,
subdirectory: str | None = None,
source_root: Path | None = None,
) -> Package:
if vcs != "git":
raise ValueError(f"Unsupported VCS dependency {vcs}")
return _get_package_from_git(
url=url,
branch=branch,
tag=tag,
rev=rev,
subdirectory=subdirectory,
source_root=source_root,
)
from __future__ import annotations from __future__ import annotations
import functools
import logging import logging
import os
import re import re
import tempfile
import time import time
import urllib.parse
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import cast from typing import cast
...@@ -22,19 +17,16 @@ from poetry.core.version.markers import AnyMarker ...@@ -22,19 +17,16 @@ from poetry.core.version.markers import AnyMarker
from poetry.core.version.markers import EmptyMarker from poetry.core.version.markers import EmptyMarker
from poetry.core.version.markers import MarkerUnion from poetry.core.version.markers import MarkerUnion
from poetry.inspection.info import PackageInfo
from poetry.inspection.info import PackageInfoError
from poetry.mixology.incompatibility import Incompatibility from poetry.mixology.incompatibility import Incompatibility
from poetry.mixology.incompatibility_cause import DependencyCause from poetry.mixology.incompatibility_cause import DependencyCause
from poetry.mixology.incompatibility_cause import PythonCause from poetry.mixology.incompatibility_cause import PythonCause
from poetry.mixology.term import Term from poetry.mixology.term import Term
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
from poetry.packages.direct_origin import DirectOrigin
from poetry.packages.package_collection import PackageCollection from poetry.packages.package_collection import PackageCollection
from poetry.puzzle.exceptions import OverrideNeeded from poetry.puzzle.exceptions import OverrideNeeded
from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.exceptions import PackageNotFound
from poetry.utils.helpers import download_file
from poetry.utils.helpers import get_file_hash from poetry.utils.helpers import get_file_hash
from poetry.vcs.git import Git
if TYPE_CHECKING: if TYPE_CHECKING:
...@@ -42,6 +34,7 @@ if TYPE_CHECKING: ...@@ -42,6 +34,7 @@ if TYPE_CHECKING:
from collections.abc import Collection from collections.abc import Collection
from collections.abc import Iterable from collections.abc import Iterable
from collections.abc import Iterator from collections.abc import Iterator
from pathlib import Path
from cleo.io.io import IO from cleo.io.io import IO
from packaging.utils import NormalizedName from packaging.utils import NormalizedName
...@@ -100,39 +93,6 @@ class Indicator(ProgressIndicator): ...@@ -100,39 +93,6 @@ class Indicator(ProgressIndicator):
return f"{elapsed:.1f}s" return f"{elapsed:.1f}s"
@functools.lru_cache(maxsize=None)
def _get_package_from_git(
url: str,
branch: str | None = None,
tag: str | None = None,
rev: str | None = None,
subdirectory: str | None = None,
source_root: Path | None = None,
) -> Package:
source = Git.clone(
url=url,
source_root=source_root,
branch=branch,
tag=tag,
revision=rev,
clean=False,
)
revision = Git.get_revision(source)
path = Path(source.path)
if subdirectory:
path = path.joinpath(subdirectory)
package = Provider.get_package_from_directory(path)
package._source_type = "git"
package._source_url = url
package._source_reference = rev or tag or branch or "HEAD"
package._source_resolved_reference = revision
package._source_subdirectory = subdirectory
return package
class Provider: class Provider:
UNSAFE_PACKAGES: set[str] = set() UNSAFE_PACKAGES: set[str] = set()
...@@ -351,7 +311,7 @@ class Provider: ...@@ -351,7 +311,7 @@ class Provider:
Basically, we clone the repository in a temporary directory Basically, we clone the repository in a temporary directory
and get the information we need by checking out the specified reference. and get the information we need by checking out the specified reference.
""" """
package = self.get_package_from_vcs( package = DirectOrigin.get_package_from_vcs(
dependency.vcs, dependency.vcs,
dependency.source, dependency.source,
branch=dependency.branch, branch=dependency.branch,
...@@ -368,31 +328,9 @@ class Provider: ...@@ -368,31 +328,9 @@ class Provider:
return package return package
@staticmethod
def get_package_from_vcs(
vcs: str,
url: str,
branch: str | None = None,
tag: str | None = None,
rev: str | None = None,
subdirectory: str | None = None,
source_root: Path | None = None,
) -> Package:
if vcs != "git":
raise ValueError(f"Unsupported VCS dependency {vcs}")
return _get_package_from_git(
url=url,
branch=branch,
tag=tag,
rev=rev,
subdirectory=subdirectory,
source_root=source_root,
)
def _search_for_file(self, dependency: FileDependency) -> Package: def _search_for_file(self, dependency: FileDependency) -> Package:
dependency.validate(raise_error=True) dependency.validate(raise_error=True)
package = self.get_package_from_file(dependency.full_path) package = DirectOrigin.get_package_from_file(dependency.full_path)
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -408,22 +346,9 @@ class Provider: ...@@ -408,22 +346,9 @@ class Provider:
return package return package
@classmethod
def get_package_from_file(cls, file_path: Path) -> Package:
try:
package = PackageInfo.from_path(path=file_path).to_package(
root_dir=file_path
)
except PackageInfoError:
raise RuntimeError(
f"Unable to determine package info from path: {file_path}"
)
return package
def _search_for_directory(self, dependency: DirectoryDependency) -> Package: def _search_for_directory(self, dependency: DirectoryDependency) -> Package:
dependency.validate(raise_error=True) dependency.validate(raise_error=True)
package = self.get_package_from_directory(dependency.full_path) package = DirectOrigin.get_package_from_directory(dependency.full_path)
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -434,12 +359,8 @@ class Provider: ...@@ -434,12 +359,8 @@ class Provider:
return package return package
@classmethod
def get_package_from_directory(cls, directory: Path) -> Package:
return PackageInfo.from_directory(path=directory).to_package(root_dir=directory)
def _search_for_url(self, dependency: URLDependency) -> Package: def _search_for_url(self, dependency: URLDependency) -> Package:
package = self.get_package_from_url(dependency.url) package = DirectOrigin.get_package_from_url(dependency.url)
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -453,23 +374,6 @@ class Provider: ...@@ -453,23 +374,6 @@ class Provider:
return package return package
@classmethod
def get_package_from_url(cls, url: str) -> Package:
file_name = os.path.basename(urllib.parse.urlparse(url).path)
with tempfile.TemporaryDirectory() as temp_dir:
dest = Path(temp_dir) / file_name
download_file(url, dest)
package = cls.get_package_from_file(dest)
package.files = [
{"file": file_name, "hash": "sha256:" + get_file_hash(dest)}
]
package._source_type = "url"
package._source_url = url
return package
def _get_dependencies_with_overrides( def _get_dependencies_with_overrides(
self, dependencies: list[Dependency], package: DependencyPackage self, dependencies: list[Dependency], package: DependencyPackage
) -> list[Dependency]: ) -> list[Dependency]:
......
...@@ -16,7 +16,7 @@ from typing import cast ...@@ -16,7 +16,7 @@ from typing import cast
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from tomlkit.items import InlineTable from tomlkit.items import InlineTable
from poetry.puzzle.provider import Provider from poetry.packages.direct_origin import DirectOrigin
if TYPE_CHECKING: if TYPE_CHECKING:
...@@ -120,7 +120,7 @@ class RequirementsParser: ...@@ -120,7 +120,7 @@ class RequirementsParser:
pair["subdirectory"] = parsed.subdirectory pair["subdirectory"] = parsed.subdirectory
source_root = self._env.path.joinpath("src") if self._env else None source_root = self._env.path.joinpath("src") if self._env else None
package = Provider.get_package_from_vcs( package = DirectOrigin.get_package_from_vcs(
"git", "git",
url=url.url, url=url.url,
rev=pair.get("rev"), rev=pair.get("rev"),
...@@ -139,7 +139,7 @@ class RequirementsParser: ...@@ -139,7 +139,7 @@ class RequirementsParser:
return self._parse_git_url(requirement) return self._parse_git_url(requirement)
if url_parsed.scheme in ["http", "https"]: if url_parsed.scheme in ["http", "https"]:
package = Provider.get_package_from_url(requirement) package = DirectOrigin.get_package_from_url(requirement)
assert package.source_url is not None assert package.source_url is not None
return {"name": package.name, "url": package.source_url} return {"name": package.name, "url": package.source_url}
...@@ -158,9 +158,9 @@ class RequirementsParser: ...@@ -158,9 +158,9 @@ class RequirementsParser:
path = self._cwd.joinpath(requirement) path = self._cwd.joinpath(requirement)
if path.is_file(): if path.is_file():
package = Provider.get_package_from_file(path.resolve()) package = DirectOrigin.get_package_from_file(path.resolve())
else: else:
package = Provider.get_package_from_directory(path.resolve()) package = DirectOrigin.get_package_from_directory(path.resolve())
return { return {
"name": package.name, "name": package.name,
......
...@@ -239,7 +239,7 @@ def mock_user_config_dir(mocker: MockerFixture, config_dir: Path) -> None: ...@@ -239,7 +239,7 @@ def mock_user_config_dir(mocker: MockerFixture, config_dir: Path) -> None:
def download_mock(mocker: MockerFixture) -> None: def download_mock(mocker: MockerFixture) -> None:
# Patch download to not download anything but to just copy from fixtures # Patch download to not download anything but to just copy from fixtures
mocker.patch("poetry.utils.helpers.download_file", new=mock_download) mocker.patch("poetry.utils.helpers.download_file", new=mock_download)
mocker.patch("poetry.puzzle.provider.download_file", new=mock_download) mocker.patch("poetry.packages.direct_origin.download_file", new=mock_download)
mocker.patch("poetry.repositories.http_repository.download_file", new=mock_download) mocker.patch("poetry.repositories.http_repository.download_file", new=mock_download)
......
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