Home | History | Annotate | Download | only in apitools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2015 Google Inc.
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """Bootstrap setuptools installation
     18 
     19 If you want to use setuptools in your package's setup.py, just include this
     20 file in the same directory with it, and add this to the top of your setup.py::
     21 
     22     from ez_setup import use_setuptools
     23     use_setuptools()
     24 
     25 If you want to require a specific version of setuptools, set a download
     26 mirror, or use an alternate download directory, you can do so by supplying
     27 the appropriate options to ``use_setuptools()``.
     28 
     29 This file can also be run as a script to install or upgrade setuptools.
     30 """
     31 import sys
     32 DEFAULT_VERSION = "0.6c11"
     33 DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
     34 
     35 md5_data = {
     36     'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
     37     'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
     38     'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
     39     'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
     40     'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
     41     'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
     42     'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
     43     'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
     44     'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
     45     'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
     46     'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
     47     'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
     48     'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
     49     'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
     50     'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
     51 }
     52 
     53 import sys, os
     54 try: from hashlib import md5
     55 except ImportError: from md5 import md5
     56 
     57 def _validate_md5(egg_name, data):
     58     if egg_name in md5_data:
     59         digest = md5(data).hexdigest()
     60         if digest != md5_data[egg_name]:
     61             print >>sys.stderr, (
     62                 "md5 validation of %s failed!  (Possible download problem?)"
     63                 % egg_name
     64             )
     65             sys.exit(2)
     66     return data
     67 
     68 def use_setuptools(
     69     version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
     70     download_delay=15
     71 ):
     72     """Automatically find/download setuptools and make it available on sys.path
     73 
     74     `version` should be a valid setuptools version number that is available
     75     as an egg for download under the `download_base` URL (which should end with
     76     a '/').  `to_dir` is the directory where setuptools will be downloaded, if
     77     it is not already available.  If `download_delay` is specified, it should
     78     be the number of seconds that will be paused before initiating a download,
     79     should one be required.  If an older version of setuptools is installed,
     80     this routine will print a message to ``sys.stderr`` and raise SystemExit in
     81     an attempt to abort the calling script.
     82     """
     83     was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
     84     def do_download():
     85         egg = download_setuptools(version, download_base, to_dir, download_delay)
     86         sys.path.insert(0, egg)
     87         import setuptools; setuptools.bootstrap_install_from = egg
     88     try:
     89         import pkg_resources
     90     except ImportError:
     91         return do_download()       
     92     try:
     93         pkg_resources.require("setuptools>="+version); return
     94     except pkg_resources.VersionConflict, e:
     95         if was_imported:
     96             print >>sys.stderr, (
     97             "The required version of setuptools (>=%s) is not available, and\n"
     98             "can't be installed while this script is running. Please install\n"
     99             " a more recent version first, using 'easy_install -U setuptools'."
    100             "\n\n(Currently using %r)"
    101             ) % (version, e.args[0])
    102             sys.exit(2)
    103     except pkg_resources.DistributionNotFound:
    104         pass
    105 
    106     del pkg_resources, sys.modules['pkg_resources']    # reload ok
    107     return do_download()
    108 
    109 def download_setuptools(
    110     version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
    111     delay = 15
    112 ):
    113     """Download setuptools from a specified location and return its filename
    114 
    115     `version` should be a valid setuptools version number that is available
    116     as an egg for download under the `download_base` URL (which should end
    117     with a '/'). `to_dir` is the directory where the egg will be downloaded.
    118     `delay` is the number of seconds to pause before an actual download attempt.
    119     """
    120     import urllib2, shutil
    121     egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
    122     url = download_base + egg_name
    123     saveto = os.path.join(to_dir, egg_name)
    124     src = dst = None
    125     if not os.path.exists(saveto):  # Avoid repeated downloads
    126         try:
    127             from distutils import log
    128             if delay:
    129                 log.warn("""
    130 ---------------------------------------------------------------------------
    131 This script requires setuptools version %s to run (even to display
    132 help).  I will attempt to download it for you (from
    133 %s), but
    134 you may need to enable firewall access for this script first.
    135 I will start the download in %d seconds.
    136 
    137 (Note: if this machine does not have network access, please obtain the file
    138 
    139    %s
    140 
    141 and place it in this directory before rerunning this script.)
    142 ---------------------------------------------------------------------------""",
    143                     version, download_base, delay, url
    144                 ); from time import sleep; sleep(delay)
    145             log.warn("Downloading %s", url)
    146             src = urllib2.urlopen(url)
    147             # Read/write all in one block, so we don't create a corrupt file
    148             # if the download is interrupted.
    149             data = _validate_md5(egg_name, src.read())
    150             dst = open(saveto,"wb"); dst.write(data)
    151         finally:
    152             if src: src.close()
    153             if dst: dst.close()
    154     return os.path.realpath(saveto)
    155 
    156 
    157 
    158 
    159 
    160 
    161 
    162 
    163 
    164 
    165 
    166 
    167 
    168 
    169 
    170 
    171 
    172 
    173 
    174 
    175 
    176 
    177 
    178 
    179 
    180 
    181 
    182 
    183 
    184 
    185 
    186 
    187 
    188 
    189 
    190 
    191 def main(argv, version=DEFAULT_VERSION):
    192     """Install or upgrade setuptools and EasyInstall"""
    193     try:
    194         import setuptools
    195     except ImportError:
    196         egg = None
    197         try:
    198             egg = download_setuptools(version, delay=0)
    199             sys.path.insert(0,egg)
    200             from setuptools.command.easy_install import main
    201             return main(list(argv)+[egg])   # we're done here
    202         finally:
    203             if egg and os.path.exists(egg):
    204                 os.unlink(egg)
    205     else:
    206         if setuptools.__version__ == '0.0.1':
    207             print >>sys.stderr, (
    208             "You have an obsolete version of setuptools installed.  Please\n"
    209             "remove it from your system entirely before rerunning this script."
    210             )
    211             sys.exit(2)
    212 
    213     req = "setuptools>="+version
    214     import pkg_resources
    215     try:
    216         pkg_resources.require(req)
    217     except pkg_resources.VersionConflict:
    218         try:
    219             from setuptools.command.easy_install import main
    220         except ImportError:
    221             from easy_install import main
    222         main(list(argv)+[download_setuptools(delay=0)])
    223         sys.exit(0) # try to force an exit
    224     else:
    225         if argv:
    226             from setuptools.command.easy_install import main
    227             main(argv)
    228         else:
    229             print "Setuptools version",version,"or greater has been installed."
    230             print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
    231 
    232 def update_md5(filenames):
    233     """Update our built-in md5 registry"""
    234 
    235     import re
    236 
    237     for name in filenames:
    238         base = os.path.basename(name)
    239         f = open(name,'rb')
    240         md5_data[base] = md5(f.read()).hexdigest()
    241         f.close()
    242 
    243     data = ["    %r: %r,\n" % it for it in md5_data.items()]
    244     data.sort()
    245     repl = "".join(data)
    246 
    247     import inspect
    248     srcfile = inspect.getsourcefile(sys.modules[__name__])
    249     f = open(srcfile, 'rb'); src = f.read(); f.close()
    250 
    251     match = re.search("\nmd5_data = {\n([^}]+)}", src)
    252     if not match:
    253         print >>sys.stderr, "Internal error!"
    254         sys.exit(2)
    255 
    256     src = src[:match.start(1)] + repl + src[match.end(1):]
    257     f = open(srcfile,'w')
    258     f.write(src)
    259     f.close()
    260 
    261 
    262 if __name__=='__main__':
    263     if len(sys.argv)>2 and sys.argv[1]=='--md5update':
    264         update_md5(sys.argv[2:])
    265     else:
    266         main(sys.argv[1:])
    267