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

Fix resolution of path, url and VCS dependencies #2398

Merged
merged 2 commits into from
Jun 5, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions poetry/installation/pip_installer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
import tempfile

from io import open
from subprocess import CalledProcessError

from clikit.api.io import IO
Expand Down Expand Up @@ -181,9 +180,7 @@ def create_temporary_requirement(self, package):
return name

def install_directory(self, package):
from poetry.masonry.builder import SdistBuilder
from poetry.factory import Factory
from poetry.utils._compat import decode
from poetry.utils.env import NullEnv
from poetry.utils.toml_file import TomlFile

Expand All @@ -210,17 +207,20 @@ def install_directory(self, package):

setup = os.path.join(req, "setup.py")
has_setup = os.path.exists(setup)
if not has_setup and has_poetry and (package.develop or not has_build_system):
if has_poetry and (package.develop or not has_build_system):
# We actually need to rely on creating a temporary setup.py
# file since pip, as of this comment, does not support
# build-system for editable packages
# We also need it for non-PEP-517 packages
builder = SdistBuilder(
from poetry.masonry.builders.editable import EditableBuilder

builder = EditableBuilder(
Factory().create_poetry(pyproject.parent), NullEnv(), NullIO()
)

with open(setup, "w", encoding="utf-8") as f:
f.write(decode(builder.build_setup()))
builder.build()

return

if package.develop:
args.append("-e")
Expand Down
10 changes: 10 additions & 0 deletions poetry/mixology/version_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ def _get_min(dependency):
if dependency.name in self._locked:
return 1

# VCS, URL, File or Directory dependencies
# represent a single version
if (
dependency.is_vcs()
or dependency.is_url()
or dependency.is_file()
or dependency.is_directory()
):
return 1

try:
return len(self._provider.search_for(dependency))
except ValueError:
Expand Down
11 changes: 11 additions & 0 deletions poetry/packages/directory_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,14 @@ def supports_poetry(self):

def is_directory(self):
return True

def __str__(self):
if self.is_root:
return self._pretty_name

return "{} ({} {})".format(
self._pretty_name, self._pretty_constraint, self._path
)

def __hash__(self):
return hash((self._name, self._full_path))
15 changes: 15 additions & 0 deletions poetry/packages/file_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ def __init__(
name, "*", category=category, optional=optional, allows_prereleases=True
)

@property
def base(self):
return self._base

@property
def path(self):
return self._path
Expand All @@ -59,3 +63,14 @@ def hash(self):
h.update(content)

return h.hexdigest()

def __str__(self):
if self.is_root:
return self._pretty_name

return "{} ({} {})".format(
self._pretty_name, self._pretty_constraint, self._path
)

def __hash__(self):
return hash((self._name, self._full_path))
1 change: 1 addition & 0 deletions poetry/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ def with_python_versions(self, python_versions):

def clone(self): # type: () -> Package
clone = self.__class__(self.pretty_name, self.version)
clone.description = self.description
clone.category = self.category
clone.optional = self.optional
clone.python_versions = self.python_versions
Expand Down
6 changes: 6 additions & 0 deletions poetry/packages/url_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ def base_pep_508_name(self): # type: () -> str

def is_url(self): # type: () -> bool
return True

def __str__(self):
return "{} ({} url)".format(self._pretty_name, self._pretty_constraint)

def __hash__(self):
return hash((self._name, self._url))
8 changes: 8 additions & 0 deletions poetry/packages/vcs_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,11 @@ def is_vcs(self): # type: () -> bool

def accepts_prereleases(self): # type: () -> bool
return True

def __str__(self):
return "{} ({} {})".format(
self._pretty_name, self._pretty_constraint, self._vcs
)

def __hash__(self):
return hash((self._name, self._vcs, self._branch, self._tag, self._rev))
73 changes: 62 additions & 11 deletions poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(self, package, pool, io): # type: (Package, Pool, Any) -> None
self._search_for = {}
self._is_debugging = self._io.is_debug() or self._io.is_very_verbose()
self._in_progress = False
self._deferred_cache = {}

@property
def pool(self): # type: () -> Pool
Expand Down Expand Up @@ -164,6 +165,9 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package]
Basically, we clone the repository in a temporary directory
and get the information we need by checking out the specified reference.
"""
if dependency in self._deferred_cache:
return [self._deferred_cache[dependency]]

package = self.get_package_from_vcs(
dependency.vcs,
dependency.source,
Expand All @@ -178,6 +182,11 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package]

package.requires += package.extras[extra]

dependency._constraint = package.version
dependency._pretty_constraint = package.version.text

self._deferred_cache[dependency] = package

return [package]

@classmethod
Expand Down Expand Up @@ -214,7 +223,17 @@ def get_package_from_vcs(
return package

def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
package = self.get_package_from_file(dependency.full_path)
if dependency in self._deferred_cache:
dependency, _package = self._deferred_cache[dependency]

package = _package.clone()
else:
package = self.get_package_from_file(dependency.full_path)

dependency._constraint = package.version
dependency._pretty_constraint = package.version.text

self._deferred_cache[dependency] = (dependency, package)

if dependency.name != package.name:
# For now, the dependency's name must match the actual package's name
Expand All @@ -224,6 +243,9 @@ def search_for_file(self, dependency): # type: (FileDependency) -> List[Package
)
)

if dependency.base is not None:
package.root_dir = dependency.base

package.source_url = dependency.path.as_posix()
package.files = [
{"file": dependency.path.name, "hash": "sha256:" + dependency.hash()}
Expand Down Expand Up @@ -270,15 +292,25 @@ def get_package_from_file(cls, file_path): # type: (Path) -> Package
def search_for_directory(
self, dependency
): # type: (DirectoryDependency) -> List[Package]
package = self.get_package_from_directory(
dependency.full_path, name=dependency.name
)
if dependency in self._deferred_cache:
dependency, _package = self._deferred_cache[dependency]

package = _package.clone()
else:
package = self.get_package_from_directory(
dependency.full_path, name=dependency.name
)

dependency._constraint = package.version
dependency._pretty_constraint = package.version.text

self._deferred_cache[dependency] = (dependency, package)

package.source_url = dependency.path.as_posix()
package.develop = dependency.develop

if dependency.base is not None:
package.root_dir = dependency.base.as_posix()
package.root_dir = dependency.base

for extra in dependency.extras:
if extra in package.extras:
Expand Down Expand Up @@ -434,6 +466,9 @@ def get_package_from_directory(
return package

def search_for_url(self, dependency): # type: (URLDependency) -> List[Package]
if dependency in self._deferred_cache:
return [self._deferred_cache[dependency]]

package = self.get_package_from_url(dependency.url)

if dependency.name != package.name:
Expand All @@ -451,6 +486,11 @@ def search_for_url(self, dependency): # type: (URLDependency) -> List[Package]

package.requires += package.extras[extra]

dependency._constraint = package.version
dependency._pretty_constraint = package.version.text

self._deferred_cache[dependency] = package

return [package]

@classmethod
Expand Down Expand Up @@ -551,6 +591,17 @@ def complete_package(
else:
requires = package.requires

# Retrieving constraints for deferred dependencies
for r in requires:
if r.is_directory():
self.search_for_directory(r)
elif r.is_file():
self.search_for_file(r)
elif r.is_vcs():
self.search_for_vcs(r)
elif r.is_url():
self.search_for_url(r)

dependencies = [
r
for r in requires
Expand Down Expand Up @@ -696,15 +747,15 @@ def complete_package(
if (package.dependency.is_directory() or package.dependency.is_file()) and (
dep.is_directory() or dep.is_file()
):
if dep.path.as_posix().startswith(package.source_url):
relative = (Path(package.source_url) / dep.path).relative_to(
package.source_url
relative_path = Path(
os.path.relpath(
dep.full_path.as_posix(), package.root_dir.as_posix()
)
else:
relative = Path(package.source_url) / dep.path
)

# TODO: Improve the way we set the correct relative path for dependencies
dep._path = relative
dep._path = relative_path

clean_dependencies.append(dep)

package.requires = clean_dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ license = "MIT"
[tool.poetry.dependencies]
python = "*"
project-with-extras = {path = "../../project_with_extras/"}
project-with-transitive-file-dependencies = {path = "../project_with_transitive_file_dependencies/"}

[tool.poetry.dev-dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[tool.poetry]
name = "inner-directory-project"
version = "1.2.4"
description = "This is a description"
authors = ["Your Name <you@example.com>"]
license = "MIT"

[tool.poetry.dependencies]
python = "*"

[tool.poetry.dev-dependencies]
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ license = "MIT"
[tool.poetry.dependencies]
python = "*"
demo = {path = "../../distributions/demo-0.1.0-py2.py3-none-any.whl"}
inner-directory-project = {path = "./inner-directory-project"}

[tool.poetry.dev-dependencies]
Loading