Skip to content

Commit

Permalink
Fix resolution of path, url and VCS dependencies (#2398)
Browse files Browse the repository at this point in the history
* Fix resolution of path, url and VCS dependencies

* Fix editable installation of Poetry packages
  • Loading branch information
sdispater authored Jun 5, 2020
1 parent 7ee66c0 commit e4e8e3c
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 41 deletions.
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 <[email protected]>"]
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

0 comments on commit e4e8e3c

Please sign in to comment.