Commit 30a848f2 by Sébastien Eustace

Merge branch 'master' into develop

parents e6261dc9 9d87b562
# Change Log # Change Log
## [Unreleased] ## [0.11.5] - 2018-09-04
### Fixed ### Fixed
- Fixed a recursion error with circular dependencies. - Fixed a recursion error with circular dependencies.
- Fixed the `config` command setting incorrect values for paths.
- Fixed an `OSError` on Python >= 3.5 for `git` dependencies with recursive symlinks.
- Fixed the possible deletion of system paths by `cache:clear`.
- Fixed a performance issue when parsing the lock file by upgrading `tomlkit`.
## [0.11.4] - 2018-07-30 ## [0.11.4] - 2018-07-30
...@@ -463,7 +467,8 @@ Initial release ...@@ -463,7 +467,8 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.11.4...master [Unreleased]: https://github.com/sdispater/poetry/compare/0.11.5...master
[0.11.5]: https://github.com/sdispater/poetry/releases/tag/0.11.5
[0.11.4]: https://github.com/sdispater/poetry/releases/tag/0.11.4 [0.11.4]: https://github.com/sdispater/poetry/releases/tag/0.11.4
[0.11.3]: https://github.com/sdispater/poetry/releases/tag/0.11.3 [0.11.3]: https://github.com/sdispater/poetry/releases/tag/0.11.3
[0.11.2]: https://github.com/sdispater/poetry/releases/tag/0.11.2 [0.11.2]: https://github.com/sdispater/poetry/releases/tag/0.11.2
......
...@@ -7,7 +7,7 @@ theme: ...@@ -7,7 +7,7 @@ theme:
extra: extra:
version: 2.0 version: 2.0
pages: nav:
- Introduction: index.md - Introduction: index.md
- Basic Usage: basic-usage.md - Basic Usage: basic-usage.md
- Libraries: libraries.md - Libraries: libraries.md
......
__version__ = "0.11.4" __version__ = "0.11.5"
...@@ -15,16 +15,26 @@ class CacheClearCommand(Command): ...@@ -15,16 +15,26 @@ class CacheClearCommand(Command):
def handle(self): def handle(self):
from cachy import CacheManager from cachy import CacheManager
from poetry.locations import CACHE_DIR from poetry.locations import CACHE_DIR
from poetry.utils._compat import Path
cache = self.argument("cache") cache = self.argument("cache")
parts = cache.split(":") parts = cache.split(":")
cache_dir = os.path.join(CACHE_DIR, "cache", "repositories", parts[0]) root = parts[0]
base_cache = Path(CACHE_DIR) / "cache" / "repositories"
cache_dir = base_cache / root
try:
cache_dir.relative_to(base_cache)
except ValueError:
raise ValueError("{} is not a valid repository cache".format(root))
cache = CacheManager( cache = CacheManager(
{ {
"default": parts[0], "default": parts[0],
"serializer": "json", "serializer": "json",
"stores": {parts[0]: {"driver": "file", "path": cache_dir}}, "stores": {parts[0]: {"driver": "file", "path": str(cache_dir)}},
} }
) )
...@@ -41,7 +51,7 @@ class CacheClearCommand(Command): ...@@ -41,7 +51,7 @@ class CacheClearCommand(Command):
# Calculate number of entries # Calculate number of entries
entries_count = 0 entries_count = 0
for path, dirs, files in os.walk(cache_dir): for path, dirs, files in os.walk(str(cache_dir)):
entries_count += len(files) entries_count += len(files)
delete = self.confirm( delete = self.confirm(
......
...@@ -64,7 +64,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -64,7 +64,7 @@ To remove a repository (repo is a short alias for repositories):
), ),
"settings.virtualenvs.path": ( "settings.virtualenvs.path": (
str, str,
lambda val: str(Path(val).resolve()), lambda val: str(Path(val)),
str(Path(CACHE_DIR) / "virtualenvs"), str(Path(CACHE_DIR) / "virtualenvs"),
), ),
} }
......
...@@ -90,7 +90,7 @@ def dependency_from_pep_508(name): ...@@ -90,7 +90,7 @@ def dependency_from_pep_508(name):
for or_ in markers["extra"]: for or_ in markers["extra"]:
for _, extra in or_: for _, extra in or_:
dep.extras.append(extra) dep.in_extras.append(extra)
if "python_version" in markers: if "python_version" in markers:
ors = [] ors = []
......
import glob
import logging import logging
import os import os
import pkginfo import pkginfo
...@@ -25,6 +26,7 @@ from poetry.mixology.term import Term ...@@ -25,6 +26,7 @@ from poetry.mixology.term import Term
from poetry.repositories import Pool from poetry.repositories import Pool
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.helpers import parse_requires
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
...@@ -193,7 +195,21 @@ class Provider: ...@@ -193,7 +195,21 @@ class Provider:
try: try:
venv.run("python", "setup.py", "egg_info") venv.run("python", "setup.py", "egg_info")
egg_info = next(tmp_dir.glob("**/*.egg-info")) # 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)) meta = pkginfo.UnpackedSDist(str(egg_info))
...@@ -209,7 +225,15 @@ class Provider: ...@@ -209,7 +225,15 @@ class Provider:
package = Package(meta.name, meta.version) package = Package(meta.name, meta.version)
for req in reqs: for req in reqs:
package.requires.append(dependency_from_pep_508(req)) 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: except Exception:
raise raise
finally: finally:
...@@ -231,6 +255,12 @@ class Provider: ...@@ -231,6 +255,12 @@ class Provider:
) )
) )
if dependency.extras:
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
return [package] return [package]
def search_for_file(self, dependency): # type: (FileDependency) -> List[Package] def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
...@@ -314,10 +344,10 @@ class Provider: ...@@ -314,10 +344,10 @@ class Provider:
] ]
def complete_package(self, package): # type: (str, Version) -> Package def complete_package(self, package): # type: (str, Version) -> Package
if package.is_root() or package.source_type in {"git", "file"}: if package.is_root():
return package return package
if package.source_type != "directory": if package.source_type not in {"directory", "file", "git"}:
package = self._pool.package( package = self._pool.package(
package.name, package.version.text, extras=package.requires_extras package.name, package.version.text, extras=package.requires_extras
) )
......
...@@ -155,8 +155,8 @@ class PyPiRepository(Repository): ...@@ -155,8 +155,8 @@ class PyPiRepository(Repository):
) )
continue continue
if dependency.extras: if dependency.in_extras:
for extra in dependency.extras: for extra in dependency.in_extras:
if extra not in package.extras: if extra not in package.extras:
package.extras[extra] = [] package.extras[extra] = []
......
...@@ -17,6 +17,7 @@ except NameError: # Python 3 ...@@ -17,6 +17,7 @@ except NameError: # Python 3
PY2 = sys.version_info[0] == 2 PY2 = sys.version_info[0] == 2
PY35 = sys.version_info >= (3, 5)
PY36 = sys.version_info >= (3, 6) PY36 = sys.version_info >= (3, 6)
......
[tool.poetry] [tool.poetry]
name = "poetry" name = "poetry"
version = "0.11.4" version = "0.11.5"
description = "Python dependency management and packaging made easy." description = "Python dependency management and packaging made easy."
authors = [ authors = [
"Sébastien Eustace <sebastien@eustace.io>" "Sébastien Eustace <sebastien@eustace.io>"
...@@ -34,7 +34,7 @@ cachecontrol = { version = "^0.12.4", extras = ["filecache"] } ...@@ -34,7 +34,7 @@ cachecontrol = { version = "^0.12.4", extras = ["filecache"] }
pkginfo = "^1.4" pkginfo = "^1.4"
html5lib = "^1.0" html5lib = "^1.0"
shellingham = "^1.1" shellingham = "^1.1"
tomlkit = "^0.4.0" tomlkit = "^0.4.4"
# The typing module is not in the stdlib in Python 2.7 and 3.4 # The typing module is not in the stdlib in Python 2.7 and 3.4
typing = { version = "^3.6", python = "~2.7 || ~3.4" } typing = { version = "^3.6", python = "~2.7 || ~3.4" }
...@@ -47,7 +47,7 @@ virtualenv = { version = "^16.0", python = "~2.7" } ...@@ -47,7 +47,7 @@ virtualenv = { version = "^16.0", python = "~2.7" }
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^3.4" pytest = "^3.4"
pytest-cov = "^2.5" pytest-cov = "^2.5"
mkdocs = "^0.17.3" mkdocs = "^1.0"
pymdown-extensions = "^4.9" pymdown-extensions = "^4.9"
pygments = "^2.2" pygments = "^2.2"
pytest-mock = "^1.9" pytest-mock = "^1.9"
......
...@@ -13,6 +13,7 @@ kwargs = dict( ...@@ -13,6 +13,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"]},
) )
......
...@@ -39,7 +39,7 @@ def test_dependency_from_pep_508_with_extras(): ...@@ -39,7 +39,7 @@ def test_dependency_from_pep_508_with_extras():
assert dep.name == "requests" assert dep.name == "requests"
assert str(dep.constraint) == "2.18.0" assert str(dep.constraint) == "2.18.0"
assert dep.extras == ["foo", "bar"] assert dep.in_extras == ["foo", "bar"]
def test_dependency_from_pep_508_with_python_version(): def test_dependency_from_pep_508_with_python_version():
...@@ -84,7 +84,7 @@ def test_dependency_from_pep_508_complex(): ...@@ -84,7 +84,7 @@ def test_dependency_from_pep_508_complex():
assert dep.name == "requests" assert dep.name == "requests"
assert str(dep.constraint) == "2.18.0" assert str(dep.constraint) == "2.18.0"
assert dep.extras == ["foo"] assert dep.in_extras == ["foo"]
assert dep.python_versions == ">=2.7 !=3.2.*" assert dep.python_versions == ">=2.7 !=3.2.*"
assert dep.platform == "win32 || darwin" assert dep.platform == "win32 || darwin"
......
...@@ -12,6 +12,7 @@ from poetry.puzzle.exceptions import SolverProblemError ...@@ -12,6 +12,7 @@ from poetry.puzzle.exceptions import SolverProblemError
from tests.helpers import get_dependency from tests.helpers import get_dependency
from tests.helpers import get_package from tests.helpers import get_package
from tests.repositories.test_pypi_repository import MockRepository
@pytest.fixture() @pytest.fixture()
...@@ -921,3 +922,44 @@ def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( ...@@ -921,3 +922,44 @@ def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency(
{"job": "install", "package": package_a}, {"job": "install", "package": package_a},
], ],
) )
def test_solver_can_resolve_git_dependencies(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)
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
def test_solver_can_resolve_git_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)
package.add_dependency(
"demo", {"git": "https://github.com/demo/demo.git", "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")},
],
)
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