[Python-checkins] devinabox: Completely rework devinabox to be easier for core devs to use.

brett.cannon python-checkins at python.org
Fri Mar 22 00:26:39 CET 2013


http://hg.python.org/devinabox/rev/2c20681befa8
changeset:   47:2c20681befa8
user:        Brett Cannon <brett at python.org>
date:        Thu Mar 21 19:26:30 2013 -0400
summary:
  Completely rework devinabox to be easier for core devs to use.

Previously devinabox was essentially a script for people to use to
pull together a directory full of the code needed to contribute to
Python. Unfortunately the script turned out to be brittle, requiring
patches for every sprint it was used with.

Since the users of this project have continually been core devs,
re-orient towards core devs leading sprints. This means a README
outlining what hg repos to clone, a script to help generate a coverage
report using coverage.py, and a script for new contributors to use to
help build CPython quickly and with sane defaults in a single step.
All other details should be gleaned from the devguide.

files:
  .hgignore        |   12 +-
  README           |  158 +++++++++++++++
  build_cpython.py |   12 +-
  full_coverage.py |  127 ++++++++++++
  index.html       |   43 ++++
  make_a_box.py    |  362 -----------------------------------
  6 files changed, 338 insertions(+), 376 deletions(-)


diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,20 +1,12 @@
 syntax: glob
 
-# Repositories/files
+# Repositories
 coveragepy
 cpython
 devguide
-Mercurial
 peps
-Visual C++ Express
 # Generated
-.coverage
 coverage_report
-build
-coverage.html
-README
+original_coverage_report
 # Misc
-.*.swp
-*.pyc
-*.pyo
 __pycache__
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,158 @@
+devinabox -- Bootstrapping a Python core dev sprint
+/////////////////////////////////////////////////////////
+
+The devinabox project is meant to help a CPython core developer produce a
+directory which contains everything necessary to enable someone at a sprint
+(regardless of OS) to get set up quickly. This README is **NOT** meant for a new
+contributor! If you *are* a new contributor, ask the core developer leading your
+sprint about how to get started.
+
+This README outlines two things: what to download to create a devinabox and what
+is provided to help new contributors.
+
+
+Stuff to download
+=================
+
+The following sections outline various files to download and repositories to
+clone into your devinabox. Make sure to **NOT** change the directories that
+repositories are cloned into. The default names are assumed by the other files
+in the devinabox.
+
+When you are done you should have in this directory everything
+someone needs to contribute. Simply copy the whole directory to some sort of
+media (USB 3 thumb drive and CD tend to work well) and then pass it around for
+people to copy somewhere on to their system. They can run ``hg pull -u`` to
+get updates, sparing the probably taxed internet connection at the sprint from
+doing complete repository cloning.
+
+
+Mercurial
+---------
+
+You will want to download the latest release of Mercurial
+(http://pypi.python.org/pypi/Mercurial) and TortoiseHg for Windows users
+(http://tortoisehg.bitbucket.org/download/). OS X users can be told that
+Mercurial is available through Homebrew if they prefer
+(XXX; if they use MacPorts or any other package manager then tell them they
+should switch to Homebrew at home as it handles Python the best and to use the
+download of Mercurial you have provided to save time).
+
+Providing Mercurial guarantees there is no issue with new contributors trying to
+update repositories or generating patches.
+
+
+A Compiler
+-----------
+
+Since you will most likely be dealing with developers this section is probably
+not important, but just in case you get questions about compilers, here are some
+suggestions.
+
+OS X users should be told to download XCode from the Apple App Store **ahead of
+time**. It's on the order of a couple GiB in size, so you don't want to have
+people downloading it at the sprint.
+
+If new contributors think they may be doing C development, suggest LLVM + clang
+for better error reporting than gcc. OS X users can get an up-to-date version
+of the toolchain through Homebrew.
+
+For Windows users, tell them to download and install Visual C++ Express
+(http://www.microsoft.com/express/Downloads/) **ahead of time**.
+
+
+CPython
+-------
+
+Clone the CPython repository and build it (you will be cleaning up your build
+later, though as a final step).
+
+Also make sure to build the documentation. This not only alleviates the need for
+everyone to build it from scratch, but it will also pull in copies of projects
+that users can rely on, if necessary, to build other documentation
+(e.g. everything needed to build the devguide). If the documentation starts
+being built by a repository build of CPython (and thus Python 3) this may no
+longer hold true.
+
+
+PEPs
+----
+
+Clone the repository and build it. That way if people need to reference a PEP
+they can easily find itand will be able to use the easier-to-read HTML version.
+
+No specific guidelines for building the PEPs are provided for new contributors
+since there is only a slim chance they will be editing a PEP, and if they are
+then they should be able to figure out how to get the PEPs to build on their
+own.
+
+
+Devguide
+--------
+
+Clone the repository and build it. This gives people a local copy to use
+rather than having to use the (probably slow) internet connection at the sprint.
+
+If a new contributor needs to be able to build the devguide, they should only
+need to set their ``PYTHONPATH`` to point at the ``cpython/Doc/tools`` directory
+in the CPython repository thanks to the requisite projects being pulled in when
+you built the CPython documentation.
+
+
+Coverage.py
+-----------
+
+01. Build the CPython repository
+02. Clone the repository from https://bitbucket.org/ned/coveragepy
+03. Download Distribute from https://pypi.python.org/pypi/distribute
+04. Unpack and build Distribute with Python 3 in the coveragepy repository
+05. Symlink the ``distribute-N.N.N/build/lib/setuptools`` directory into
+   ``coveragepy``
+06. Symlink ``distribute-N.N.N/build/lib/pkg_resources.py`` into ``coveragepy``
+07. Run ``./cpython/python full_coverage.py build``
+08. Run ``./cpython/python full_coverage.py run``
+09. Run ``./cpython/python full_coverage.py html original_coverage_report``
+10. ``make distclean`` the CPython repository
+
+
+All these steps will generate a complete coverage report for the standard
+library and put it in the ``original_coverage_report`` directory. Do note that
+the  location is **not** the default one for the script to prevent users from
+accidentally overwriting the original copy (and thus needing to run the whole
+coverage again from scratch).
+
+Do be aware that this step takes a few **hours**.
+
+
+Included files to help out
+==========================
+
+A couple of files are included in order to make things a little bit easier for
+both you and the new contributors.
+
+
+``full_coverage.py``
+---------------------
+
+As discussed earlier, this generates the coverage report for the standard
+library in the most thorough way possible. The ``run`` option can take specific
+tests as an argument. The ``html`` directory can take an argument for a
+directory to write to, but the default should not conflict with the original
+coverage run you did earlier (if you followed the directions =) .
+
+
+``build_cpython.py``
+--------------------
+On UNIX-based OSs, builds the CPython repository, and on all platforms it
+verifies that the expected CPython binary exists.
+
+While the devguide includes instructions on how to build under UNIX, the script
+just simplifies this by having a single command subsume both the configure and
+build steps. It also uses reasonable defaults (e.g. all cores on the CPU).
+
+
+``index.html``
+--------------
+
+An HTML file with links to the various pieces of documentation you built
+previously and the helper scripts.
\ No newline at end of file
diff --git a/build_cpython.py b/build_cpython.py
--- a/build_cpython.py
+++ b/build_cpython.py
@@ -1,5 +1,9 @@
 #!/usr/bin/env python
-"""Build CPython"""
+"""Build CPython on UNIX.
+
+On all platforms, return the path to the executable.
+
+"""
 import multiprocessing
 import os
 import subprocess
@@ -45,6 +49,6 @@
     return executable()
 
 if __name__ == '__main__':
-    if not main():
-        print('No executable found')
-        sys.exit(1)
+    executable = main()
+    print(executable or 'No executable found')
+    sys.exit(0 if executable else 1)
diff --git a/full_coverage.py b/full_coverage.py
new file mode 100644
--- /dev/null
+++ b/full_coverage.py
@@ -0,0 +1,127 @@
+"""Use coverage.py on CPython's standard library.
+
+See the ``-h`` or ``help`` command of the script for instructions on use.
+
+"""
+import importlib.machinery
+import contextlib
+import os
+import shutil
+import subprocess
+import sys
+import webbrowser
+
+
+def path_from_here(path):
+    """Calculate the absolute path to 'path' from where this file is located."""
+    return os.path.abspath(os.path.join(os.path.dirname(__file__), path))
+
+COVERAGE = path_from_here('coveragepy')
+REPORT = path_from_here('coverage_report')
+CPYTHON = os.path.dirname(sys.executable)
+
+
+ at contextlib.contextmanager
+def chdir(directory):
+    """Change the directory temporarily."""
+    original_directory = os.getcwd()
+    os.chdir(directory)
+    yield
+    os.chdir(original_directory)
+
+
+def build(args):
+    """Build coverage.py's C-based tracer.
+
+    Make sure to delete any pre-existing build to make sure it uses the latest
+    source from CPython.
+    """
+    with chdir(COVERAGE):
+        for ext in importlib.machinery.EXTENSION_SUFFIXES:
+            tracer_path = os.path.join('coverage', 'tracer' + ext)
+            try:
+                os.unlink(tracer_path)
+            except FileNotFoundError:
+                pass
+        subprocess.check_call([sys.executable, 'setup.py', 'clean'])
+        env = os.environ.copy()
+        env['CPPFLAGS'] = '-I {} -I {}'.format(CPYTHON,
+                                               os.path.join(CPYTHON, 'Include'))
+        command = [sys.executable, 'setup.py', 'build_ext', '--inplace']
+        process = subprocess.Popen(command, env=env)
+        process.wait()
+
+
+def run(args):
+    """Run coverage.py against Python's stdlib as best as possible.
+
+    If any specific tests are listed, then limit the run to those tests.
+    """
+    command = [sys.executable, COVERAGE, 'run', '--pylib', 'Lib/test/regrtest.py']
+    if args.tests:
+        command.extend(args.tests)
+    with chdir(CPYTHON):
+        try:
+            os.unlink(os.path.join(CPYTHON, '.coverage'))
+        except FileNotFoundError:
+            pass
+        pythonpath = os.path.join(COVERAGE, 'coverage', 'fullcoverage')
+        process = subprocess.Popen(command, env={'PYTHONPATH': pythonpath})
+        process.wait()
+
+
+def report(args):
+    """Generate the HTML-based coverage report.
+
+    Write the results to either REPORT or a user-specified location.
+    """
+    report = os.path.abspath(args.directory)
+    title = '{} {} test coverage'.format(sys.implementation.name,
+                           sys.version.partition(' \n')[0])
+    if os.path.exists(report):
+        shutil.rmtree(report)
+    with chdir(CPYTHON):
+        subprocess.check_call([sys.executable, COVERAGE, 'html', '-i',
+                               '-d', report, '--omit', 'Lib/test/*',
+                               '--title', title])
+    print(os.path.join(report, 'index.html'))
+
+
+if __name__ == '__main__':
+    import argparse
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers(dest='subparser_name',
+            help="use coverage.py on Python's standard library")
+
+    build_parser = subparsers.add_parser('build',
+            help='build coverage.py using {}'.format(sys.executable))
+    build_parser.set_defaults(func=build)
+
+    stdlib_path = os.path.join(CPYTHON, 'Lib')
+    run_parser = subparsers.add_parser('run',
+            help='run coverage.py over the standard library at {} '
+                 '(coverage.py must already be built)'.format(stdlib_path))
+    run_parser.add_argument('tests', action='store', nargs='*',
+            help='optional list of tests to run (default: all tests)')
+    run_parser.set_defaults(func=run)
+
+    report_parser = subparsers.add_parser('html',
+            help='generate an HTML coverage report')
+    report_parser.add_argument('directory',
+            help='where to save the report (default: {})'.format(REPORT),
+            nargs='?', action='store', default=REPORT)
+    report_parser.set_defaults(func=report)
+
+    help_parser = subparsers.add_parser('help',
+            help='show the help message for the specified command')
+    help_parser.add_argument('command', nargs='?',
+            help='for which command to show a help message')
+
+    args = parser.parse_args()
+    if args.subparser_name != 'help':
+        args.func(args)
+    else:
+        help_args = ['-h']
+        if args.command:
+            help_args.insert(0, args.command)
+        parser.parse_args(help_args)
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
--- /dev/null
+++ b/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>devinabox Documentation Index</title>
+<style>
+    body, li a:nth-child(n+2) {
+        font-size: small;
+    }
+
+    h1 {
+        font-size: xx-large;
+        text-transform: uppercase;
+        background-color: black;
+        color: white;
+        text-indent: 0.5em;
+    }
+
+    a {
+        font-size: x-large;
+    }
+
+    .note {
+        font-size: small;
+    }
+</style>
+
+<h1>Documentation</h1>
+<ul>
+    <li><a href="devguide/_build/html/index.html">Devguide</a>
+        (read first)
+    <li><a href="cpython/Doc/build/html/index.html">Python documentation</a>
+    <li><a href="coverage_report/index.html">Test coverage report</a>
+        (in regards to
+        <a href="devguide/_build/html/coverage.html">increasing test coverage</a>)
+    <li><a href="peps/pep-0000.html"><acronym XXX>PEPs</acronym></a>
+        (only read if necessary)
+</ul>
+
+<h1>Scripts</h1>
+<ul>
+    <li><a href="build_cpython.py">build_cpython.py</a>
+        (find the CPython binary & build if possible)
+    <li><a href="full_coverage.py">full_coverage.py</a>
+        (build, run, and/or report for coverage.py; see help message for usage)
+</ul>
\ No newline at end of file
diff --git a/make_a_box.py b/make_a_box.py
deleted file mode 100755
--- a/make_a_box.py
+++ /dev/null
@@ -1,362 +0,0 @@
-#!/usr/bin/env python3
-"""Python-Dev In a Box: everything you need to contribute to Python in under
-700 MB.
-
-This script will clone, checkout, or (ask you to) download almost everything
-you need to contribute to (C)Python's development. It will also "build"
-everything so that users do not need to do **everything** from scratch. This
-also allows for easy offline use.
-
-Beyond this script itself, there are other scripts provided along side this one
-to help in executing common tasks.
-
-"""
-import abc
-import contextlib
-import datetime
-from distutils.version import LooseVersion as Version
-import operator
-import os
-import os.path
-import shutil
-import subprocess
-import sys
-import urllib.request
-import urllib.parse
-import webbrowser
-import xmlrpc.client
-import build_cpython
-
-
-def rename(new_name):
-    """Decorator to rename an object that defines __name__."""
-    def do_rename(ob):
-        ob.__name__ = new_name
-        return ob
-    return do_rename
-
-
- at contextlib.contextmanager
-def change_cwd(directory):
-    cwd = os.getcwd()
-    os.chdir(directory)
-    try:
-        yield
-    finally:
-        os.chdir(cwd)
-
-
-class Provider(metaclass=abc.ABCMeta):
-
-    """Base class for items to be provided."""
-
-    @abc.abstractproperty
-    def directory(self):
-        """Directory to put everything."""
-        raise NotImplementedError
-
-    @abc.abstractproperty
-    def size(self):
-        """Size of what is provided (built and everything)."""
-        raise NotImplementedError
-
-    def _prompt(self, message):
-        """Prompt the user to perform an action, waiting for a response."""
-        input("{} [press Enter when done]".format(message))
-
-    @abc.abstractmethod
-    def create(self):
-        """Create what is to be provided."""
-        raise NotImplementedError
-
-    def build(self):
-        """Optional step to "build" something."""
-
-
-class HgProvider(Provider):
-
-    """Provide an Hg repository."""
-
-    @abc.abstractproperty
-    def url(self):
-        """Location of the repository."""
-        raise NotImplementedError
-
-    @property
-    def directory(self):
-        return os.path.abspath(os.path.basename(self.url))
-
-    def create(self):
-        """Clone an Hg repository to 'directory'."""
-        if os.path.exists(self.directory):
-            subprocess.check_call(['hg', 'pull', '-u', self.url],
-                                  cwd=self.directory)
-        else:
-            subprocess.check_call(['hg', 'clone', self.url, self.directory])
-
-
- at rename('Visual C++ Express')
-class VisualCPPExpress(Provider):
-
-    """The Web installer for Visual C++ Express"""
-
-    size = 4
-    directory = 'Visual C++ Express'
-
-    def create(self):
-        """Bring up a browser window to download the release."""
-        try:
-            os.mkdir(self.directory)
-        except OSError:
-            pass
-        url = 'http://www.microsoft.com/express/Downloads/'
-        try:
-            webbrowser.open(url)
-        except webbrowser.Error:
-            pass
-        self._prompt('download Visual C++ Express at {} and put in {}'.format(
-                        url, self.directory))
-
-
- at rename('coverage.py')
-class CoveragePy(HgProvider):
-
-    """Clone of coverage.py (WARNING: building takes a while)"""
-
-    url = 'https://bitbucket.org/ned/coveragepy'
-    size = 133  # Includes the coverage report
-    docs = os.path.join('coverage_report', 'index.html')
-
-    @contextlib.contextmanager
-    def build_cpython(self):
-        self.executable = build_cpython.main()
-        if not self.executable:
-            print('No CPython executable found')
-            sys.exit(1)
-        self.cpython_dir = os.path.dirname(self.executable)
-        yield
-        print('Cleaning up the CPython build ...')
-        with change_cwd(self.cpython_dir):
-            subprocess.check_call(['make', 'distclean'])
-
-    @contextlib.contextmanager
-    def build_coveragepy(self):
-        env = os.environ.copy()
-        env['CPPFLAGS'] = '-I {} -I {}'.format(self.cpython_dir,
-                                     os.path.join(self.cpython_dir, 'Include'))
-        with change_cwd(self.coveragepy_dir):
-            # Don't want to use venv because we want the file paths to match
-            # up with the repo and not installation locations.
-            print('Compiling coverage.py extension(s) ...')
-            cmd = [self.executable, 'setup.py', 'build_ext', '--inplace']
-            subprocess.check_call(cmd, env=env)
-            yield
-            print('Cleaning up coverage.py build ...')
-            shutil.rmtree('build')
-            os.unlink(os.path.join('coverage', 'tracer.so'))
-
-    def generate_coveragepy_command(self, command, *args):
-        exclude = '{},{}'.format(os.path.join('Lib', 'test', '*'),
-                                 os.path.join('Lib', '*', 'tests', '*'))
-        return [self.executable, self.coveragepy_dir, command,
-                '--include', os.path.join(self.cpython_dir, 'Lib', '*'),
-                '--omit', exclude] + list(args)
-
-    @contextlib.contextmanager
-    def run_coveragepy(self):
-        print('Running coverage ...')
-        regrtest_path = os.path.join(self.cpython_dir, 'Lib', 'test',
-                                     'regrtest.py')
-        fullcoverage = os.path.join(self.coveragepy_dir, 'coverage',
-                                    'fullcoverage')
-        env = os.environ.copy()
-        env['PYTHONPATH'] = fullcoverage
-        cmd = self.generate_coveragepy_command('run', '--pylib', regrtest_path)
-        subprocess.check_call(cmd, env=env)
-        yield
-        os.unlink('.coverage')
-
-    def report_coveragepy(self):
-        print('Generating report ...')
-        cmd = self.generate_coveragepy_command('html', '-i', '-d',
-                                os.path.join(self.root_dir, 'coverage_report'))
-        subprocess.check_call(cmd)
-
-    def build(self):
-        """Run coverage over CPython."""
-        self.coveragepy_dir = self.directory
-        self.root_dir = os.path.dirname(self.directory)
-        with self.build_cpython(), self.build_coveragepy():
-            with change_cwd(self.cpython_dir):
-                with self.run_coveragepy():
-                    self.report_coveragepy()
-
-
-class Mercurial(Provider):
-
-    """Source release of Mercurial along with TortoiseHg"""
-
-    directory = 'Mercurial'
-    size = 47  # Includes TortoiseHg for 32/64-bit Windows
-
-    def _download_url(self):
-        """Discover the URL to download Mercurial from."""
-        pypi = xmlrpc.client.ServerProxy('http://pypi.python.org/pypi')
-        hg_versions = pypi.package_releases('Mercurial')
-        hg_versions.sort(key=Version)
-        latest_release = hg_versions[-1]
-        # Mercurial keeps releases on their servers
-        release_data = pypi.release_data('Mercurial', latest_release)
-        fname = "mercurial-%s.tar.gz" % latest_release
-        try:
-            release_url = release_data['download_url']
-        except KeyError:
-            print('Mercurial has changed how it releases software on PyPI; '
-                  'please report this to bugs.python.org')
-            sys.exit(1)
-        return os.path.join(release_url, fname)
-
-    def _url_filename(self, url):
-        """Find the filename from the URL."""
-        return os.path.split(urllib.parse.urlparse(url).path)[1]
-
-    def _create_mercurial(self):
-        """Download the latest source release of Mercurial."""
-        file_url = self._download_url()
-        file_name = self._url_filename(file_url)
-        with urllib.request.urlopen(file_url) as url_file:
-            with open(os.path.join(self.directory, file_name), 'wb') as file:
-                file.write(url_file.read())
-
-    def _create_tortoisehg(self):
-        """Open a web page to the TortoiseHg download page."""
-        url = 'http://tortoisehg.bitbucket.org/download/'
-        try:
-            webbrowser.open(url)
-        except webbrowser.Error:
-            pass
-        self._prompt('Download TortoiseHg from {} and put in {}'.format(url,
-                         self.directory))
-
-    def create(self):
-        """Fetch the latest source distribution for Mercurial."""
-        try:
-            os.mkdir(self.directory)
-        except OSError:
-            pass
-        self._create_mercurial()
-        self._create_tortoisehg()
-
-
-class PEPs(HgProvider):
-
-    """Checkout of the Python Enhancement Proposals (for PEPs 7 & 8)"""
-
-    url = 'http://hg.python.org/peps'
-    size = 20
-    docs = os.path.join('peps', 'pep-0000.html')
-
-    def build(self):
-        """Build the PEPs."""
-        with change_cwd(self.directory):
-            subprocess.check_call(['make'])
-
-
-class Devguide(HgProvider):
-
-    """Clone of the Python Developer's Guide"""
-
-    size = 4
-
-    url = 'http://hg.python.org/devguide'
-    docs = os.path.join('devguide', '_build', 'html', 'index.html')
-
-    def build(self):
-        """Build the devguide using Sphinx from CPython's docs."""
-        # Grab Sphinx from cpython/Doc/tools/
-        tools_directory = os.path.join('cpython', 'Doc', 'tools')
-        sphinx_build_path = os.path.join(tools_directory, 'sphinx-build.py')
-        sphinx_build_path = os.path.abspath(sphinx_build_path)
-        orig_pythonpath = os.environ.get('PYTHONPATH')
-        os.environ['PYTHONPATH'] = os.path.abspath(tools_directory)
-        orig_sphinxbuild = os.environ.get('SPHINXBUILD')
-        os.environ['SPHINXBUILD'] = 'python {}'.format(sphinx_build_path)
-        try:
-            with change_cwd(self.directory):
-                subprocess.check_call(['make', 'html'])
-        finally:
-            if orig_pythonpath:
-                os.environ['PYTHONPATH'] = orig_pythonpath
-            if orig_sphinxbuild:
-                os.environ['SPHINXBUILD'] = orig_sphinxbuild
-        index_path = os.path.join(self.directory, '_build', 'html',
-                                  'index.html')
-
-
-class CPython(HgProvider):
-
-    """Clone of CPython (and requisite tools to build the documentation)"""
-
-    url = 'http://hg.python.org/cpython'
-    size = 330  # Only docs are built
-    docs = os.path.join('cpython', 'Doc', 'build', 'html', 'index.html')
-
-    def create(self):
-        """Clone CPython and get the necessary tools to build the
-        documentation."""
-        super().create()
-        with change_cwd(os.path.join(self.directory, 'Doc')):
-            subprocess.check_call(['make', 'checkout'])
-
-    def build(self):
-        """Build CPython's documentation.
-
-        CPython itself is not built as one will most likely not want to
-        distribute that on a CD. The build_cpython.py script can be used by the
-        Box user for building CPython.
-
-        """
-        cmd = 'make' if sys.platform != 'win32' else 'make.bat'
-        with change_cwd(os.path.join(self.directory, 'Doc')):
-                subprocess.check_call([cmd, 'html'])
-
-
-if __name__ == '__main__':
-    # XXX CLI: create, build, list
-    all_providers = (CPython, Devguide, PEPs, CoveragePy, Mercurial,
-                     VisualCPPExpress)
-    print(__doc__)
-    print('Please choose what to provide [answer y/n]:\n')
-    desired_providers = []
-    for provider in all_providers:
-        docstring = provider.__doc__#.replace('\n', ' ')
-        msg = '{} ({} MB built)? '.format(docstring, provider.size)
-        response = input(msg)
-        if response in ('Y', 'y'):
-            desired_providers.append(provider)
-    print()
-    getting = ', '.join(map(operator.attrgetter('__name__'),
-                            desired_providers))
-    print('Getting {}'.format(getting))
-    total_size = sum(map(operator.attrgetter('size'), desired_providers))
-    msg = 'The requested Box will be about {} MB. OK? [y/n] '.format(total_size)
-    response = input(msg)
-    if response not in ('Y', 'y'):
-        sys.exit(0)
-    else:
-        print()
-        for provider in desired_providers:
-            ins = provider()
-            print('Fetching {} ...'.format(provider.__name__))
-            ins.create()
-            print('Building {} ...'.format(provider.__name__))
-            ins.build()
-    with open('README', 'w') as file:
-        header = 'Python-Dev In a Box: created on {}\n'
-        file.write(header.format(datetime.date.today()))
-        file.write('\n')
-        file.write('Documentation indices can be found at:\n')
-        for provider in desired_providers:
-            if hasattr(provider, 'docs'):
-                file.write('  {}\n'.format(provider.docs))
-    sys.exit(0)

-- 
Repository URL: http://hg.python.org/devinabox


More information about the Python-checkins mailing list