Commit 09b61214 authored by Reinout van Rees's avatar Reinout van Rees

Merge pull request #282 from buildout/reinout-constraint-logging

Nicer version constraint logging + better requirement conflict info logging
parents cc0c0d4f caff88e3
...@@ -2,9 +2,9 @@ language: python ...@@ -2,9 +2,9 @@ language: python
env: env:
- PYTHON_VER=2.6 - PYTHON_VER=2.6
- PYTHON_VER=2.7 - PYTHON_VER=2.7
- PYTHON_VER=3.2
- PYTHON_VER=3.3 - PYTHON_VER=3.3
- PYTHON_VER=3.4 - PYTHON_VER=3.4
- PYTHON_VER=3.5
sudo: false sudo: false
cache: cache:
......
...@@ -4,7 +4,17 @@ Change History ...@@ -4,7 +4,17 @@ Change History
2.4.8 (unreleased) 2.4.8 (unreleased)
================== ==================
- Nothing changed yet. - Added more elaborate version and requirement information when there's a
version conflict. Previously, you could get a report of a version conflict
without information about which dependency requested the conflicing
requirement.
Now all this information is logged and displayed in case of an error.
[reinout]
- Dropped 3.2 support (at least in the automatic tests) as setuptools will
soon stop supporting it. Added python 3.5 to the automatic tests.
[reinout]
2.4.7 (2015-10-29) 2.4.7 (2015-10-29)
......
...@@ -19,6 +19,10 @@ ifeq ($(PYTHON_VER),3.4) ...@@ -19,6 +19,10 @@ ifeq ($(PYTHON_VER),3.4)
PYTHON_MINOR ?= 3.4.2 PYTHON_MINOR ?= 3.4.2
PYTHON_CONFIGURE_ARGS ?= --without-ensurepip PYTHON_CONFIGURE_ARGS ?= --without-ensurepip
endif endif
ifeq ($(PYTHON_VER),3.5)
PYTHON_MINOR ?= 3.5.0
PYTHON_CONFIGURE_ARGS ?= --without-ensurepip
endif
ifndef PYTHON_MINOR ifndef PYTHON_MINOR
$(error Please specify desired PYTHON_MINOR for Python $(PYTHON_VER)) $(error Please specify desired PYTHON_MINOR for Python $(PYTHON_VER))
......
...@@ -102,9 +102,9 @@ setup( ...@@ -102,9 +102,9 @@ setup(
'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Topic :: Software Development :: Build Tools', 'Topic :: Software Development :: Build Tools',
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
], ],
......
...@@ -222,10 +222,28 @@ class Installer: ...@@ -222,10 +222,28 @@ class Installer:
self._newest = newest self._newest = newest
self._env = pkg_resources.Environment(path) self._env = pkg_resources.Environment(path)
self._index = _get_index(index, links, self._allow_hosts) self._index = _get_index(index, links, self._allow_hosts)
self._requirements_and_constraints = []
if versions is not None: if versions is not None:
self._versions = normalize_versions(versions) self._versions = normalize_versions(versions)
def _version_conflict_information(self, name):
"""Return textual requirements/constraint information for debug purposes
We do a very simple textual search, as that filters out most
extraneous information witout missing anything.
"""
output = [
"Version and requirements information containing %s:" % name]
version_constraint = self._versions.get(name)
if version_constraint:
output.append(
"[versions] constraint on %s: %s" % (name, version_constraint))
output += [line for line in self._requirements_and_constraints
if name.lower() in line.lower()]
return '\n '.join(output)
def _satisfied(self, req, source=None): def _satisfied(self, req, source=None):
dists = [dist for dist in self._env[req.project_name] if dist in req] dists = [dist for dist in self._env[req.project_name] if dist in req]
if not dists: if not dists:
...@@ -608,14 +626,24 @@ class Installer: ...@@ -608,14 +626,24 @@ class Installer:
def _constrain(self, requirement): def _constrain(self, requirement):
"""Return requirement with optional [versions] constraint added."""
constraint = self._versions.get(requirement.project_name.lower()) constraint = self._versions.get(requirement.project_name.lower())
if constraint: if constraint:
requirement = _constrained_requirement(constraint, requirement) try:
requirement = _constrained_requirement(constraint,
requirement)
except IncompatibleConstraintError:
logger.info(self._version_conflict_information(
requirement.project_name.lower()))
raise
return requirement return requirement
def install(self, specs, working_set=None): def install(self, specs, working_set=None):
logger.debug('Installing %s.', repr(specs)[1:-1]) logger.debug('Installing %s.', repr(specs)[1:-1])
self._requirements_and_constraints.append(
"Base installation request: %s" % repr(specs)[1:-1])
for_buildout_run = bool(working_set) for_buildout_run = bool(working_set)
path = self._path path = self._path
...@@ -688,11 +716,17 @@ class Installer: ...@@ -688,11 +716,17 @@ class Installer:
self._maybe_add_setuptools(ws, dist) self._maybe_add_setuptools(ws, dist)
if dist not in req: if dist not in req:
# Oops, the "best" so far conflicts with a dependency. # Oops, the "best" so far conflicts with a dependency.
logger.info(self._version_conflict_information(req.key))
raise VersionConflict( raise VersionConflict(
pkg_resources.VersionConflict(dist, req), ws) pkg_resources.VersionConflict(dist, req), ws)
best[req.key] = dist best[req.key] = dist
requirements.extend(dist.requires(req.extras)[::-1]) extra_requirements = dist.requires(req.extras)[::-1]
for extra_requirement in extra_requirements:
self._requirements_and_constraints.append(
"Requirement of %s: %s" % (current_requirement, extra_requirement))
requirements.extend(extra_requirements)
processed[req] = True processed[req] = True
return ws return ws
...@@ -1505,7 +1539,9 @@ def _constrained_requirement(constraint, requirement): ...@@ -1505,7 +1539,9 @@ def _constrained_requirement(constraint, requirement):
assert constraint.startswith('==') assert constraint.startswith('==')
constraint = constraint[2:] constraint = constraint[2:]
if constraint not in requirement: if constraint not in requirement:
bad_constraint(constraint, requirement) msg = ("The requirement (%r) is not allowed by your [versions] "
"constraint (%s)" % (str(requirement), constraint))
raise IncompatibleConstraintError(msg)
# Sigh, copied from Requirement.__str__ # Sigh, copied from Requirement.__str__
extras = ','.join(requirement.extras) extras = ','.join(requirement.extras)
...@@ -1528,8 +1564,3 @@ class IncompatibleConstraintError(zc.buildout.UserError): ...@@ -1528,8 +1564,3 @@ class IncompatibleConstraintError(zc.buildout.UserError):
""" """
IncompatibleVersionError = IncompatibleConstraintError # Backward compatibility IncompatibleVersionError = IncompatibleConstraintError # Backward compatibility
def bad_constraint(constraint, requirement):
logger.error("The constraint, %s, is not consistent with the "
"requirement, %r.", constraint, str(requirement))
raise IncompatibleConstraintError("Bad constraint", constraint, requirement)
...@@ -242,13 +242,15 @@ we'll get an error: ...@@ -242,13 +242,15 @@ we'll get an error:
... versions = dict(demo='0.2', demoneeded='1.0')) ... versions = dict(demo='0.2', demoneeded='1.0'))
Traceback (most recent call last): Traceback (most recent call last):
... ...
IncompatibleConstraintError: Bad constraint 0.2 demo>0.2 IncompatibleConstraintError: The requirement ('demo>0.2') is not allowed by your [versions] constraint (0.2)
>>> print_(handler) >>> print_(handler)
zc.buildout.easy_install DEBUG zc.buildout.easy_install DEBUG
Installing 'demo >0.2'. Installing 'demo >0.2'.
zc.buildout.easy_install ERROR zc.buildout.easy_install INFO
The constraint, 0.2, is not consistent with the requirement, 'demo>0.2'. Version and requirements information containing demo:
[versions] constraint on demo: 0.2
Base installation request: 'demo >0.2'
>>> handler.clear() >>> handler.clear()
......
...@@ -306,6 +306,9 @@ Now, let's create a buildout that requires y and z: ...@@ -306,6 +306,9 @@ Now, let's create a buildout that requires y and z:
Installing eggs. Installing eggs.
Getting distribution for 'demoneeded==1.1'. Getting distribution for 'demoneeded==1.1'.
Got demoneeded 1.1. Got demoneeded 1.1.
Version and requirements information containing demoneeded:
Requirement of samplez: demoneeded==1.1
Requirement of sampley: demoneeded==1.0
While: While:
Installing eggs. Installing eggs.
Error: There is a version conflict. Error: There is a version conflict.
...@@ -355,6 +358,9 @@ If we use the verbose switch, we can see where requirements are coming from: ...@@ -355,6 +358,9 @@ If we use the verbose switch, we can see where requirements are coming from:
Getting required 'sampley' Getting required 'sampley'
required by sampleb 1. required by sampleb 1.
We have a develop egg: sampley 1 We have a develop egg: sampley 1
Version and requirements information containing demoneeded:
Requirement of samplez: demoneeded==1.1
Requirement of sampley: demoneeded==1.0
While: While:
Installing eggs. Installing eggs.
Error: There is a version conflict. Error: There is a version conflict.
......
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