Commit c497a5d8 by Sébastien Eustace

Fix extras being selected even when not required.

parent 4bc442ff
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
- Fixed reading of some `setup.py` files. - Fixed reading of some `setup.py` files.
- Fixed a `KeyError` when getting information for packages which require reading setup files. - Fixed a `KeyError` when getting information for packages which require reading setup files.
- Fixed the building of wheels with C extensions and an `src` layout. - Fixed the building of wheels with C extensions and an `src` layout.
- Fixed extras being selected when resolving dependencies even when not required.
## [0.12.6] - 2018-11-05 ## [0.12.6] - 2018-11-05
......
import glob
import os
import pkginfo
from pkginfo.distribution import HEADER_ATTRS from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0 from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.io import NullIO
from poetry.utils._compat import PY35
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
from poetry.utils.env import Env
from poetry.utils.env import EnvCommandError
from poetry.utils.setup_reader import SetupReader
from .dependency import Dependency from .dependency import Dependency
...@@ -25,15 +15,13 @@ HEADER_ATTRS.update( ...@@ -25,15 +15,13 @@ HEADER_ATTRS.update(
class DirectoryDependency(Dependency): class DirectoryDependency(Dependency):
def __init__( def __init__(
self, self,
name,
path, # type: Path path, # type: Path
category="main", # type: str category="main", # type: str
optional=False, # type: bool optional=False, # type: bool
base=None, # type: Path base=None, # type: Path
develop=True, # type: bool develop=True, # type: bool
): ):
from . import dependency_from_pep_508
from .package import Package
self._path = path self._path = path
self._base = base self._base = base
self._full_path = path self._full_path = path
...@@ -49,7 +37,7 @@ class DirectoryDependency(Dependency): ...@@ -49,7 +37,7 @@ class DirectoryDependency(Dependency):
if self._full_path.is_file(): if self._full_path.is_file():
raise ValueError("{} is a file, expected a directory".format(self._path)) raise ValueError("{} is a file, expected a directory".format(self._path))
# Checking content to dertermine actions # Checking content to determine actions
setup = self._full_path / "setup.py" setup = self._full_path / "setup.py"
pyproject = TomlFile(self._full_path / "pyproject.toml") pyproject = TomlFile(self._full_path / "pyproject.toml")
if pyproject.exists(): if pyproject.exists():
...@@ -65,121 +53,8 @@ class DirectoryDependency(Dependency): ...@@ -65,121 +53,8 @@ class DirectoryDependency(Dependency):
) )
) )
if self._supports_poetry:
from poetry.poetry import Poetry
poetry = Poetry.create(self._full_path)
package = poetry.package
self._package = Package(package.pretty_name, package.version)
self._package.requires += package.requires
self._package.dev_requires += package.dev_requires
self._package.extras = package.extras
self._package.python_versions = package.python_versions
else:
# Execute egg_info
current_dir = os.getcwd()
os.chdir(str(self._full_path))
try:
cwd = base
venv = Env.get(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
result = SetupReader.read_from_directory(self._full_path)
if not result["name"]:
# The name could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package name for {}".format(path)
)
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package version for {}".format(path)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
if python_requires is None:
python_requires = "*"
package_summary = ""
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
if result["extras_require"]:
requires += "\n"
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires)
else:
os.chdir(current_dir)
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(self._full_path), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(self._full_path.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
package_summary = meta.summary
python_requires = meta.requires_python
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
finally:
os.chdir(current_dir)
package = Package(package_name, package_version)
package.description = package_summary
for req in reqs:
package.requires.append(dependency_from_pep_508(req))
if python_requires:
package.python_versions = python_requires
self._package = package
self._package.source_type = "directory"
self._package.source_url = self._path.as_posix()
super(DirectoryDependency, self).__init__( super(DirectoryDependency, self).__init__(
self._package.name, name, "*", category=category, optional=optional, allows_prereleases=True
self._package.version,
category=category,
optional=optional,
allows_prereleases=True,
) )
@property @property
...@@ -191,8 +66,8 @@ class DirectoryDependency(Dependency): ...@@ -191,8 +66,8 @@ class DirectoryDependency(Dependency):
return self._full_path.resolve() return self._full_path.resolve()
@property @property
def package(self): def base(self):
return self._package return self._base
@property @property
def develop(self): def develop(self):
......
import hashlib import hashlib
import io import io
import pkginfo
from pkginfo.distribution import HEADER_ATTRS from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0 from pkginfo.distribution import HEADER_ATTRS_2_0
...@@ -19,6 +17,7 @@ HEADER_ATTRS.update( ...@@ -19,6 +17,7 @@ HEADER_ATTRS.update(
class FileDependency(Dependency): class FileDependency(Dependency):
def __init__( def __init__(
self, self,
name,
path, # type: Path path, # type: Path
category="main", # type: str category="main", # type: str
optional=False, # type: bool optional=False, # type: bool
...@@ -37,18 +36,8 @@ class FileDependency(Dependency): ...@@ -37,18 +36,8 @@ class FileDependency(Dependency):
if self._full_path.is_dir(): if self._full_path.is_dir():
raise ValueError("{} is a directory, expected a file".format(self._path)) raise ValueError("{} is a directory, expected a file".format(self._path))
if self._path.suffix == ".whl":
self._meta = pkginfo.Wheel(str(self._full_path))
else:
# Assume sdist
self._meta = pkginfo.SDist(str(self._full_path))
super(FileDependency, self).__init__( super(FileDependency, self).__init__(
self._meta.name, name, "*", category=category, optional=optional, allows_prereleases=True
self._meta.version,
category=category,
optional=optional,
allows_prereleases=True,
) )
@property @property
...@@ -59,10 +48,6 @@ class FileDependency(Dependency): ...@@ -59,10 +48,6 @@ class FileDependency(Dependency):
def full_path(self): def full_path(self):
return self._full_path.resolve() return self._full_path.resolve()
@property
def metadata(self):
return self._meta
def is_file(self): def is_file(self):
return True return True
......
...@@ -261,7 +261,7 @@ class Package(object): ...@@ -261,7 +261,7 @@ class Package(object):
file_path = Path(constraint["file"]) file_path = Path(constraint["file"])
dependency = FileDependency( dependency = FileDependency(
file_path, category=category, base=self.root_dir name, file_path, category=category, base=self.root_dir
) )
elif "path" in constraint: elif "path" in constraint:
path = Path(constraint["path"]) path = Path(constraint["path"])
...@@ -273,10 +273,15 @@ class Package(object): ...@@ -273,10 +273,15 @@ class Package(object):
if is_file: if is_file:
dependency = FileDependency( dependency = FileDependency(
path, category=category, optional=optional, base=self.root_dir name,
path,
category=category,
optional=optional,
base=self.root_dir,
) )
else: else:
dependency = DirectoryDependency( dependency = DirectoryDependency(
name,
path, path,
category=category, category=category,
optional=optional, optional=optional,
......
...@@ -11,6 +11,8 @@ from contextlib import contextmanager ...@@ -11,6 +11,8 @@ from contextlib import contextmanager
from tempfile import mkdtemp from tempfile import mkdtemp
from typing import List from typing import List
from poetry.io import NullIO
from poetry.packages import Dependency from poetry.packages import Dependency
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
from poetry.packages import DirectoryDependency from poetry.packages import DirectoryDependency
...@@ -177,134 +179,16 @@ class Provider: ...@@ -177,134 +179,16 @@ class Provider:
if dependency.tag or dependency.rev: if dependency.tag or dependency.rev:
revision = dependency.reference revision = dependency.reference
pyproject = TomlFile(tmp_dir / "pyproject.toml") directory_dependency = DirectoryDependency(
pyproject_content = None dependency.name,
has_poetry = False tmp_dir,
if pyproject.exists(): category=dependency.category,
pyproject_content = pyproject.read() optional=dependency.is_optional(),
has_poetry = ( )
"tool" in pyproject_content for extra in dependency.extras:
and "poetry" in pyproject_content["tool"] directory_dependency.extras.append(extra)
)
if pyproject_content and has_poetry:
# If a pyproject.toml file exists
# We use it to get the information we need
info = pyproject_content["tool"]["poetry"]
name = info["name"]
version = info["version"]
package = Package(name, version, version)
package.source_type = dependency.vcs
package.source_url = dependency.source
package.source_reference = dependency.reference
for req_name, req_constraint in info["dependencies"].items():
if req_name == "python":
package.python_versions = req_constraint
continue
package.add_dependency(req_name, req_constraint)
else:
# We need to use setup.py here
# to figure the information we need
# We need to place ourselves in the proper
# folder for it to work
venv = Env.get(self._io)
current_dir = os.getcwd()
os.chdir(tmp_dir.as_posix())
try:
try:
venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
# Most likely an error with the egg_info command
self.debug(
"<warning>Error executing the egg_info command. Reading setup files.</warning>"
)
result = SetupReader.read_from_directory(tmp_dir)
if not result["name"]:
result["name"] = dependency.name
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the version for {}".format(
dependency.name
)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
if result["extras_require"]:
requires += "\n"
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires) package = self.search_for_directory(directory_dependency)[0]
else:
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(tmp_dir), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(tmp_dir.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
python_requires = meta.requires_python
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(package_name, package_version)
if python_requires:
package.python_versions = python_requires
for req in reqs:
dep = dependency_from_pep_508(req)
if dep.in_extras:
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
package.requires.append(dep)
except Exception:
raise
finally:
os.chdir(current_dir)
package.source_type = "git" package.source_type = "git"
package.source_url = dependency.source package.source_url = dependency.source
...@@ -314,50 +198,196 @@ class Provider: ...@@ -314,50 +198,196 @@ class Provider:
finally: finally:
shutil.rmtree(tmp_dir.as_posix()) shutil.rmtree(tmp_dir.as_posix())
if dependency.name != package.name: return [package]
def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
if dependency.path.suffix == ".whl":
meta = pkginfo.Wheel(str(dependency.full_path))
else:
# Assume sdist
meta = pkginfo.SDist(str(dependency.full_path))
if dependency.name != meta.name:
# For now, the dependency's name must match the actual package's name # For now, the dependency's name must match the actual package's name
raise RuntimeError( raise RuntimeError(
"The dependency name for {} does not match the actual package's name: {}".format( "The dependency name for {} does not match the actual package's name: {}".format(
dependency.name, package.name dependency.name, meta.name
) )
) )
if dependency.extras: package = Package(meta.name, meta.version)
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
return [package]
def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
package = Package(dependency.name, dependency.pretty_constraint)
package.source_type = "file" package.source_type = "file"
package.source_url = dependency.path.as_posix() package.source_url = dependency.path.as_posix()
package.description = dependency.metadata.summary package.description = meta.summary
for req in dependency.metadata.requires_dist: for req in meta.requires_dist:
package.requires.append(dependency_from_pep_508(req)) dep = dependency_from_pep_508(req)
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
if dependency.metadata.requires_python: if not dep.is_optional():
package.python_versions = dependency.metadata.requires_python package.requires.append(dep)
if dependency.metadata.platforms: if meta.requires_python:
package.platform = " || ".join(dependency.metadata.platforms) package.python_versions = meta.requires_python
package.hashes = [dependency.hash()] package.hashes = [dependency.hash()]
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
package.requires += package.extras[extra]
return [package] return [package]
def search_for_directory( def search_for_directory(
self, dependency self, dependency
): # type: (DirectoryDependency) -> List[Package] ): # type: (DirectoryDependency) -> List[Package]
package = dependency.package if dependency.supports_poetry():
if dependency.extras: from poetry.poetry import Poetry
for extra in dependency.extras:
if extra in package.extras: poetry = Poetry.create(dependency.full_path)
for dep in package.extras[extra]:
dep.activate() pkg = poetry.package
package = Package(pkg.name, pkg.version)
for dep in pkg.requires:
if not dep.is_optional():
package.requires.append(dep)
for extra, deps in pkg.extras.items():
if extra not in package.extras:
package.extras[extra] = []
for dep in deps:
package.extras[extra].append(dep)
package.python_versions = pkg.python_versions
else:
# Execute egg_info
current_dir = os.getcwd()
os.chdir(str(dependency.full_path))
try:
cwd = dependency.full_path
venv = Env.get(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
result = SetupReader.read_from_directory(dependency.full_path)
if not result["name"]:
# The name could not be determined
# We use the dependency name
result["name"] = dependency.name
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package version for {}".format(
dependency.path
)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
if python_requires is None:
python_requires = "*"
package_summary = ""
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
if result["extras_require"]:
requires += "\n"
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires)
else:
os.chdir(current_dir)
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(dependency.full_path), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(dependency.full_path.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
package_summary = meta.summary
python_requires = meta.requires_python
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
finally:
os.chdir(current_dir)
package = Package(package_name, package_version)
if dependency.name != package.name:
# For now, the dependency's name must match the actual package's name
raise RuntimeError(
"The dependency name for {} does not match the actual package's name: {}".format(
dependency.name, package.name
)
)
package.description = package_summary
for req in reqs:
dep = dependency_from_pep_508(req)
if dep.in_extras:
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
if not dep.is_optional():
package.requires.append(dep)
if python_requires:
package.python_versions = python_requires
package.source_type = "directory"
package.source_url = dependency.path.as_posix()
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
package.requires += package.extras[extra]
return [package] return [package]
...@@ -581,14 +611,15 @@ class Provider: ...@@ -581,14 +611,15 @@ class Provider:
) )
if package.dependency.is_directory() and dep.is_directory(): if package.dependency.is_directory() and dep.is_directory():
if dep.package.source_url.startswith(package.source_url): if dep.path.as_posix().startswith(package.source_url):
relative = ( relative = (Path(package.source_url) / dep.path).relative_to(
Path(package.source_url) / dep.package.source_url package.source_url
).relative_to(package.source_url) )
else: else:
relative = Path(package.source_url) / dep.package.source_url relative = Path(package.source_url) / dep.path
dep.package.source_url = relative.as_posix() # TODO: Improve the way we set the correct relative path for dependencies
dep._path = relative
package.requires = dependencies package.requires = dependencies
......
import sys
from cleo.testers import CommandTester from cleo.testers import CommandTester
from tests.helpers import get_dependency
from tests.helpers import get_package from tests.helpers import get_package
...@@ -78,7 +75,6 @@ Resolving dependencies... ...@@ -78,7 +75,6 @@ Resolving dependencies...
Resolution results: Resolution results:
- pendulum (2.0.3) - pendulum (2.0.3)
- cleo (0.6.5)
- demo (0.1.2) - demo (0.1.2)
""" """
......
...@@ -12,7 +12,7 @@ kwargs = dict( ...@@ -12,7 +12,7 @@ kwargs = dict(
url="https://github.com/demo/demo", url="https://github.com/demo/demo",
packages=["demo"], packages=["demo"],
install_requires=["pendulum>=1.4.4"], install_requires=["pendulum>=1.4.4"],
extras_require={"foo": ["cleo"]}, extras_require={"foo": ["cleo"], "bar": ["tomlkit"]},
) )
......
[[package]] [[package]]
description = ""
category = "dev"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "dev"
name = "pendulum"
optional = true
python-versions = "*"
version = "1.4.4"
[[package]]
category = "main" category = "main"
description = "" description = ""
name = "project-with-extras" name = "project-with-extras"
...@@ -36,7 +20,7 @@ python-versions = "*" ...@@ -36,7 +20,7 @@ python-versions = "*"
version = "1.2.3" version = "1.2.3"
[package.dependencies] [package.dependencies]
project-with-extras = "1.2.3" project-with-extras = "*"
[package.source] [package.source]
reference = "" reference = ""
...@@ -48,7 +32,5 @@ content-hash = "123456789" ...@@ -48,7 +32,5 @@ content-hash = "123456789"
python-versions = "*" python-versions = "*"
[metadata.hashes] [metadata.hashes]
cachy = []
project-with-extras = [] project-with-extras = []
project-with-transitive-directory-dependencies = [] project-with-transitive-directory-dependencies = []
pendulum = []
[[package]] [[package]]
description = "" description = ""
category = "main" category = "main"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "main"
name = "pendulum" name = "pendulum"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -35,6 +27,5 @@ content-hash = "123456789" ...@@ -35,6 +27,5 @@ content-hash = "123456789"
python-versions = "*" python-versions = "*"
[metadata.hashes] [metadata.hashes]
cachy = []
project-with-extras = [] project-with-extras = []
pendulum = [] pendulum = []
[[package]] [[package]]
name = "demo" name = "demo"
version = "0.1.0" version = "0.1.0"
description = "Description" description = ""
category = "main" category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
...@@ -12,7 +12,7 @@ reference = "" ...@@ -12,7 +12,7 @@ reference = ""
url = "tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl" url = "tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
[package.dependencies] [package.dependencies]
pendulum = ">=1.4.0.0,<2.0.0.0" pendulum = ">=1.4.4"
[[package]] [[package]]
name = "pendulum" name = "pendulum"
...@@ -27,5 +27,5 @@ python-versions = "*" ...@@ -27,5 +27,5 @@ python-versions = "*"
content-hash = "123456789" content-hash = "123456789"
[metadata.hashes] [metadata.hashes]
demo = ["373a7e4bb99653541f35e52738f1b8a8e889c12e5f8b93a88c757d948b0cbe89"] demo = ["70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a"]
pendulum = [] pendulum = []
...@@ -678,11 +678,12 @@ def test_run_installs_with_local_file(installer, locker, repo, package): ...@@ -678,11 +678,12 @@ def test_run_installs_with_local_file(installer, locker, repo, package):
def test_run_installs_with_local_poetry_directory_and_extras( def test_run_installs_with_local_poetry_directory_and_extras(
installer, locker, repo, package, tmpdir installer, locker, repo, package, tmpdir
): ):
file_path = Path("tests/fixtures/project_with_extras/") file_path = Path("tests/fixtures/project_with_extras")
package.add_dependency("demo", {"path": str(file_path), "extras": ["extras_a"]}) package.add_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run() installer.run()
...@@ -699,7 +700,9 @@ def test_run_installs_with_local_poetry_directory_transitive( ...@@ -699,7 +700,9 @@ def test_run_installs_with_local_poetry_directory_transitive(
file_path = Path( file_path = Path(
"tests/fixtures/directory/project_with_transitive_directory_dependencies/" "tests/fixtures/directory/project_with_transitive_directory_dependencies/"
) )
package.add_dependency("demo", {"path": str(file_path)}) package.add_dependency(
"project-with-transitive-directory-dependencies", {"path": str(file_path)}
)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
...@@ -717,7 +720,7 @@ def test_run_installs_with_local_setuptools_directory( ...@@ -717,7 +720,7 @@ def test_run_installs_with_local_setuptools_directory(
installer, locker, repo, package, tmpdir installer, locker, repo, package, tmpdir
): ):
file_path = Path("tests/fixtures/project_with_setup/") file_path = Path("tests/fixtures/project_with_setup/")
package.add_dependency("demo", {"path": str(file_path)}) package.add_dependency("my-package", {"path": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
......
import pytest import pytest
from poetry.packages.directory_dependency import DirectoryDependency from poetry.packages.directory_dependency import DirectoryDependency
from poetry.utils._compat import PY35
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv from poetry.utils.env import MockEnv as BaseMockEnv
...@@ -17,39 +16,6 @@ class MockEnv(BaseMockEnv): ...@@ -17,39 +16,6 @@ class MockEnv(BaseMockEnv):
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo" DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo"
def test_directory_dependency_egg_info(): def test_directory_dependency_must_exist():
dependency = DirectoryDependency(DIST_PATH / "demo") with pytest.raises(ValueError):
DirectoryDependency("demo", DIST_PATH / "invalid")
assert dependency.is_directory()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.2"
assert dependency.python_versions == "*"
package = dependency.package
assert package.name == "demo"
assert package.pretty_version == "0.1.2"
assert package.python_versions == "*"
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_directory_dependency_no_egg_info(mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(DIST_PATH / "demo")
assert dependency.is_directory()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.2"
assert dependency.python_versions == "*"
package = dependency.package
assert package.name == "demo"
assert package.pretty_version == "0.1.2"
assert package.python_versions == "*"
def test_directory_dependency_with_no_version_should_raise_an_error(mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
with pytest.raises(RuntimeError):
DirectoryDependency(DIST_PATH / "no-version")
...@@ -6,35 +6,11 @@ from poetry.utils._compat import Path ...@@ -6,35 +6,11 @@ from poetry.utils._compat import Path
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions" DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
def test_file_dependency_wheel():
dependency = FileDependency(DIST_PATH / "demo-0.1.0-py2.py3-none-any.whl")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_sdist():
dependency = FileDependency(DIST_PATH / "demo-0.1.0.tar.gz")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_wrong_path(): def test_file_dependency_wrong_path():
with pytest.raises(ValueError): with pytest.raises(ValueError):
FileDependency(DIST_PATH / "demo-0.2.0.tar.gz") FileDependency("demo", DIST_PATH / "demo-0.2.0.tar.gz")
def test_file_dependency_dir(): def test_file_dependency_dir():
with pytest.raises(ValueError): with pytest.raises(ValueError):
FileDependency(DIST_PATH) FileDependency("demo", DIST_PATH)
...@@ -2,11 +2,14 @@ import pytest ...@@ -2,11 +2,14 @@ import pytest
from poetry.io import NullIO from poetry.io import NullIO
from poetry.packages import ProjectPackage from poetry.packages import ProjectPackage
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.packages.file_dependency import FileDependency
from poetry.packages.vcs_dependency import VCSDependency from poetry.packages.vcs_dependency import VCSDependency
from poetry.puzzle.provider import Provider from poetry.puzzle.provider import Provider
from poetry.repositories.pool import Pool from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository from poetry.repositories.repository import Repository
from poetry.utils._compat import PY35 from poetry.utils._compat import PY35
from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv from poetry.utils.env import MockEnv as BaseMockEnv
...@@ -50,11 +53,29 @@ def test_search_for_vcs_setup_egg_info(provider): ...@@ -50,11 +53,29 @@ def test_search_for_vcs_setup_egg_info(provider):
assert package.name == "demo" assert package.name == "demo"
assert package.version.text == "0.1.2" assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_setup_egg_info_with_extras(provider):
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
dependency.extras.append("foo")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [ assert package.requires == [
get_dependency("pendulum", ">=1.4.4"), get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True), get_dependency("cleo", optional=True),
] ]
assert package.extras == {"foo": [get_dependency("cleo")]} assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") @pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
...@@ -67,11 +88,32 @@ def test_search_for_vcs_read_setup(provider, mocker): ...@@ -67,11 +88,32 @@ def test_search_for_vcs_read_setup(provider, mocker):
assert package.name == "demo" assert package.name == "demo"
assert package.version.text == "0.1.2" assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_vcs_read_setup_with_extras(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
dependency.extras.append("foo")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [ assert package.requires == [
get_dependency("pendulum", ">=1.4.4"), get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True), get_dependency("cleo", optional=True),
] ]
assert package.extras == {"foo": [get_dependency("cleo")]} assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker): def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker):
...@@ -81,3 +123,226 @@ def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker): ...@@ -81,3 +123,226 @@ def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker):
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
provider.search_for_vcs(dependency) provider.search_for_vcs(dependency)
def test_search_for_directory_setup_egg_info(provider):
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_setup_egg_info_with_extras(provider):
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
dependency.extras.append("foo")
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_directory_setup_read_setup(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_directory_setup_read_setup_with_extras(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
dependency.extras.append("foo")
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_poetry(provider):
dependency = DirectoryDependency(
"demo", Path(__file__).parent.parent / "fixtures" / "project_with_extras"
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
assert package.requires == []
assert package.extras == {
"extras_a": [get_dependency("pendulum", ">=1.4.4")],
"extras_b": [get_dependency("cachy", ">=0.2.0")],
}
def test_search_for_directory_poetry_with_extras(provider):
dependency = DirectoryDependency(
"demo", Path(__file__).parent.parent / "fixtures" / "project_with_extras"
)
dependency.extras.append("extras_a")
package = provider.search_for_directory(dependency)[0]
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"extras_a": [get_dependency("pendulum", ">=1.4.4")],
"extras_b": [get_dependency("cachy", ">=0.2.0")],
}
def test_search_for_file_sdist(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz",
)
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_sdist_with_extras(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz",
)
dependency.extras.append("foo")
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl",
)
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel_with_extras(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl",
)
dependency.extras.append("foo")
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
...@@ -10,6 +10,7 @@ from poetry.repositories.pool import Pool ...@@ -10,6 +10,7 @@ from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository from poetry.repositories.repository import Repository
from poetry.puzzle import Solver from poetry.puzzle import Solver
from poetry.puzzle.exceptions import SolverProblemError from poetry.puzzle.exceptions import SolverProblemError
from poetry.utils._compat import Path
from poetry.version.markers import parse_marker from poetry.version.markers import parse_marker
from tests.helpers import get_dependency from tests.helpers import get_dependency
...@@ -913,7 +914,6 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package): ...@@ -913,7 +914,6 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package):
ops, ops,
[ [
{"job": "install", "package": pendulum}, {"job": "install", "package": pendulum},
{"job": "install", "package": cleo},
{"job": "install", "package": get_package("demo", "0.1.2")}, {"job": "install", "package": get_package("demo", "0.1.2")},
], ],
) )
...@@ -1246,7 +1246,6 @@ def test_solver_git_dependencies_update(solver, repo, package, installed): ...@@ -1246,7 +1246,6 @@ def test_solver_git_dependencies_update(solver, repo, package, installed):
ops, ops,
[ [
{"job": "install", "package": pendulum}, {"job": "install", "package": pendulum},
{"job": "install", "package": cleo},
{ {
"job": "update", "job": "update",
"from": get_package("demo", "0.1.2"), "from": get_package("demo", "0.1.2"),
...@@ -1255,9 +1254,10 @@ def test_solver_git_dependencies_update(solver, repo, package, installed): ...@@ -1255,9 +1254,10 @@ def test_solver_git_dependencies_update(solver, repo, package, installed):
], ],
) )
op = ops[2] op = ops[1]
assert op.job_type == "update" assert op.job_type == "update"
assert op.package.source_type == "git"
assert op.package.source_reference.startswith("9cf87a2") assert op.package.source_reference.startswith("9cf87a2")
assert op.initial_package.source_reference == "123456" assert op.initial_package.source_reference == "123456"
...@@ -1282,7 +1282,6 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed ...@@ -1282,7 +1282,6 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed
ops, ops,
[ [
{"job": "install", "package": pendulum}, {"job": "install", "package": pendulum},
{"job": "install", "package": cleo},
{ {
"job": "install", "job": "install",
"package": get_package("demo", "0.1.2"), "package": get_package("demo", "0.1.2"),
...@@ -1290,3 +1289,202 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed ...@@ -1290,3 +1289,202 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed
}, },
], ],
) )
def test_solver_can_resolve_directory_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
)
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
)
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
)
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
)
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
)
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = str(
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
)
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
...@@ -64,25 +64,21 @@ def test_poetry(): ...@@ -64,25 +64,21 @@ def test_poetry():
assert demo.is_file() assert demo.is_file()
assert not demo.is_vcs() assert not demo.is_vcs()
assert demo.name == "demo" assert demo.name == "demo"
assert demo.pretty_constraint == "0.1.0" assert demo.pretty_constraint == "*"
demo = dependencies["my-package"] demo = dependencies["my-package"]
assert not demo.is_file() assert not demo.is_file()
assert demo.is_directory() assert demo.is_directory()
assert not demo.is_vcs() assert not demo.is_vcs()
assert demo.name == "my-package" assert demo.name == "my-package"
assert demo.pretty_constraint == "0.1.2" assert demo.pretty_constraint == "*"
assert demo.package.requires[0].name == "pendulum"
assert demo.package.requires[1].name == "cachy"
assert demo.package.requires[1].extras == ["msgpack"]
simple_project = dependencies["simple-project"] simple_project = dependencies["simple-project"]
assert not simple_project.is_file() assert not simple_project.is_file()
assert simple_project.is_directory() assert simple_project.is_directory()
assert not simple_project.is_vcs() assert not simple_project.is_vcs()
assert simple_project.name == "simple-project" assert simple_project.name == "simple-project"
assert simple_project.pretty_constraint == "1.2.3" assert simple_project.pretty_constraint == "*"
assert simple_project.package.requires == []
assert "db" in package.extras assert "db" in package.extras
......
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