Skip to content

Commit e8b1787

Browse files
committed
Updated bootstrap.py
1 parent ec9fa1f commit e8b1787

File tree

1 file changed

+218
-66
lines changed

1 file changed

+218
-66
lines changed

bootstrap.py

+218-66
Original file line numberDiff line numberDiff line change
@@ -16,116 +16,268 @@
1616
Simply run this script in a directory containing a buildout.cfg.
1717
The script accepts buildout command-line options, so you can
1818
use the -c option to specify an alternate configuration file.
19-
20-
$Id$
2119
"""
2220

2321
import os
2422
import shutil
2523
import sys
2624
import tempfile
25+
import urllib
2726
import urllib2
27+
import subprocess
2828
from optparse import OptionParser
2929

30-
tmpeggs = tempfile.mkdtemp()
30+
if sys.platform == 'win32':
31+
def quote(c):
32+
if ' ' in c:
33+
return '"%s"' % c # work around spawn lamosity on windows
34+
else:
35+
return c
36+
else:
37+
quote = str
38+
39+
# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
40+
stdout, stderr = subprocess.Popen(
41+
[sys.executable, '-Sc',
42+
'try:\n'
43+
' import ConfigParser\n'
44+
'except ImportError:\n'
45+
' print 1\n'
46+
'else:\n'
47+
' print 0\n'],
48+
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
49+
has_broken_dash_S = bool(int(stdout.strip()))
50+
51+
# In order to be more robust in the face of system Pythons, we want to
52+
# run without site-packages loaded. This is somewhat tricky, in
53+
# particular because Python 2.6's distutils imports site, so starting
54+
# with the -S flag is not sufficient. However, we'll start with that:
55+
if not has_broken_dash_S and 'site' in sys.modules:
56+
# We will restart with python -S.
57+
args = sys.argv[:]
58+
args[0:0] = [sys.executable, '-S']
59+
args = map(quote, args)
60+
os.execv(sys.executable, args)
61+
# Now we are running with -S. We'll get the clean sys.path, import site
62+
# because distutils will do it later, and then reset the path and clean
63+
# out any namespace packages from site-packages that might have been
64+
# loaded by .pth files.
65+
clean_path = sys.path[:]
66+
import site # imported because of its side effects
67+
sys.path[:] = clean_path
68+
for k, v in sys.modules.items():
69+
if k in ('setuptools', 'pkg_resources') or (
70+
hasattr(v, '__path__') and
71+
len(v.__path__) == 1 and
72+
not os.path.exists(os.path.join(v.__path__[0], '__init__.py'))):
73+
# This is a namespace package. Remove it.
74+
sys.modules.pop(k)
3175

3276
is_jython = sys.platform.startswith('java')
3377

78+
setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
79+
distribute_source = 'http://python-distribute.org/distribute_setup.py'
80+
81+
3482
# parsing arguments
35-
parser = OptionParser(
36-
'This is a custom version of the zc.buildout %prog script. It is '
37-
'intended to meet a temporary need if you encounter problems with '
38-
'the zc.buildout 1.5 release.')
39-
parser.add_option("-v", "--version", dest="version", default='1.4.4',
40-
help='Use a specific zc.buildout version. *This '
41-
'bootstrap script defaults to '
42-
'1.4.4, unlike usual buildpout bootstrap scripts.*')
43-
parser.add_option("-d", "--distribute",
44-
action="store_true", dest="distribute", default=False,
45-
help="Use Disribute rather than Setuptools.")
83+
def normalize_to_url(option, opt_str, value, parser):
84+
if value:
85+
if '://' not in value: # It doesn't smell like a URL.
86+
value = 'file://%s' % (
87+
urllib.pathname2url(
88+
os.path.abspath(os.path.expanduser(value))),)
89+
if opt_str == '--download-base' and not value.endswith('/'):
90+
# Download base needs a trailing slash to make the world happy.
91+
value += '/'
92+
else:
93+
value = None
94+
name = opt_str[2:].replace('-', '_')
95+
setattr(parser.values, name, value)
96+
97+
usage = '''\
98+
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
99+
100+
Bootstraps a buildout-based project.
46101
102+
Simply run this script in a directory containing a buildout.cfg, using the
103+
Python that you want bin/buildout to use.
104+
105+
Note that by using --setup-source and --download-base to point to
106+
local resources, you can keep this script from going over the network.
107+
'''
108+
109+
parser = OptionParser(usage=usage)
110+
parser.add_option("-v", "--version", dest="version",
111+
help="use a specific zc.buildout version")
112+
parser.add_option("-d", "--distribute",
113+
action="store_true", dest="use_distribute", default=False,
114+
help="Use Distribute rather than Setuptools.")
115+
parser.add_option("--setup-source", action="callback", dest="setup_source",
116+
callback=normalize_to_url, nargs=1, type="string",
117+
help=("Specify a URL or file location for the setup file. "
118+
"If you use Setuptools, this will default to " +
119+
setuptools_source + "; if you use Distribute, this "
120+
"will default to " + distribute_source + "."))
121+
parser.add_option("--download-base", action="callback", dest="download_base",
122+
callback=normalize_to_url, nargs=1, type="string",
123+
help=("Specify a URL or directory for downloading "
124+
"zc.buildout and either Setuptools or Distribute. "
125+
"Defaults to PyPI."))
126+
parser.add_option("--eggs",
127+
help=("Specify a directory for storing eggs. Defaults to "
128+
"a temporary directory that is deleted when the "
129+
"bootstrap script completes."))
130+
parser.add_option("-t", "--accept-buildout-test-releases",
131+
dest='accept_buildout_test_releases',
132+
action="store_true", default=False,
133+
help=("Normally, if you do not specify a --version, the "
134+
"bootstrap script and buildout gets the newest "
135+
"*final* versions of zc.buildout and its recipes and "
136+
"extensions for you. If you use this flag, "
137+
"bootstrap and buildout will get the newest releases "
138+
"even if they are alphas or betas."))
47139
parser.add_option("-c", None, action="store", dest="config_file",
48140
help=("Specify the path to the buildout configuration "
49141
"file to be used."))
50142

51143
options, args = parser.parse_args()
52144

53-
# if -c was provided, we push it back into args for buildout' main function
54-
if options.config_file is not None:
55-
args += ['-c', options.config_file]
56-
57-
if options.version is not None:
58-
VERSION = '==%s' % options.version
145+
if options.eggs:
146+
eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
59147
else:
60-
VERSION = ''
148+
eggs_dir = tempfile.mkdtemp()
149+
150+
if options.setup_source is None:
151+
if options.use_distribute:
152+
options.setup_source = distribute_source
153+
else:
154+
options.setup_source = setuptools_source
61155

62-
USE_DISTRIBUTE = options.distribute
63-
args = args + ['bootstrap']
156+
if options.accept_buildout_test_releases:
157+
args.insert(0, 'buildout:accept-buildout-test-releases=true')
64158

65-
to_reload = False
66159
try:
67160
import pkg_resources
161+
import setuptools # A flag. Sometimes pkg_resources is installed alone.
68162
if not hasattr(pkg_resources, '_distribute'):
69-
to_reload = True
70163
raise ImportError
71164
except ImportError:
165+
ez_code = urllib2.urlopen(
166+
options.setup_source).read().replace('\r\n', '\n')
72167
ez = {}
73-
if USE_DISTRIBUTE:
74-
exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
75-
).read() in ez
76-
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
77-
else:
78-
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
79-
).read() in ez
80-
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
168+
exec ez_code in ez
169+
setup_args = dict(to_dir=eggs_dir, download_delay=0)
170+
if options.download_base:
171+
setup_args['download_base'] = options.download_base
172+
if options.use_distribute:
173+
setup_args['no_fake'] = True
174+
if sys.version_info[:2] == (2, 4):
175+
setup_args['version'] = '0.6.32'
176+
ez['use_setuptools'](**setup_args)
177+
if 'pkg_resources' in sys.modules:
178+
reload(sys.modules['pkg_resources'])
179+
import pkg_resources
180+
# This does not (always?) update the default working set. We will
181+
# do it.
182+
for path in sys.path:
183+
if path not in pkg_resources.working_set.entries:
184+
pkg_resources.working_set.add_entry(path)
81185

82-
if to_reload:
83-
reload(pkg_resources)
84-
else:
85-
import pkg_resources
186+
cmd = [quote(sys.executable),
187+
'-c',
188+
quote('from setuptools.command.easy_install import main; main()'),
189+
'-mqNxd',
190+
quote(eggs_dir)]
86191

87-
if sys.platform == 'win32':
88-
def quote(c):
89-
if ' ' in c:
90-
return '"%s"' % c # work around spawn lamosity on windows
91-
else:
92-
return c
93-
else:
94-
def quote(c):
95-
return c
192+
if not has_broken_dash_S:
193+
cmd.insert(1, '-S')
96194

97-
ws = pkg_resources.working_set
195+
find_links = options.download_base
196+
if not find_links:
197+
find_links = os.environ.get('bootstrap-testing-find-links')
198+
if not find_links and options.accept_buildout_test_releases:
199+
find_links = 'http://downloads.buildout.org/'
200+
if find_links:
201+
cmd.extend(['-f', quote(find_links)])
98202

99-
if USE_DISTRIBUTE:
100-
requirement = 'distribute'
203+
if options.use_distribute:
204+
setup_requirement = 'distribute'
101205
else:
102-
requirement = 'setuptools'
206+
setup_requirement = 'setuptools'
207+
ws = pkg_resources.working_set
208+
setup_requirement_path = ws.find(
209+
pkg_resources.Requirement.parse(setup_requirement)).location
210+
env = dict(
211+
os.environ,
212+
PYTHONPATH=setup_requirement_path)
103213

104-
env = dict(os.environ,
105-
PYTHONPATH=
106-
ws.find(pkg_resources.Requirement.parse(requirement)).location
107-
)
214+
requirement = 'zc.buildout'
215+
version = options.version
216+
if version is None and not options.accept_buildout_test_releases:
217+
# Figure out the most recent final version of zc.buildout.
218+
import setuptools.package_index
219+
_final_parts = '*final-', '*final'
108220

109-
cmd = [quote(sys.executable),
110-
'-c',
111-
quote('from setuptools.command.easy_install import main; main()'),
112-
'-mqNxd',
113-
quote(tmpeggs)]
221+
def _final_version(parsed_version):
222+
for part in parsed_version:
223+
if (part[:1] == '*') and (part not in _final_parts):
224+
return False
225+
return True
226+
index = setuptools.package_index.PackageIndex(
227+
search_path=[setup_requirement_path])
228+
if find_links:
229+
index.add_find_links((find_links,))
230+
req = pkg_resources.Requirement.parse(requirement)
231+
if index.obtain(req) is not None:
232+
best = []
233+
bestv = None
234+
for dist in index[req.project_name]:
235+
distv = dist.parsed_version
236+
if distv >= pkg_resources.parse_version('2dev'):
237+
continue
238+
if _final_version(distv):
239+
if bestv is None or distv > bestv:
240+
best = [dist]
241+
bestv = distv
242+
elif distv == bestv:
243+
best.append(dist)
244+
if best:
245+
best.sort()
246+
version = best[-1].version
114247

115-
if 'bootstrap-testing-find-links' in os.environ:
116-
cmd.extend(['-f', os.environ['bootstrap-testing-find-links']])
248+
if version:
249+
requirement += '==' + version
250+
else:
251+
requirement += '<2dev'
117252

118-
cmd.append('zc.buildout' + VERSION)
253+
cmd.append(requirement)
119254

120255
if is_jython:
121256
import subprocess
122257
exitcode = subprocess.Popen(cmd, env=env).wait()
123258
else: # Windows prefers this, apparently; otherwise we would prefer subprocess
124259
exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
125-
assert exitcode == 0
260+
if exitcode != 0:
261+
sys.stdout.flush()
262+
sys.stderr.flush()
263+
print ("An error occurred when trying to install zc.buildout. "
264+
"Look above this message for any errors that "
265+
"were output by easy_install.")
266+
sys.exit(exitcode)
126267

127-
ws.add_entry(tmpeggs)
128-
ws.require('zc.buildout' + VERSION)
268+
ws.add_entry(eggs_dir)
269+
ws.require(requirement)
129270
import zc.buildout.buildout
271+
272+
# If there isn't already a command in the args, add bootstrap
273+
if not [a for a in args if '=' not in a]:
274+
args.append('bootstrap')
275+
276+
277+
# if -c was provided, we push it back into args for buildout's main function
278+
if options.config_file is not None:
279+
args[0:0] = ['-c', options.config_file]
280+
130281
zc.buildout.buildout.main(args)
131-
shutil.rmtree(tmpeggs)
282+
if not options.eggs: # clean up temporary egg directory
283+
shutil.rmtree(eggs_dir)

0 commit comments

Comments
 (0)