Commit c10742fd by Sébastien Eustace

Merge branch 'master' into develop

parents 47a05c26 4829967a
......@@ -13,6 +13,31 @@
- Improved debugging of dependency resolution.
## [0.8.6] - 2018-04-30
### Fixed
- Fixed config files not being created.
## [0.8.5] - 2018-04-19
### Fixed
- Fixed a bug in dependency resolution which led to installation errors.
- Fixed a bug where malformed sdists would lead to dependency resolution failing.
## [0.8.4] - 2018-04-18
### Fixed
- Fixed a bug where dependencies constraints in lock were too strict.
- Fixed unicode error in `search` command for Python 2.7.
- Fixed error with git dependencies.
>>>>>>> master
## [0.8.3] - 2018-04-16
### Fixed
......@@ -259,7 +284,10 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.8.3...master
[Unreleased]: https://github.com/sdispater/poetry/compare/0.8.6...master
[0.8.6]: https://github.com/sdispater/poetry/releases/tag/0.8.6
[0.8.5]: https://github.com/sdispater/poetry/releases/tag/0.8.5
[0.8.4]: https://github.com/sdispater/poetry/releases/tag/0.8.4
[0.8.3]: https://github.com/sdispater/poetry/releases/tag/0.8.3
[0.8.2]: https://github.com/sdispater/poetry/releases/tag/0.8.2
[0.8.1]: https://github.com/sdispater/poetry/releases/tag/0.8.1
......
......@@ -56,7 +56,7 @@ If you want to install prerelease versions, you can use the `--preview` option.
poetry self:update --preview
```
And finally, if you want to install a spcific version you can pass it as an argument
And finally, if you want to install a specific version you can pass it as an argument
to `self:update`.
```bash
......
......@@ -119,6 +119,7 @@ def temporary_directory(*args, **kwargs):
class Installer:
CURRENT_PYTHON = sys.executable
METADATA_URL = 'https://pypi.org/pypi/poetry/json'
VERSION_REGEX = re.compile(
'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'
......@@ -197,7 +198,7 @@ class Installer:
dist = os.path.join(dir, 'dist')
print(' - Getting dependencies')
self.call(
'python', '-m', 'pip', 'install', 'poetry=={}'.format(version),
self.CURRENT_PYTHON, '-m', 'pip', 'install', 'poetry=={}'.format(version),
'--target', dist
)
......@@ -243,7 +244,7 @@ class Installer:
)
self.call(
'python', '-m', 'pip', 'install',
self.CURRENT_PYTHON, '-m', 'pip', 'install',
'--upgrade',
'--no-deps',
os.path.join(dir, 'poetry-{}-{}.whl'.format(version, tag))
......
__version__ = '0.8.3'
__version__ = '0.8.6'
......@@ -49,12 +49,12 @@ To remove a repository (repo is a short alias for repositories):
# Create config file if it does not exist
if not self._config.file.exists():
self._config.file.parent.mkdir(parents=True, exist_ok=True)
with self._config.file.open() as f:
with self._config.file.open('w') as f:
f.write(TEMPLATE)
if not self._auth_config.file.exists():
self._auth_config.file.parent.mkdir(parents=True, exist_ok=True)
with self._auth_config.file.open() as f:
with self._auth_config.file.open('w') as f:
f.write(AUTH_TEMPLATE)
def handle(self):
......
......@@ -9,8 +9,7 @@ class InstallCommand(VenvCommand):
{ --no-dev : Do not install dev dependencies. }
{ --dry-run : Outputs the operations but will not execute anything
(implicitly enables --verbose). }
{ --E|extras=* : Extra sets of dependencies to install
(multiple values allowed). }
{ --E|extras=* : Extra sets of dependencies to install. }
"""
help = """The <info>install</info> command reads the <comment>pyproject.toml</> file from
......
......@@ -322,8 +322,8 @@ class Installer:
' - Updating <info>{}</> (<comment>{}</> -> <comment>{}</>)'
.format(
target.pretty_name,
source.pretty_version,
target.pretty_version
source.full_pretty_version,
target.full_pretty_version
)
)
......
......@@ -156,6 +156,9 @@ class Provider(SpecificationProvider, UI):
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
......
......@@ -3,6 +3,7 @@ from typing import List
from poetry.mixology import Resolver
from poetry.mixology.dependency_graph import DependencyGraph
from poetry.mixology.exceptions import ResolverError
from poetry.packages.constraints.generic_constraint import GenericConstraint
from poetry.semver.version_parser import VersionParser
......@@ -43,37 +44,20 @@ class Solver:
# Setting info
for vertex in graph.vertices.values():
tags = self._get_tags_for_vertex(vertex, requested)
if 'main' in tags['category']:
vertex.payload.category = 'main'
else:
vertex.payload.category = 'dev'
category, optional, python, platform = self._get_tags_for_vertex(
vertex, requested
)
if not tags['optional']:
vertex.payload.optional = False
else:
vertex.payload.optional = True
vertex.payload.category = category
vertex.payload.optional = optional
# Finding the less restrictive requirements
# If requirements are empty, drop them
requirements = {}
parser = VersionParser()
for req_name, reqs in tags['requirements'].items():
for req in reqs:
if req_name == 'python':
if 'python' not in requirements:
requirements['python'] = req
continue
previous = parser.parse_constraints(requirements['python'])
current = parser.parse_constraints(req)
if python is not None and python != '*':
requirements['python'] = python
if current.matches(previous):
requirements['python'] = req
if req_name == 'platform':
if 'platform' not in requirements:
requirements['platform'] = req
continue
if platform is not None and platform != '*':
requirements['platform'] = platform
vertex.payload.requirements = requirements
......@@ -128,46 +112,109 @@ class Solver:
)
def _get_tags_for_vertex(self, vertex, requested):
tags = {
'category': [],
'optional': True,
'requirements': {
'python': [],
'platform': []
}
}
category = 'dev'
optional = True
python_version = None
platform = None
if not vertex.incoming_edges:
# Original dependency
for req in requested:
if req.name == vertex.name:
tags['category'].append(req.category)
if not req.is_optional():
tags['optional'] = False
if vertex.payload.name == req.name:
category = req.category
optional = req.is_optional()
if req.python_versions != '*':
tags['requirements']['python'].append(str(req.python_constraint))
python_version = str(req.python_constraint)
if req.platform != '*':
tags['requirements']['platform'].append(str(req.platform_constraint))
platform = str(req.platform_constraint)
break
else:
return category, optional, python_version, platform
parser = VersionParser()
python_versions = []
platforms = []
for edge in vertex.incoming_edges:
python_version = None
platform = None
for req in edge.origin.payload.requires:
if req.name == vertex.payload.name:
if req.python_versions != '*':
tags['requirements']['python'].append(req.python_versions)
python_version = req.python_versions
platform = req.platform
break
(top_category,
top_optional,
top_python_version,
top_platform) = self._get_tags_for_vertex(
edge.origin, requested
)
if req.platform != '*':
tags['requirements']['platform'].append(req.platform)
if top_category == 'main':
category = top_category
sub_tags = self._get_tags_for_vertex(edge.origin, requested)
optional = optional and top_optional
tags['category'] += sub_tags['category']
tags['optional'] = tags['optional'] and sub_tags['optional']
requirements = sub_tags['requirements']
tags['requirements']['python'] += requirements.get('python', [])
tags['requirements']['platform'] += requirements.get('platform', [])
# Take the most restrictive constraints
if top_python_version is not None:
if python_version is not None:
previous = parser.parse_constraints(python_version)
current = parser.parse_constraints(top_python_version)
if top_python_version != '*' and previous.matches(current):
python_versions.append(top_python_version)
else:
python_versions.append(python_version)
else:
python_versions.append(top_python_version)
elif python_version is not None:
python_versions.append(python_version)
if top_platform is not None:
if platform is not None:
previous = GenericConstraint.parse(platform)
current = GenericConstraint.parse(top_platform)
if top_platform != '*' and previous.matches(current):
platforms.append(top_platform)
else:
platforms.append(platform)
else:
platforms.append(top_platform)
elif platform is not None:
platforms.append(platform)
if not python_versions:
python_version = None
else:
# Find the least restrictive constraint
python_version = python_versions[0]
previous = parser.parse_constraints(python_version)
for constraint in python_versions[1:]:
current = parser.parse_constraints(constraint)
if python_version == '*':
continue
elif constraint == '*':
python_version = constraint
elif current.matches(previous):
python_version = constraint
if not platforms:
platform = None
else:
platform = platforms[0]
previous = GenericConstraint.parse(platform)
for constraint in platforms[1:]:
current = GenericConstraint.parse(constraint)
if platform == '*':
continue
elif constraint == '*':
platform = constraint
elif current.matches(previous):
platform = constraint
return tags
return category, optional, python_version, platform
......@@ -33,6 +33,7 @@ from poetry.semver.constraints import Constraint
from poetry.semver.constraints.base_constraint import BaseConstraint
from poetry.semver.version_parser import VersionParser
from poetry.utils._compat import Path
from poetry.utils._compat import to_str
from poetry.utils.helpers import temporary_directory
from poetry.version.markers import InvalidMarker
......@@ -221,7 +222,7 @@ class PyPiRepository(Repository):
for hit in hits:
result = Package(hit['name'], hit['version'], hit['version'])
result.description = hit['summary']
result.description = to_str(hit['summary'])
results.append(result)
return results
......@@ -358,7 +359,12 @@ class PyPiRepository(Repository):
filepath = os.path.join(temp_dir, filename)
self._download(url, filepath)
try:
meta = pkginfo.Wheel(filepath)
except ValueError:
# Unable to determine dependencies
# Assume none
return
if meta.requires_dist:
return meta.requires_dist
......@@ -371,10 +377,15 @@ class PyPiRepository(Repository):
filepath = Path(temp_dir) / filename
self._download(url, str(filepath))
try:
meta = pkginfo.SDist(str(filepath))
if meta.requires_dist:
return meta.requires_dist
except ValueError:
# Unable to determine dependencies
# We pass and go deeper
pass
# Still not dependencies found
# So, we unpack and introspect
......
......@@ -39,7 +39,7 @@ class Git:
return self._config
def clone(self, repository, dest): # type: (...) -> str
return self.run('clone', repository, dest)
return self.run('clone', repository, str(dest))
def checkout(self, rev, folder=None): # type: (...) -> str
args = []
......
......@@ -319,9 +319,6 @@ def test_run_with_optional_and_python_restricted_dependencies(installer, locker,
installer.run()
expected = fixture('with-optional-dependencies')
import json
print(json.dumps(locker.written_data, indent=2, sort_keys=True))
print(json.dumps(expected, indent=2, sort_keys=True))
assert locker.written_data == expected
installer = installer.installer
......
......@@ -502,3 +502,87 @@ def test_solver_does_not_return_prereleases_if_not_requested(solver, repo):
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_c},
])
def test_solver_sub_dependencies_with_requirements(solver, repo):
package_a = get_package('A', '1.0')
package_b = get_package('B', '1.0')
package_c = get_package('C', '1.0')
package_d = get_package('D', '1.0')
package_c.add_dependency('D', {'version': '^1.0', 'python': '<4.0'})
package_a.add_dependency('C')
package_b.add_dependency('D', '^1.0')
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
dependency_a = get_dependency('A')
dependency_b = get_dependency('B')
request = [
dependency_a,
dependency_b,
]
ops = solver.solve(request)
check_solver_result(ops, [
{'job': 'install', 'package': package_c},
{'job': 'install', 'package': package_d},
{'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
])
op = ops[1]
assert op.package.requirements == {}
def test_solver_sub_dependencies_with_requirements_complex(solver, repo):
package_a = get_package('A', '1.0')
package_b = get_package('B', '1.0')
package_c = get_package('C', '1.0')
package_d = get_package('D', '1.0')
package_e = get_package('E', '1.0')
package_f = get_package('F', '1.0')
package_a.add_dependency('B', '^1.0')
package_a.add_dependency('D', {'version': '^1.0', 'python': '<4.0'})
package_b.add_dependency('E', {'version': '^1.0', 'platform': 'win32'})
package_b.add_dependency('F')
package_c.add_dependency('F', '^1.0')
package_d.add_dependency('F')
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
repo.add_package(package_e)
repo.add_package(package_f)
dependency_a = get_dependency('A')
dependency_b = get_dependency('B')
dependency_c = get_dependency('C')
request = [
dependency_a,
dependency_b,
dependency_c,
]
ops = solver.solve(request)
check_solver_result(ops, [
{'job': 'install', 'package': package_d},
{'job': 'install', 'package': package_e},
{'job': 'install', 'package': package_f},
{'job': 'install', 'package': package_a},
{'job': 'install', 'package': package_b},
{'job': 'install', 'package': package_c},
])
op = ops[1]
assert op.package.requirements == {'platform': 'win32'}
op = ops[2]
assert op.package.requirements == {}
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