Commit f618b8ff authored by Reinout van Rees's avatar Reinout van Rees

Merged master

parents 4e5a3d53 813a4590
Change History
**************
2.2.6 (unreleased)
==================
Unreleased
==========
- Undo breakage on Windows machines where ``sys.prefix`` can also be a
``site-packages`` directory: don't remove it from ``sys.path``. See
https://github.com/buildout/buildout/issues/217
- Remove assumption that ``pkg_resources`` is a module (untrue since
release of `setuptools 8.3``). See
https://github.com/buildout/buildout/issues/227
- Fix for #212. For certain kinds of conflict errors you'd get an UnpackError
when rendering the error message. Instead of a nicely formatted version
conflict message.
[reinout]
2.3.1 (2014-12-16)
==================
- Fixed: Buildout merged single-version requirements with
version-range requirements in a way that caused it to think there
wasn't a single-version requirement. IOW, buildout throught that
versions were being picked when they weren't.
- Suppress spurios (and possibly non-spurious) version-parsing warnings.
2.3.0 (2014-12-14)
==================
- Buildout is now compatible with (and requires) setuptools 8.
2.2.5 (2014-11-04)
==================
......
......@@ -78,12 +78,12 @@ locally in your project.
The bootstrap script for buildout version 2 is at:
http://downloads.buildout.org/2/bootstrap.py
https://bootstrap.pypa.io/bootstrap-buildout.py
So, for example, to install buildout 2 in a project, you might::
wget http://downloads.buildout.org/2/bootstrap.py
python bootstrap.py
wget https://bootstrap.pypa.io/bootstrap-buildout.py
python bootstrap-buildout.py
Then to build your project, you can just run::
......@@ -91,8 +91,6 @@ Then to build your project, you can just run::
from the project directory.
The bootstrap script is often checked into version control.
buildout 2 is somewhat backward-incompatible with version 1. Most
projects will probably work fine with either. If you need to keep
using version 1, however, specify a version requirement when you use
......
......@@ -59,6 +59,8 @@ parser.add_option("-f", "--find-links",
parser.add_option("--allow-site-packages",
action="store_true", default=False,
help=("Let bootstrap.py use existing site packages"))
parser.add_option("--setuptools-version",
help="use a specific setuptools version")
options, args = parser.parse_args()
......@@ -86,9 +88,17 @@ if not options.allow_site_packages:
# We can't remove these reliably
if hasattr(site, 'getsitepackages'):
for sitepackage_path in site.getsitepackages():
# Strip all site-packages directories from sys.path that
# are not sys.prefix; this is because on Windows
# sys.prefix is a site-package directory.
if sitepackage_path != sys.prefix:
sys.path[:] = [x for x in sys.path if sitepackage_path not in x]
setup_args = dict(to_dir=tmpeggs, download_delay=0)
if options.setuptools_version is not None:
setup_args['version'] = options.setuptools_version
ez['use_setuptools'](**setup_args)
import setuptools
import pkg_resources
......@@ -128,10 +138,15 @@ if version is None and not options.accept_buildout_test_releases:
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
try:
return not parsed_version.is_prerelease
except AttributeError:
# Older setuptools
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
index = setuptools.package_index.PackageIndex(
search_path=[setuptools_path])
if find_links:
......
......@@ -47,18 +47,18 @@ except ImportError:
from urllib2 import urlopen
# XXX use a more permanent ez_setup.py URL when available.
exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py'
).read(), ez)
exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
ez['use_setuptools'](to_dir='eggs', download_delay=0)
import pkg_resources
import pkg_resources, setuptools
setuptools_path = os.path.dirname(os.path.dirname(setuptools.__file__))
######################################################################
# Install buildout
if subprocess.call(
[sys.executable] +
['setup.py', '-q', 'develop', '-m', '-x', '-d', 'develop-eggs'],
env=dict(os.environ, PYTHONPATH=os.path.dirname(pkg_resources.__file__))):
env=dict(os.environ, PYTHONPATH=setuptools_path)):
raise RuntimeError("buildout build failed.")
pkg_resources.working_set.add_entry('src')
......
......@@ -12,7 +12,7 @@
#
##############################################################################
name = "zc.buildout"
version = "2.2.5"
version = "2.3.1"
import os
from setuptools import setup
......@@ -88,7 +88,7 @@ setup(
package_dir = {'': 'src'},
namespace_packages = ['zc'],
install_requires = [
'setuptools>=3.3',
'setuptools>=8.0',
],
include_package_data = True,
entry_points = entry_points,
......
......@@ -89,3 +89,41 @@ Let's make sure the generated `buildout` script uses it::
'/sample/eggs/setuptools-...egg',
'/sample/eggs/zc.buildout-2.0.0...egg',
]...
Now trying the `--setuptools-version` option, that let you define a version for
`setuptools`.
Now let's try with `8.0`, which happens to exist::
>>> print_('X'); print_(system(
... zc.buildout.easy_install._safe_arg(sys.executable)+' '+
... 'bootstrap.py --setuptools-version 8.0')); print_('X')
... # doctest: +ELLIPSIS
X...Generated script '/sample/bin/buildout'...X
Let's make sure the generated `buildout` script uses it::
>>> print_(open(buildout_script).read()) # doctest: +ELLIPSIS
#...
sys.path[0:0] = [
'/sample/eggs/setuptools-8.0...egg',
'/sample/eggs/zc.buildout-...egg',
]...
Now let's try specifying both `zc.buildout` and `setuptools` to versions
which happens to exist::
>>> print_('X'); print_(system(
... zc.buildout.easy_install._safe_arg(sys.executable)+' '+
... 'bootstrap.py --setuptools-version 8.0 --version 2.0.0')); print_('X')
... # doctest: +ELLIPSIS
X...Generated script '/sample/bin/buildout'...X
Let's make sure the generated `buildout` script uses it::
>>> print_(open(buildout_script).read()) # doctest: +ELLIPSIS
#...
sys.path[0:0] = [
'/sample/eggs/setuptools-8.0...egg',
'/sample/eggs/zc.buildout-2.0.0...egg',
]...
......@@ -35,6 +35,10 @@ import subprocess
import sys
import tempfile
import zc.buildout
import warnings
warnings.filterwarnings(
'ignore', '.+is being parsed as a legacy, non PEP 440, version')
_oprp = getattr(os.path, 'realpath', lambda path: path)
def realpath(path):
......@@ -421,10 +425,10 @@ class Installer:
# Now find the best one:
best = []
bestv = ()
bestv = None
for dist in dists:
distv = dist.parsed_version
if distv > bestv:
if bestv is None or distv > bestv:
best = [dist]
bestv = distv
elif distv == bestv:
......@@ -1399,12 +1403,8 @@ def _fix_file_links(links):
link += '/'
yield link
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
return not parsed_version.is_prerelease
def redo_pyc(egg):
if not os.path.isdir(egg):
......@@ -1441,12 +1441,27 @@ def redo_pyc(egg):
call_subprocess(args)
def _constrained_requirement(constraint, requirement):
if constraint[0] not in '<>':
if constraint.startswith('='):
assert constraint.startswith('==')
constraint = constraint[2:]
if constraint not in requirement:
bad_constraint(constraint, requirement)
# Sigh, copied from Requirement.__str__
extras = ','.join(requirement.extras)
if extras:
extras = '[%s]' % extras
return pkg_resources.Requirement.parse(
"%s[%s]%s" % (
requirement.project_name,
','.join(requirement.extras),
_constrained_requirement_constraint(constraint, requirement)
"%s%s==%s" % (requirement.project_name, extras, constraint))
if requirement.specs:
return pkg_resources.Requirement.parse(
str(requirement) + ',' + constraint
)
else:
return pkg_resources.Requirement.parse(
str(requirement) + ' ' + constraint
)
class IncompatibleConstraintError(zc.buildout.UserError):
......@@ -1459,91 +1474,3 @@ 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)
_parse_constraint = re.compile(r'([<>]=?)\s*(\S+)').match
_comparef = {
'>' : lambda x, y: x > y,
'>=': lambda x, y: x >= y,
'<' : lambda x, y: x < y,
'<=': lambda x, y: x <= y,
}
_opop = {'<': '>', '>': '<'}
_opeqop = {'<': '>=', '>': '<='}
def _constrained_requirement_constraint(constraint, requirement):
# Simple cases:
# No specs to merge with:
if not requirement.specs:
if not constraint[0] in '<=>':
constraint = '==' + constraint
return constraint
# Simple single-version constraint:
if constraint[0] not in '<>':
if constraint.startswith('='):
assert constraint.startswith('==')
constraint = constraint[2:]
if constraint in requirement:
return '=='+constraint
bad_constraint(constraint, requirement)
# OK, we have a complex constraint (<. <=, >=, or >) and specs.
# In many cases, the spec needs to filter constraints.
# In other cases, the constraints need to limit the constraint.
specs = requirement.specs
cop, cv = _parse_constraint(constraint).group(1, 2)
pcv = pkg_resources.parse_version(cv)
# Special case, all of the specs are == specs:
if not [op for (op, v) in specs if op != '==']:
# There aren't any non-== specs.
# See if any of the specs satisfy the constraint:
specs = [op+v for (op, v) in specs
if _comparef[cop](pkg_resources.parse_version(v), pcv)]
if specs:
return ','.join(specs)
bad_constraint(constraint, requirement)
cop0 = cop[0]
# Normalize specs by splitting >= and <= specs. We need to do this
# because these have really weird semantics. Also cache parsed
# versions, which we'll need for comparisons:
specs = []
for op, v in requirement.specs:
pv = pkg_resources.parse_version(v)
if op == _opeqop[cop0]:
specs.append((op[0], v, pv))
specs.append(('==', v, pv))
else:
specs.append((op, v, pv))
# Error if there are opposite specs that conflict with the constraint
# and there are no equal specs that satisfy the constraint:
if [v for (op, v, pv) in specs
if op == _opop[cop0] and _comparef[_opop[cop0]](pv, pcv)
]:
eqspecs = [op+v for (op, v, pv) in specs
if _comparef[cop](pv, pcv)]
if eqspecs:
# OK, we do, use these:
return ','.join(eqspecs)
bad_constraint(constraint, requirement)
# We have a combination of range constraints and eq specs that
# satisfy the requirement.
# Return the constraint + the filtered specs
return ','.join(
op+v
for (op, v) in (
[(cop, cv)] +
[(op, v) for (op, v, pv) in specs if _comparef[cop](pv, pcv)]
)
)
......@@ -163,6 +163,34 @@ We won't get output for the spam distribution, which we didn't pick,
but we will get output for setuptools, which we didn't specify
versions for.
.. Edge case: version applied to range requirement:
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = foo
... find-links = %s
...
... [versions]
... spam = 1
... eggs = 2.2
...
... [foo]
... recipe = spam >0
... ''' % join('recipe', 'dist'))
>>> print_(system(buildout+' -v'), end='')
Installing 'zc.buildout', 'setuptools'.
We have a develop egg: zc.buildout 1.0.0.
We have the best distribution that satisfies 'setuptools'.
Picked: setuptools = 0.6
Installing 'spam >0'.
We have the distribution that satisfies 'spam==1'.
Uninstalling foo.
Installing foo.
recipe v1
You can request buildout to generate an error if it picks any
versions:
......@@ -204,7 +232,8 @@ We can name a version something else, if we wish, using the versions option:
... recipe = spam
... ''' % join('recipe', 'dist'))
>>> print_(system(buildout), end='') # doctest: +ELLIPSIS
Updating foo.
Uninstalling foo.
Installing foo.
recipe v1
We can also disable checking versions:
......
......@@ -2735,28 +2735,7 @@ def test_constrained_requirement():
... ('x', '1', 'x==1'),
... ('x>1', '2', 'x==2'),
... ('x>3', '2', IncompatibleConstraintError),
... ('x>1', '>2', 'x>2'),
... ('x>1', '> 2', 'x>2'),
... ('x>1', '>=2', 'x>=2'),
... ('x<1', '>2', IncompatibleConstraintError),
... ('x<=1', '>=1', 'x>=1,<1,==1'),
... ('x<3', '>1', 'x>1,<3'),
... ('x==2', '>1', 'x==2'),
... ('x==2', '>=2', 'x==2'),
... ('x[y]', '1', 'x[y]==1'),
... ('x[y]>1', '2', 'x[y]==2'),
... ('x<3', '2', 'x==2'),
... ('x<1', '2', IncompatibleConstraintError),
... ('x<3', '<2', 'x<2'),
... ('x<3', '< 2', 'x<2'),
... ('x<3', '<=2', 'x<=2'),
... ('x<3', '<= 2', 'x<=2'),
... ('x>3', '<2', IncompatibleConstraintError),
... ('x>=1', '<=1', 'x<=1,>1,==1'),
... ('x<3', '>1', 'x>1,<3'),
... ('x==2', '<3', 'x==2'),
... ('x==2', '<=2', 'x==2'),
... ('x[y]<3', '2', 'x[y]==2'),
... ('x>1', '>2', 'x>1,>2'),
... ]
>>> from zc.buildout.easy_install import _constrained_requirement
>>> for o, c, e in examples:
......@@ -2853,13 +2832,14 @@ def want_new_zcrecipeegg():
... eggs = demo
... ''')
>>> print_(system(join('bin', 'buildout')), end='') # doctest: +ELLIPSIS
The constraint, >=2.0.0a3,...
Getting distribution for 'zc.recipe.egg<2dev,>=2.0.0a3'.
While:
Installing.
Getting section egg.
Initializing section egg.
Installing recipe zc.recipe.egg <2dev.
Error: Bad constraint >=2.0.0a3 zc.recipe.egg<2dev
Getting distribution for 'zc.recipe.egg<2dev,>=2.0.0a3'.
Error: Couldn't find a distribution for 'zc.recipe.egg<2dev,>=2.0.0a3'.
"""
def macro_inheritance_bug():
......
......@@ -13,13 +13,11 @@
##############################################################################
"""Install packages as eggs
"""
import logging
import os
import re
import sys
import zc.buildout.easy_install
import zipfile
logger = logging.getLogger(__name__)
......
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