1#!/usr/bin/env python
2
3"""
4Setuptools bootstrapping installer.
5
6Maintained at https://github.com/pypa/setuptools/tree/bootstrap.
7
8Run this script to install or upgrade setuptools.
9
10This method is DEPRECATED. Check https://github.com/pypa/setuptools/issues/581 for more details.
11"""
12
13import os
14import shutil
15import sys
16import tempfile
17import zipfile
18import optparse
19import subprocess
20import platform
21import textwrap
22import contextlib
23
24from distutils import log
25
26try:
27    from urllib.request import urlopen
28except ImportError:
29    from urllib2 import urlopen
30
31try:
32    from site import USER_SITE
33except ImportError:
34    USER_SITE = None
35
36# 33.1.1 is the last version that supports setuptools self upgrade/installation.
37#DEFAULT_VERSION = "33.1.1"
38DEFAULT_VERSION = "41.0.1"
39DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/"
40DEFAULT_SAVE_DIR = os.curdir
41DEFAULT_DEPRECATION_MESSAGE = "ez_setup.py is deprecated and when using it setuptools will be pinned to {0} since it's the last version that supports setuptools self upgrade/installation, check https://github.com/pypa/setuptools/issues/581 for more info; use pip to install setuptools"
42
43MEANINGFUL_INVALID_ZIP_ERR_MSG = 'Maybe {0} is corrupted, delete it and try again.'
44
45log.warn(DEFAULT_DEPRECATION_MESSAGE.format(DEFAULT_VERSION))
46
47
48def _python_cmd(*args):
49    """
50    Execute a command.
51
52    Return True if the command succeeded.
53    """
54    args = (sys.executable,) + args
55    return subprocess.call(args) == 0
56
57
58def _install(archive_filename, install_args=()):
59    """Install Setuptools."""
60    with archive_context(archive_filename):
61        # installing
62        log.warn('Installing Setuptools')
63        if not _python_cmd('setup.py', 'install', *install_args):
64            log.warn('Something went wrong during the installation.')
65            log.warn('See the error message above.')
66            # exitcode will be 2
67            return 2
68
69
70def _build_egg(egg, archive_filename, to_dir):
71    """Build Setuptools egg."""
72    with archive_context(archive_filename):
73        # building an egg
74        log.warn('Building a Setuptools egg in %s', to_dir)
75        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
76    # returning the result
77    log.warn(egg)
78    if not os.path.exists(egg):
79        raise IOError('Could not build the egg.')
80
81
82class ContextualZipFile(zipfile.ZipFile):
83
84    """Supplement ZipFile class to support context manager for Python 2.6."""
85
86    def __enter__(self):
87        return self
88
89    def __exit__(self, type, value, traceback):
90        self.close()
91
92    def __new__(cls, *args, **kwargs):
93        """Construct a ZipFile or ContextualZipFile as appropriate."""
94        if hasattr(zipfile.ZipFile, '__exit__'):
95            return zipfile.ZipFile(*args, **kwargs)
96        return super(ContextualZipFile, cls).__new__(cls)
97
98
99@contextlib.contextmanager
100def archive_context(filename):
101    """
102    Unzip filename to a temporary directory, set to the cwd.
103
104    The unzipped target is cleaned up after.
105    """
106    tmpdir = tempfile.mkdtemp()
107    log.warn('Extracting in %s', tmpdir)
108    old_wd = os.getcwd()
109    try:
110        os.chdir(tmpdir)
111        try:
112            with ContextualZipFile(filename) as archive:
113                archive.extractall()
114        except zipfile.BadZipfile as err:
115            if not err.args:
116                err.args = ('', )
117            err.args = err.args + (
118                MEANINGFUL_INVALID_ZIP_ERR_MSG.format(filename),
119            )
120            raise
121
122        # going in the directory
123        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
124        os.chdir(subdir)
125        log.warn('Now working in %s', subdir)
126        yield
127
128    finally:
129        os.chdir(old_wd)
130        shutil.rmtree(tmpdir)
131
132
133def _do_download(version, download_base, to_dir, download_delay):
134    """Download Setuptools."""
135    py_desig = 'py{sys.version_info[0]}.{sys.version_info[1]}'.format(sys=sys)
136    tp = 'setuptools-{version}-{py_desig}.egg'
137    egg = os.path.join(to_dir, tp.format(**locals()))
138    if not os.path.exists(egg):
139        archive = download_setuptools(version, download_base,
140            to_dir, download_delay)
141        _build_egg(egg, archive, to_dir)
142    sys.path.insert(0, egg)
143
144    # Remove previously-imported pkg_resources if present (see
145    # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
146    if 'pkg_resources' in sys.modules:
147        _unload_pkg_resources()
148
149    import setuptools
150    setuptools.bootstrap_install_from = egg
151
152
153def use_setuptools(
154        version=DEFAULT_VERSION, download_base=DEFAULT_URL,
155        to_dir=DEFAULT_SAVE_DIR, download_delay=15):
156    """
157    Ensure that a setuptools version is installed.
158
159    Return None. Raise SystemExit if the requested version
160    or later cannot be installed.
161    """
162    to_dir = os.path.abspath(to_dir)
163
164    # prior to importing, capture the module state for
165    # representative modules.
166    rep_modules = 'pkg_resources', 'setuptools'
167    imported = set(sys.modules).intersection(rep_modules)
168
169    try:
170        import pkg_resources
171        pkg_resources.require("setuptools>=" + version)
172        # a suitable version is already installed
173        return
174    except ImportError:
175        # pkg_resources not available; setuptools is not installed; download
176        pass
177    except pkg_resources.DistributionNotFound:
178        # no version of setuptools was found; allow download
179        pass
180    except pkg_resources.VersionConflict as VC_err:
181        if imported:
182            _conflict_bail(VC_err, version)
183
184        # otherwise, unload pkg_resources to allow the downloaded version to
185        #  take precedence.
186        del pkg_resources
187        _unload_pkg_resources()
188
189    return _do_download(version, download_base, to_dir, download_delay)
190
191
192def _conflict_bail(VC_err, version):
193    """
194    Setuptools was imported prior to invocation, so it is
195    unsafe to unload it. Bail out.
196    """
197    conflict_tmpl = textwrap.dedent("""
198        The required version of setuptools (>={version}) is not available,
199        and can't be installed while this script is running. Please
200        install a more recent version first, using
201        'easy_install -U setuptools'.
202
203        (Currently using {VC_err.args[0]!r})
204        """)
205    msg = conflict_tmpl.format(**locals())
206    sys.stderr.write(msg)
207    sys.exit(2)
208
209
210def _unload_pkg_resources():
211    sys.meta_path = [
212        importer
213        for importer in sys.meta_path
214        if importer.__class__.__module__ != 'pkg_resources.extern'
215    ]
216    del_modules = [
217        name for name in sys.modules
218        if name.startswith('pkg_resources')
219    ]
220    for mod_name in del_modules:
221        del sys.modules[mod_name]
222
223
224def _clean_check(cmd, target):
225    """
226    Run the command to download target.
227
228    If the command fails, clean up before re-raising the error.
229    """
230    try:
231        subprocess.check_call(cmd)
232    except subprocess.CalledProcessError:
233        if os.access(target, os.F_OK):
234            os.unlink(target)
235        raise
236
237
238def download_file_powershell(url, target):
239    """
240    Download the file at url to target using Powershell.
241
242    Powershell will validate trust.
243    Raise an exception if the command cannot complete.
244    """
245    target = os.path.abspath(target)
246    ps_cmd = (
247        "[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
248        "[System.Net.CredentialCache]::DefaultCredentials; "
249        '(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")'
250        % locals()
251    )
252    cmd = [
253        'powershell',
254        '-Command',
255        ps_cmd,
256    ]
257    _clean_check(cmd, target)
258
259
260def has_powershell():
261	# on win8 powershell is mulfunction for downloading
262    return False
263    """Determine if Powershell is available."""
264    if platform.system() != 'Windows':
265        return False
266    cmd = ['powershell', '-Command', 'echo test']
267    with open(os.path.devnull, 'wb') as devnull:
268        try:
269            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
270        except Exception:
271            return False
272    return True
273download_file_powershell.viable = has_powershell
274
275
276def download_file_curl(url, target):
277    cmd = ['curl', url, '--location', '--silent', '--output', target]
278    _clean_check(cmd, target)
279
280
281def has_curl():
282    cmd = ['curl', '--version']
283    with open(os.path.devnull, 'wb') as devnull:
284        try:
285            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
286        except Exception:
287            return False
288    return True
289download_file_curl.viable = has_curl
290
291
292def download_file_wget(url, target):
293    cmd = ['wget', url, '--quiet', '--output-document', target]
294    _clean_check(cmd, target)
295
296
297def has_wget():
298    cmd = ['wget', '--version']
299    with open(os.path.devnull, 'wb') as devnull:
300        try:
301            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
302        except Exception:
303            return False
304    return True
305download_file_wget.viable = has_wget
306
307
308def download_file_insecure(url, target):
309    """Use Python to download the file, without connection authentication."""
310    src = urlopen(url)
311    try:
312        # Read all the data in one block.
313        data = src.read()
314    finally:
315        src.close()
316
317    # Write all the data in one block to avoid creating a partial file.
318    with open(target, "wb") as dst:
319        dst.write(data)
320download_file_insecure.viable = lambda: True
321
322
323def get_best_downloader():
324    downloaders = (
325        download_file_powershell,
326        download_file_curl,
327        download_file_wget,
328        download_file_insecure,
329    )
330    viable_downloaders = (dl for dl in downloaders if dl.viable())
331    return next(viable_downloaders, None)
332
333
334def download_setuptools(
335        version=DEFAULT_VERSION, download_base=DEFAULT_URL,
336        to_dir=DEFAULT_SAVE_DIR, delay=15,
337        downloader_factory=get_best_downloader):
338    """
339    Download setuptools from a specified location and return its filename.
340
341    `version` should be a valid setuptools version number that is available
342    as an sdist for download under the `download_base` URL (which should end
343    with a '/'). `to_dir` is the directory where the egg will be downloaded.
344    `delay` is the number of seconds to pause before an actual download
345    attempt.
346
347    ``downloader_factory`` should be a function taking no arguments and
348    returning a function for downloading a URL to a target.
349    """
350    # making sure we use the absolute path
351    to_dir = os.path.abspath(to_dir)
352    zip_name = "setuptools-%s.zip" % version
353    url = download_base + zip_name
354    saveto = os.path.join(to_dir, zip_name)
355    if not os.path.exists(saveto):  # Avoid repeated downloads
356        log.warn("Downloading %s", url)
357        downloader = downloader_factory()
358        downloader(url, saveto)
359    return os.path.realpath(saveto)
360
361
362def _build_install_args(options):
363    """
364    Build the arguments to 'python setup.py install' on the setuptools package.
365
366    Returns list of command line arguments.
367    """
368    return ['--user'] if options.user_install else []
369
370
371def _parse_args():
372    """Parse the command line for options."""
373    parser = optparse.OptionParser()
374    parser.add_option(
375        '--user', dest='user_install', action='store_true', default=False,
376        help='install in user site package')
377    parser.add_option(
378        '--download-base', dest='download_base', metavar="URL",
379        default=DEFAULT_URL,
380        help='alternative URL from where to download the setuptools package')
381    parser.add_option(
382        '--insecure', dest='downloader_factory', action='store_const',
383        const=lambda: download_file_insecure, default=get_best_downloader,
384        help='Use internal, non-validating downloader'
385    )
386    parser.add_option(
387        '--version', help="Specify which version to download",
388        default=DEFAULT_VERSION,
389    )
390    parser.add_option(
391        '--to-dir',
392        help="Directory to save (and re-use) package",
393        default=DEFAULT_SAVE_DIR,
394    )
395    options, args = parser.parse_args()
396    # positional arguments are ignored
397    return options
398
399
400def _download_args(options):
401    """Return args for download_setuptools function from cmdline args."""
402    return dict(
403        version=options.version,
404        download_base=options.download_base,
405        downloader_factory=options.downloader_factory,
406        to_dir=options.to_dir,
407    )
408
409
410def main():
411    """Install or upgrade setuptools and EasyInstall."""
412    options = _parse_args()
413    archive = download_setuptools(**_download_args(options))
414    return _install(archive, _build_install_args(options))
415
416if __name__ == '__main__':
417    sys.exit(main())
418