[Checkins] SVN: zc.buildout/branches/gary-2-bootstrap-changes/ change bootstrap to allow a bit more configuration and flexibility

Gary Poster gary.poster at canonical.com
Thu Sep 24 16:10:31 EDT 2009


Log message for revision 104512:
  change bootstrap to allow a bit more configuration and flexibility

Changed:
  U   zc.buildout/branches/gary-2-bootstrap-changes/bootstrap/bootstrap.py
  U   zc.buildout/branches/gary-2-bootstrap-changes/src/zc/buildout/bootstrap.txt

-=-
Modified: zc.buildout/branches/gary-2-bootstrap-changes/bootstrap/bootstrap.py
===================================================================
--- zc.buildout/branches/gary-2-bootstrap-changes/bootstrap/bootstrap.py	2009-09-24 20:08:34 UTC (rev 104511)
+++ zc.buildout/branches/gary-2-bootstrap-changes/bootstrap/bootstrap.py	2009-09-24 20:10:30 UTC (rev 104512)
@@ -20,21 +20,108 @@
 $Id$
 """
 
-import os, shutil, sys, tempfile, urllib2
+import os, re, shutil, sys, tempfile, textwrap, urllib, urllib2
 
-tmpeggs = tempfile.mkdtemp()
+# We have to manually parse our options rather than using one of the stdlib
+# tools because we want to pass the ones we don't recognize along to
+# zc.buildout.buildout.main.
 
-is_jython = sys.platform.startswith('java')
+configuration = {
+    '--ez_setup-source': 'http://peak.telecommunity.com/dist/ez_setup.py',
+    '--version': '',
+    '--download-base': None,
+    '--eggs': None}
 
+helpstring = __doc__ + textwrap.dedent('''
+    This script recognizes the following options itself.  The first option it
+    encounters that is not one of these will cause the script to stop parsing
+    options and pass the rest on to buildout.  Therefore, if you want to use
+    any of the following options *and* buildout command-line options like
+    -c, first use the following options, and then use the buildout options.
+
+    Options:
+      --version=ZC_BUILDOUT_VERSION
+                Specify a version number of the zc.buildout to use
+      --ez_setup-source=URL_OR_FILE
+                Specify a URL or file location for the ez_setup file.
+                Defaults to
+                %(--ez_setup-source)s
+      --download-base=URL_OR_DIRECTORY
+                Specify a URL or directory for downloading setuptools and
+                zc.buildout.  Defaults to PyPI.
+      --eggs=DIRECTORY
+                Specify a directory for storing eggs.  Defaults to a temporary
+                directory that is deleted when the bootstrap script completes.
+
+    By using --ez_setup-source and --download-base to point to local resources,
+    you can keep this script from going over the network.
+    ''' % configuration)
+match_equals = re.compile(r'(%s)=(.*)' % ('|'.join(configuration),)).match
+args = sys.argv[1:]
+if args == ['--help']:
+    print helpstring
+    sys.exit(0)
+
+# If we end up using a temporary directory for storing our eggs, this will
+# hold the path of that directory.  On the other hand, if an explicit directory
+# is specified in the argv, this will remain None.
+tmpeggs = None
+
+while args:
+    val = args[0]
+    if val in configuration:
+        del args[0]
+        if not args or args[0].startswith('-'):
+            print "ERROR: %s requires an argument."
+            print helpstring
+            sys.exit(1)
+        configuration[val] = args[0]
+    else:
+        match = match_equals(val)
+        if match and match.group(1) in configuration:
+            configuration[match.group(1)] = match.group(2)
+        else:
+            break
+    del args[0]
+
+for name in ('--ez_setup-source', '--download-base'):
+    val = configuration[name]
+    if val is not None and '://' not in val: # We're being lazy.
+        configuration[name] = 'file://%s' % (
+            urllib.pathname2url(os.path.abspath(os.path.expanduser(val))),)
+
+if (configuration['--download-base'] and
+    not configuration['--download-base'].endswith('/')):
+    # Download base needs a trailing slash to make the world happy.
+    configuration['--download-base'] += '/'
+
+if not configuration['--eggs']:
+    configuration['--eggs'] = tmpeggs = tempfile.mkdtemp()
+else:
+    configuration['--eggs'] = os.path.abspath(
+        os.path.expanduser(configuration['--eggs']))
+
+# The requirement is what we will pass to setuptools to specify zc.buildout.
+requirement = 'zc.buildout'
+if configuration['--version']:
+    requirement += '==' + configuration['--version']
+
 try:
+    import setuptools # A flag.  Sometimes pkg_resources is installed alone.
     import pkg_resources
 except ImportError:
     ez = {}
-    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
-                         ).read() in ez
-    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
-
+    exec urllib2.urlopen(configuration['--ez_setup-source']).read() in ez
+    setuptools_args = dict(to_dir=configuration['--eggs'], download_delay=0)
+    if configuration['--download-base']:
+        setuptools_args['download_base'] = configuration['--download-base']
+    ez['use_setuptools'](**setuptools_args)
     import pkg_resources
+    # This does not (always?) update the default working set.  We will
+    # do it.
+    for path in sys.path:
+        if path not in pkg_resources.working_set.entries:
+            pkg_resources.working_set.add_entry(path)
 
 if sys.platform == 'win32':
     def quote(c):
@@ -45,40 +132,40 @@
 else:
     def quote (c):
         return c
+cmd = [quote(sys.executable),
+       '-c',
+       quote('from setuptools.command.easy_install import main; main()'),
+       '-mqNxd',
+       quote(configuration['--eggs'])]
 
-cmd = 'from setuptools.command.easy_install import main; main()'
-ws  = pkg_resources.working_set
+if configuration['--download-base']:
+    cmd.extend(['-f', quote(configuration['--download-base'])])
 
-if len(sys.argv) > 2 and sys.argv[1] == '--version':
-    VERSION = '==%s' % sys.argv[2]
-    args = sys.argv[3:] + ['bootstrap']
-else:
-    VERSION = ''
-    args = sys.argv[1:] + ['bootstrap']
+cmd.append(requirement)
 
+ws = pkg_resources.working_set
+env = dict(
+    os.environ,
+    PYTHONPATH=ws.find(pkg_resources.Requirement.parse('setuptools')).location)
+
+is_jython = sys.platform.startswith('java')
 if is_jython:
     import subprocess
+    exitcode = subprocess.Popen(cmd, env=env).wait()
+else: # Windows needs this, apparently; otherwise we would prefer subprocess
+    exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
+if exitcode != 0:
+    sys.stdout.flush()
+    sys.stderr.flush()
+    print ("An error occured when trying to install zc.buildout. "
+           "Look above this message for any errors that "
+           "were output by easy_install.")
+    sys.exit(exitcode)
 
-    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
-           quote(tmpeggs), 'zc.buildout' + VERSION],
-           env=dict(os.environ,
-               PYTHONPATH=
-               ws.find(pkg_resources.Requirement.parse('setuptools')).location
-               ),
-           ).wait() == 0
-
-else:
-    assert os.spawnle(
-        os.P_WAIT, sys.executable, quote (sys.executable),
-        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
-        dict(os.environ,
-            PYTHONPATH=
-            ws.find(pkg_resources.Requirement.parse('setuptools')).location
-            ),
-        ) == 0
-
-ws.add_entry(tmpeggs)
-ws.require('zc.buildout' + VERSION)
+ws.add_entry(configuration['--eggs'])
+ws.require(requirement)
 import zc.buildout.buildout
+args.append('bootstrap')
 zc.buildout.buildout.main(args)
-shutil.rmtree(tmpeggs)
+if tmpeggs is not None:
+    shutil.rmtree(tmpeggs)

Modified: zc.buildout/branches/gary-2-bootstrap-changes/src/zc/buildout/bootstrap.txt
===================================================================
--- zc.buildout/branches/gary-2-bootstrap-changes/src/zc/buildout/bootstrap.txt	2009-09-24 20:08:34 UTC (rev 104511)
+++ zc.buildout/branches/gary-2-bootstrap-changes/src/zc/buildout/bootstrap.txt	2009-09-24 20:10:30 UTC (rev 104512)
@@ -57,7 +57,7 @@
     ...     'bootstrap.py --version UNKNOWN'); print 'X' # doctest: +ELLIPSIS
     ...
     X
-    No local packages or download links found for zc.buildout==UNKNOWN
+    No local packages or download links found for zc.buildout==UNKNOWN...
     error: Could not find suitable distribution for Requirement.parse('zc.buildout==UNKNOWN')
     ...
 
@@ -120,4 +120,71 @@
         zc.buildout.buildout.main()
     <BLANKLINE>
 
+You can specify a location of ez_setup.py, so you can rely on a local or remote
+location.  We'll write our own ez_setup.py that we will also use to test some
+other bootstrap options.
 
+    >>> write('ez_setup.py', '''\
+    ... def use_setuptools(**kwargs):
+    ...     import sys, pprint
+    ...     pprint.pprint(kwargs, width=40)
+    ...     sys.exit()
+    ... ''')
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --ez_setup-source=./ez_setup.py')
+    ... # doctest: +ELLIPSIS
+    {'download_delay': 0,
+     'to_dir': '...'}
+    <BLANKLINE>
+
+You can also pass a download-cache, and a place in which eggs should be stored
+(they are normally stored in a temporary directory).
+
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --ez_setup-source=./ez_setup.py '+
+    ...     '--download-base=./download-cache --eggs=eggs')
+    ... # doctest: +ELLIPSIS
+    {'download_base': '/sample/download-cache/',
+     'download_delay': 0,
+     'to_dir': '/sample/eggs'}
+    <BLANKLINE>
+
+Here's the entire help text.
+
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --help'),
+    ... # doctest: +ELLIPSIS
+    Bootstrap a buildout-based project
+    <BLANKLINE>
+    Simply run this script in a directory containing a buildout.cfg.
+    The script accepts buildout command-line options, so you can
+    use the -c option to specify an alternate configuration file.
+    <BLANKLINE>
+    ...
+    <BLANKLINE>
+    This script recognizes the following options itself.  The first option it
+    encounters that is not one of these will cause the script to stop parsing
+    options and pass the rest on to buildout.  Therefore, if you want to use
+    any of the following options *and* buildout command-line options like
+    -c, first use the following options, and then use the buildout options.
+    <BLANKLINE>
+    Options:
+      --version=ZC_BUILDOUT_VERSION
+                Specify a version number of the zc.buildout to use
+      --ez_setup-source=URL_OR_FILE
+                Specify a URL or file location for the ez_setup file.
+                Defaults to
+                http://peak.telecommunity.com/dist/ez_setup.py
+      --download-base=URL_OR_DIRECTORY
+                Specify a URL or directory for downloading setuptools and
+                zc.buildout.  Defaults to PyPI.
+      --eggs=DIRECTORY
+                Specify a directory for storing eggs.  Defaults to a temporary
+                directory that is deleted when the bootstrap script completes.
+    <BLANKLINE>
+    By using --ez_setup-source and --download-base to point to local resources,
+    you can keep this script from going over the network.
+    <BLANKLINE>



More information about the checkins mailing list