Update TwitterFeed cms content type
[snf-cloudcms] / distribute_setup.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34
35 #!python
36 """Bootstrap distribute installation
37
38 If you want to use setuptools in your package's setup.py, just include this
39 file in the same directory with it, and add this to the top of your setup.py::
40
41     from distribute_setup import use_setuptools
42     use_setuptools()
43
44 If you want to require a specific version of setuptools, set a download
45 mirror, or use an alternate download directory, you can do so by supplying
46 the appropriate options to ``use_setuptools()``.
47
48 This file can also be run as a script to install or upgrade setuptools.
49 """
50 import os
51 import sys
52 import time
53 import fnmatch
54 import tempfile
55 import tarfile
56 from distutils import log
57
58 try:
59     from site import USER_SITE
60 except ImportError:
61     USER_SITE = None
62
63 try:
64     import subprocess
65
66     def _python_cmd(*args):
67         args = (sys.executable,) + args
68         return subprocess.call(args) == 0
69
70 except ImportError:
71     # will be used for python 2.3
72     def _python_cmd(*args):
73         args = (sys.executable,) + args
74         # quoting arguments if windows
75         if sys.platform == 'win32':
76             def quote(arg):
77                 if ' ' in arg:
78                     return '"%s"' % arg
79                 return arg
80             args = [quote(arg) for arg in args]
81         return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
82
83 DEFAULT_VERSION = "0.6.10"
84 DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
85 SETUPTOOLS_FAKED_VERSION = "0.6c11"
86
87 SETUPTOOLS_PKG_INFO = """\
88 Metadata-Version: 1.0
89 Name: setuptools
90 Version: %s
91 Summary: xxxx
92 Home-page: xxx
93 Author: xxx
94 Author-email: xxx
95 License: xxx
96 Description: xxx
97 """ % SETUPTOOLS_FAKED_VERSION
98
99
100 def _install(tarball):
101     # extracting the tarball
102     tmpdir = tempfile.mkdtemp()
103     log.warn('Extracting in %s', tmpdir)
104     old_wd = os.getcwd()
105     try:
106         os.chdir(tmpdir)
107         tar = tarfile.open(tarball)
108         _extractall(tar)
109         tar.close()
110
111         # going in the directory
112         subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
113         os.chdir(subdir)
114         log.warn('Now working in %s', subdir)
115
116         # installing
117         log.warn('Installing Distribute')
118         if not _python_cmd('setup.py', 'install'):
119             log.warn('Something went wrong during the installation.')
120             log.warn('See the error message above.')
121     finally:
122         os.chdir(old_wd)
123
124
125 def _build_egg(egg, tarball, to_dir):
126     # extracting the tarball
127     tmpdir = tempfile.mkdtemp()
128     log.warn('Extracting in %s', tmpdir)
129     old_wd = os.getcwd()
130     try:
131         os.chdir(tmpdir)
132         tar = tarfile.open(tarball)
133         _extractall(tar)
134         tar.close()
135
136         # going in the directory
137         subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
138         os.chdir(subdir)
139         log.warn('Now working in %s', subdir)
140
141         # building an egg
142         log.warn('Building a Distribute egg in %s', to_dir)
143         _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
144
145     finally:
146         os.chdir(old_wd)
147     # returning the result
148     log.warn(egg)
149     if not os.path.exists(egg):
150         raise IOError('Could not build the egg.')
151
152
153 def _do_download(version, download_base, to_dir, download_delay):
154     egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
155                        % (version, sys.version_info[0], sys.version_info[1]))
156     if not os.path.exists(egg):
157         tarball = download_setuptools(version, download_base,
158                                       to_dir, download_delay)
159         _build_egg(egg, tarball, to_dir)
160     sys.path.insert(0, egg)
161     import setuptools
162     setuptools.bootstrap_install_from = egg
163
164
165 def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
166                    to_dir=os.curdir, download_delay=15, no_fake=True):
167     # making sure we use the absolute path
168     to_dir = os.path.abspath(to_dir)
169     was_imported = 'pkg_resources' in sys.modules or \
170         'setuptools' in sys.modules
171     try:
172         try:
173             import pkg_resources
174             if not hasattr(pkg_resources, '_distribute'):
175                 if not no_fake:
176                     _fake_setuptools()
177                 raise ImportError
178         except ImportError:
179             return _do_download(version, download_base, to_dir, download_delay)
180         try:
181             pkg_resources.require("distribute>="+version)
182             return
183         except pkg_resources.VersionConflict:
184             e = sys.exc_info()[1]
185             if was_imported:
186                 sys.stderr.write(
187                 "The required version of distribute (>=%s) is not available,\n"
188                 "and can't be installed while this script is running. Please\n"
189                 "install a more recent version first, using\n"
190                 "'easy_install -U distribute'."
191                 "\n\n(Currently using %r)\n" % (version, e.args[0]))
192                 sys.exit(2)
193             else:
194                 del pkg_resources, sys.modules['pkg_resources']    # reload ok
195                 return _do_download(version, download_base, to_dir,
196                                     download_delay)
197         except pkg_resources.DistributionNotFound:
198             return _do_download(version, download_base, to_dir,
199                                 download_delay)
200     finally:
201         if not no_fake:
202             _create_fake_setuptools_pkg_info(to_dir)
203
204 def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
205                         to_dir=os.curdir, delay=15):
206     """Download distribute from a specified location and return its filename
207
208     `version` should be a valid distribute version number that is available
209     as an egg for download under the `download_base` URL (which should end
210     with a '/'). `to_dir` is the directory where the egg will be downloaded.
211     `delay` is the number of seconds to pause before an actual download
212     attempt.
213     """
214     # making sure we use the absolute path
215     to_dir = os.path.abspath(to_dir)
216     try:
217         from urllib.request import urlopen
218     except ImportError:
219         from urllib2 import urlopen
220     tgz_name = "distribute-%s.tar.gz" % version
221     url = download_base + tgz_name
222     saveto = os.path.join(to_dir, tgz_name)
223     src = dst = None
224     if not os.path.exists(saveto):  # Avoid repeated downloads
225         try:
226             log.warn("Downloading %s", url)
227             src = urlopen(url)
228             # Read/write all in one block, so we don't create a corrupt file
229             # if the download is interrupted.
230             data = src.read()
231             dst = open(saveto, "wb")
232             dst.write(data)
233         finally:
234             if src:
235                 src.close()
236             if dst:
237                 dst.close()
238     return os.path.realpath(saveto)
239
240 def _no_sandbox(function):
241     def __no_sandbox(*args, **kw):
242         try:
243             from setuptools.sandbox import DirectorySandbox
244             if not hasattr(DirectorySandbox, '_old'):
245                 def violation(*args):
246                     pass
247                 DirectorySandbox._old = DirectorySandbox._violation
248                 DirectorySandbox._violation = violation
249                 patched = True
250             else:
251                 patched = False
252         except ImportError:
253             patched = False
254
255         try:
256             return function(*args, **kw)
257         finally:
258             if patched:
259                 DirectorySandbox._violation = DirectorySandbox._old
260                 del DirectorySandbox._old
261
262     return __no_sandbox
263
264 def _patch_file(path, content):
265     """Will backup the file then patch it"""
266     existing_content = open(path).read()
267     if existing_content == content:
268         # already patched
269         log.warn('Already patched.')
270         return False
271     log.warn('Patching...')
272     _rename_path(path)
273     f = open(path, 'w')
274     try:
275         f.write(content)
276     finally:
277         f.close()
278     return True
279
280 _patch_file = _no_sandbox(_patch_file)
281
282 def _same_content(path, content):
283     return open(path).read() == content
284
285 def _rename_path(path):
286     new_name = path + '.OLD.%s' % time.time()
287     log.warn('Renaming %s into %s', path, new_name)
288     os.rename(path, new_name)
289     return new_name
290
291 def _remove_flat_installation(placeholder):
292     if not os.path.isdir(placeholder):
293         log.warn('Unkown installation at %s', placeholder)
294         return False
295     found = False
296     for file in os.listdir(placeholder):
297         if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
298             found = True
299             break
300     if not found:
301         log.warn('Could not locate setuptools*.egg-info')
302         return
303
304     log.warn('Removing elements out of the way...')
305     pkg_info = os.path.join(placeholder, file)
306     if os.path.isdir(pkg_info):
307         patched = _patch_egg_dir(pkg_info)
308     else:
309         patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
310
311     if not patched:
312         log.warn('%s already patched.', pkg_info)
313         return False
314     # now let's move the files out of the way
315     for element in ('setuptools', 'pkg_resources.py', 'site.py'):
316         element = os.path.join(placeholder, element)
317         if os.path.exists(element):
318             _rename_path(element)
319         else:
320             log.warn('Could not find the %s element of the '
321                      'Setuptools distribution', element)
322     return True
323
324 _remove_flat_installation = _no_sandbox(_remove_flat_installation)
325
326 def _after_install(dist):
327     log.warn('After install bootstrap.')
328     placeholder = dist.get_command_obj('install').install_purelib
329     _create_fake_setuptools_pkg_info(placeholder)
330
331 def _create_fake_setuptools_pkg_info(placeholder):
332     if not placeholder or not os.path.exists(placeholder):
333         log.warn('Could not find the install location')
334         return
335     pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
336     setuptools_file = 'setuptools-%s-py%s.egg-info' % \
337             (SETUPTOOLS_FAKED_VERSION, pyver)
338     pkg_info = os.path.join(placeholder, setuptools_file)
339     if os.path.exists(pkg_info):
340         log.warn('%s already exists', pkg_info)
341         return
342
343     log.warn('Creating %s', pkg_info)
344     f = open(pkg_info, 'w')
345     try:
346         f.write(SETUPTOOLS_PKG_INFO)
347     finally:
348         f.close()
349
350     pth_file = os.path.join(placeholder, 'setuptools.pth')
351     log.warn('Creating %s', pth_file)
352     f = open(pth_file, 'w')
353     try:
354         f.write(os.path.join(os.curdir, setuptools_file))
355     finally:
356         f.close()
357
358 _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
359
360 def _patch_egg_dir(path):
361     # let's check if it's already patched
362     pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
363     if os.path.exists(pkg_info):
364         if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
365             log.warn('%s already patched.', pkg_info)
366             return False
367     _rename_path(path)
368     os.mkdir(path)
369     os.mkdir(os.path.join(path, 'EGG-INFO'))
370     pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
371     f = open(pkg_info, 'w')
372     try:
373         f.write(SETUPTOOLS_PKG_INFO)
374     finally:
375         f.close()
376     return True
377
378 _patch_egg_dir = _no_sandbox(_patch_egg_dir)
379
380 def _before_install():
381     log.warn('Before install bootstrap.')
382     _fake_setuptools()
383
384
385 def _under_prefix(location):
386     if 'install' not in sys.argv:
387         return True
388     args = sys.argv[sys.argv.index('install')+1:]
389     for index, arg in enumerate(args):
390         for option in ('--root', '--prefix'):
391             if arg.startswith('%s=' % option):
392                 top_dir = arg.split('root=')[-1]
393                 return location.startswith(top_dir)
394             elif arg == option:
395                 if len(args) > index:
396                     top_dir = args[index+1]
397                     return location.startswith(top_dir)
398         if arg == '--user' and USER_SITE is not None:
399             return location.startswith(USER_SITE)
400     return True
401
402
403 def _fake_setuptools():
404     log.warn('Scanning installed packages')
405     try:
406         import pkg_resources
407     except ImportError:
408         # we're cool
409         log.warn('Setuptools or Distribute does not seem to be installed.')
410         return
411     ws = pkg_resources.working_set
412     try:
413         setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
414                                   replacement=False))
415     except TypeError:
416         # old distribute API
417         setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
418
419     if setuptools_dist is None:
420         log.warn('No setuptools distribution found')
421         return
422     # detecting if it was already faked
423     setuptools_location = setuptools_dist.location
424     log.warn('Setuptools installation detected at %s', setuptools_location)
425
426     # if --root or --preix was provided, and if
427     # setuptools is not located in them, we don't patch it
428     if not _under_prefix(setuptools_location):
429         log.warn('Not patching, --root or --prefix is installing Distribute'
430                  ' in another location')
431         return
432
433     # let's see if its an egg
434     if not setuptools_location.endswith('.egg'):
435         log.warn('Non-egg installation')
436         res = _remove_flat_installation(setuptools_location)
437         if not res:
438             return
439     else:
440         log.warn('Egg installation')
441         pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
442         if (os.path.exists(pkg_info) and
443             _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
444             log.warn('Already patched.')
445             return
446         log.warn('Patching...')
447         # let's create a fake egg replacing setuptools one
448         res = _patch_egg_dir(setuptools_location)
449         if not res:
450             return
451     log.warn('Patched done.')
452     _relaunch()
453
454
455 def _relaunch():
456     log.warn('Relaunching...')
457     # we have to relaunch the process
458     # pip marker to avoid a relaunch bug
459     if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
460         sys.argv[0] = 'setup.py'
461     args = [sys.executable] + sys.argv
462     sys.exit(subprocess.call(args))
463
464
465 def _extractall(self, path=".", members=None):
466     """Extract all members from the archive to the current working
467        directory and set owner, modification time and permissions on
468        directories afterwards. `path' specifies a different directory
469        to extract to. `members' is optional and must be a subset of the
470        list returned by getmembers().
471     """
472     import copy
473     import operator
474     from tarfile import ExtractError
475     directories = []
476
477     if members is None:
478         members = self
479
480     for tarinfo in members:
481         if tarinfo.isdir():
482             # Extract directories with a safe mode.
483             directories.append(tarinfo)
484             tarinfo = copy.copy(tarinfo)
485             tarinfo.mode = 448 # decimal for oct 0700
486         self.extract(tarinfo, path)
487
488     # Reverse sort directories.
489     if sys.version_info < (2, 4):
490         def sorter(dir1, dir2):
491             return cmp(dir1.name, dir2.name)
492         directories.sort(sorter)
493         directories.reverse()
494     else:
495         directories.sort(key=operator.attrgetter('name'), reverse=True)
496
497     # Set correct owner, mtime and filemode on directories.
498     for tarinfo in directories:
499         dirpath = os.path.join(path, tarinfo.name)
500         try:
501             self.chown(tarinfo, dirpath)
502             self.utime(tarinfo, dirpath)
503             self.chmod(tarinfo, dirpath)
504         except ExtractError:
505             e = sys.exc_info()[1]
506             if self.errorlevel > 1:
507                 raise
508             else:
509                 self._dbg(1, "tarfile: %s" % e)
510
511
512 def main(argv, version=DEFAULT_VERSION):
513     """Install or upgrade setuptools and EasyInstall"""
514     tarball = download_setuptools()
515     _install(tarball)
516
517
518 if __name__ == '__main__':
519     main(sys.argv[1:])