diff options
Diffstat (limited to 'eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/easy_install.py')
-rw-r--r-- | eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/easy_install.py | 1906 |
1 files changed, 1906 insertions, 0 deletions
diff --git a/eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/easy_install.py b/eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/easy_install.py new file mode 100644 index 0000000..b09c0c5 --- /dev/null +++ b/eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/easy_install.py @@ -0,0 +1,1906 @@ +############################################################################# +# +# Copyright (c) 2005 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Python easy_install API + +This module provides a high-level Python API for installing packages. +It doesn't install scripts. It uses setuptools and requires it to be +installed. +""" + +import distutils.errors +import fnmatch +import glob +import logging +import os +import pkg_resources +import py_compile +import re +import setuptools.archive_util +import setuptools.command.setopt +import setuptools.package_index +import shutil +import subprocess +import sys +import tempfile +import warnings +import zc.buildout +import zipimport + +_oprp = getattr(os.path, 'realpath', lambda path: path) +def realpath(path): + return os.path.normcase(os.path.abspath(_oprp(path))) + +default_index_url = os.environ.get( + 'buildout-testing-index-url', + 'http://pypi.python.org/simple', + ) + +logger = logging.getLogger('zc.buildout.easy_install') + +url_match = re.compile('[a-z0-9+.-]+://').match + +is_win32 = sys.platform == 'win32' +is_jython = sys.platform.startswith('java') +is_distribute = ( + pkg_resources.Requirement.parse('setuptools').key=='distribute') + +BROKEN_DASH_S_WARNING = ( + 'Buildout has been asked to exclude or limit site-packages so that ' + 'builds can be repeatable when using a system Python. However, ' + 'the chosen Python executable has a broken implementation of -S (see ' + 'https://bugs.launchpad.net/virtualenv/+bug/572545 for an example ' + "problem) and this breaks buildout's ability to isolate site-packages. " + "If the executable already has a clean site-packages (e.g., " + "using virtualenv's ``--no-site-packages`` option) you may be getting " + 'equivalent repeatability. To silence this warning, use the -s argument ' + 'to the buildout script. Alternatively, use a Python executable with a ' + 'working -S (such as a standard Python binary).') + +if is_jython: + import java.lang.System + jython_os_name = (java.lang.System.getProperties()['os.name']).lower() + +setuptools_loc = pkg_resources.working_set.find( + pkg_resources.Requirement.parse('setuptools') + ).location + +# Include buildout and setuptools eggs in paths. We prevent dupes just to +# keep from duplicating any log messages about them. +buildout_loc = pkg_resources.working_set.find( + pkg_resources.Requirement.parse('zc.buildout')).location +buildout_and_setuptools_path = [setuptools_loc] +if os.path.normpath(setuptools_loc) != os.path.normpath(buildout_loc): + buildout_and_setuptools_path.append(buildout_loc) + +def _has_broken_dash_S(executable): + """Detect https://bugs.launchpad.net/virtualenv/+bug/572545 .""" + # The first attempt here was to simply have the executable attempt to import + # ConfigParser and return the return code. That worked except for tests on + # Windows, where the return code was wrong for the fake Python executable + # generated by the virtualenv.txt test, apparently because setuptools' .exe + # file does not pass the -script.py's returncode back properly, at least in + # some circumstances. Therefore...print statements. + stdout, stderr = subprocess.Popen( + [executable, '-Sc', + 'try:\n' + ' import ConfigParser\n' + 'except ImportError:\n' + ' print 1\n' + 'else:\n' + ' print 0\n'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + return bool(int(stdout.strip())) + +def _get_system_paths(executable): + """Return lists of standard lib and site paths for executable. + """ + # We want to get a list of the site packages, which is not easy. + # The canonical way to do this is to use + # distutils.sysconfig.get_python_lib(), but that only returns a + # single path, which does not reflect reality for many system + # Pythons, which have multiple additions. Instead, we start Python + # with -S, which does not import site.py and set up the extra paths + # like site-packages or (Ubuntu/Debian) dist-packages and + # python-support. We then compare that sys.path with the normal one + # (minus user packages if this is Python 2.6, because we don't + # support those (yet?). The set of the normal one minus the set of + # the ones in ``python -S`` is the set of packages that are + # effectively site-packages. + # + # The given executable might not be the current executable, so it is + # appropriate to do another subprocess to figure out what the + # additional site-package paths are. Moreover, even if this + # executable *is* the current executable, this code might be run in + # the context of code that has manipulated the sys.path--for + # instance, to add local zc.buildout or setuptools eggs. + def get_sys_path(*args, **kwargs): + cmd = [executable] + cmd.extend(args) + cmd.extend([ + "-c", "import sys, os;" + "print repr([os.path.normpath(p) for p in sys.path if p])"]) + # Windows needs some (as yet to be determined) part of the real env. + env = os.environ.copy() + # We need to make sure that PYTHONPATH, which will often be set + # to include a custom buildout-generated site.py, is not set, or + # else we will not get an accurate sys.path for the executable. + env.pop('PYTHONPATH', None) + env.update(kwargs) + _proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + stdout, stderr = _proc.communicate(); + if _proc.returncode: + raise RuntimeError( + 'error trying to get system packages:\n%s' % (stderr,)) + res = eval(stdout.strip()) + try: + res.remove('.') + except ValueError: + pass + return res + stdlib = get_sys_path('-S') # stdlib only + no_user_paths = get_sys_path(PYTHONNOUSERSITE='x') + site_paths = [p for p in no_user_paths if p not in stdlib] + return (stdlib, site_paths) + +def _get_version_info(executable): + cmd = [executable, '-Sc', + 'import sys; print(repr(tuple(x for x in sys.version_info)))'] + _proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = _proc.communicate(); + if _proc.returncode: + raise RuntimeError( + 'error trying to get system packages:\n%s' % (stderr,)) + return eval(stdout.strip()) + + +class IncompatibleVersionError(zc.buildout.UserError): + """A specified version is incompatible with a given requirement. + """ + +_versions = {sys.executable: '%d.%d' % sys.version_info[:2]} +def _get_version(executable): + try: + return _versions[executable] + except KeyError: + cmd = _safe_arg(executable) + ' -V' + p = subprocess.Popen(cmd, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + close_fds=not is_win32) + i, o = (p.stdin, p.stdout) + i.close() + version = o.read().strip() + o.close() + pystring, version = version.split() + assert pystring == 'Python' + version = re.match('(\d[.]\d)([.].*\d)?$', version).group(1) + _versions[executable] = version + return version + +FILE_SCHEME = re.compile('file://', re.I).match + + +class AllowHostsPackageIndex(setuptools.package_index.PackageIndex): + """Will allow urls that are local to the system. + + No matter what is allow_hosts. + """ + def url_ok(self, url, fatal=False): + if FILE_SCHEME(url): + return True + return setuptools.package_index.PackageIndex.url_ok(self, url, False) + + +_indexes = {} +def _get_index(executable, index_url, find_links, allow_hosts=('*',), + path=None): + # If path is None, the index will use sys.path. If you provide an empty + # path ([]), it will complain uselessly about missing index pages for + # packages found in the paths that you expect to use. Therefore, this path + # is always the same as the _env path in the Installer. + key = executable, index_url, tuple(find_links) + index = _indexes.get(key) + if index is not None: + return index + + if index_url is None: + index_url = default_index_url + index = AllowHostsPackageIndex( + index_url, hosts=allow_hosts, search_path=path, + python=_get_version(executable) + ) + + if find_links: + index.add_find_links(find_links) + + _indexes[key] = index + return index + +clear_index_cache = _indexes.clear + +if is_win32: + # work around spawn lamosity on windows + # XXX need safe quoting (see the subprocess.list2cmdline) and test + def _safe_arg(arg): + return '"%s"' % arg +else: + _safe_arg = str + +# The following string is used to run easy_install in +# Installer._call_easy_install. It is usually started with python -S +# (that is, don't import site at start). That flag, and all of the code +# in this snippet above the last two lines, exist to work around a +# relatively rare problem. If +# +# - your buildout configuration is trying to install a package that is within +# a namespace package, and +# +# - you use a Python that has a different version of this package +# installed in in its site-packages using +# --single-version-externally-managed (that is, using the mechanism +# sometimes used by system packagers: +# http://peak.telecommunity.com/DevCenter/setuptools#install-command ), and +# +# - the new package tries to do sys.path tricks in the setup.py to get a +# __version__, +# +# then the older package will be loaded first, making the setup version +# the wrong number. While very arguably packages simply shouldn't do +# the sys.path tricks, some do, and we don't want buildout to fall over +# when they do. +# +# The namespace packages installed in site-packages with +# --single-version-externally-managed use a mechanism that cause them to +# be processed when site.py is imported (see +# http://mail.python.org/pipermail/distutils-sig/2009-May/011730.html +# for another description of the problem). Simply starting Python with +# -S addresses the problem in Python 2.4 and 2.5, but Python 2.6's +# distutils imports a value from the site module, so we unfortunately +# have to do more drastic surgery in the _easy_install_preface code below. +# +# Here's an example of the .pth files created by setuptools when using that +# flag: +# +# import sys,new,os; +# p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('<NAMESPACE>',)); +# ie = os.path.exists(os.path.join(p,'__init__.py')); +# m = not ie and sys.modules.setdefault('<NAMESPACE>',new.module('<NAMESPACE>')); +# mp = (m or []) and m.__dict__.setdefault('__path__',[]); +# (p not in mp) and mp.append(p) +# +# The code, below, then, runs under -S, indicating that site.py should +# not be loaded initially. It gets the initial sys.path under these +# circumstances, and then imports site (because Python 2.6's distutils +# will want it, as mentioned above). It then reinstates the old sys.path +# value. Then it removes namespace packages (created by the setuptools +# code above) from sys.modules. It identifies namespace packages by +# iterating over every loaded module. It first looks if there is a +# __path__, so it is a package; and then it sees if that __path__ does +# not have an __init__.py. (Note that PEP 382, +# http://www.python.org/dev/peps/pep-0382, makes it possible to have a +# namespace package that has an __init__.py, but also should make it +# unnecessary for site.py to preprocess these packages, so it should be +# fine, as far as can be guessed as of this writing.) Finally, it +# imports easy_install and runs it. +_easy_install_preface = '''\ +import sys,os;\ +p = sys.path[:];\ +import site;\ +sys.path[:] = p;\ +[sys.modules.pop(k) for k, v in sys.modules.items()\ + if hasattr(v, '__path__') and len(v.__path__)==1 and\ + not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))];''' +_easy_install_cmd = ( + 'from setuptools.command.easy_install import main;main()') + + +class Installer: + + _versions = {} + _download_cache = None + _install_from_cache = False + _prefer_final = True + _use_dependency_links = True + _allow_picked_versions = True + _always_unzip = False + _include_site_packages = True + _allowed_eggs_from_site_packages = ('*',) + + def __init__(self, + dest=None, + links=(), + index=None, + executable=sys.executable, + always_unzip=None, + path=None, + newest=True, + versions=None, + use_dependency_links=None, + allow_hosts=('*',), + include_site_packages=None, + allowed_eggs_from_site_packages=None, + prefer_final=None, + ): + self._dest = dest + self._allow_hosts = allow_hosts + + if self._install_from_cache: + if not self._download_cache: + raise ValueError("install_from_cache set to true with no" + " download cache") + links = () + index = 'file://' + self._download_cache + + if use_dependency_links is not None: + self._use_dependency_links = use_dependency_links + if prefer_final is not None: + self._prefer_final = prefer_final + self._links = links = list(_fix_file_links(links)) + if self._download_cache and (self._download_cache not in links): + links.insert(0, self._download_cache) + + self._index_url = index + self._executable = executable + self._has_broken_dash_S = _has_broken_dash_S(self._executable) + if always_unzip is not None: + self._always_unzip = always_unzip + path = (path and path[:] or []) + if include_site_packages is not None: + self._include_site_packages = include_site_packages + if allowed_eggs_from_site_packages is not None: + self._allowed_eggs_from_site_packages = tuple( + allowed_eggs_from_site_packages) + if self._has_broken_dash_S: + if (not self._include_site_packages or + self._allowed_eggs_from_site_packages != ('*',)): + # We can't do this if the executable has a broken -S. + warnings.warn(BROKEN_DASH_S_WARNING) + self._include_site_packages = True + self._allowed_eggs_from_site_packages = ('*',) + self._easy_install_cmd = _easy_install_cmd + else: + self._easy_install_cmd = _easy_install_preface + _easy_install_cmd + self._easy_install_cmd = _safe_arg(self._easy_install_cmd) + stdlib, self._site_packages = _get_system_paths(executable) + version_info = _get_version_info(executable) + if version_info == sys.version_info: + # Maybe we can add the buildout and setuptools path. If we + # are including site_packages, we only have to include the extra + # bits here, so we don't duplicate. On the other hand, if we + # are not including site_packages, we only want to include the + # parts that are not in site_packages, so the code is the same. + path.extend( + set(buildout_and_setuptools_path).difference( + self._site_packages)) + if self._include_site_packages: + path.extend(self._site_packages) + if dest is not None and dest not in path: + path.insert(0, dest) + self._path = path + if self._dest is None: + newest = False + self._newest = newest + self._env = pkg_resources.Environment(path, + python=_get_version(executable)) + self._index = _get_index(executable, index, links, self._allow_hosts, + self._path) + + if versions is not None: + self._versions = versions + + _allowed_eggs_from_site_packages_regex = None + def allow_site_package_egg(self, name): + if (not self._include_site_packages or + not self._allowed_eggs_from_site_packages): + # If the answer is a blanket "no," perform a shortcut. + return False + if self._allowed_eggs_from_site_packages_regex is None: + pattern = '(%s)' % ( + '|'.join( + fnmatch.translate(name) + for name in self._allowed_eggs_from_site_packages), + ) + self._allowed_eggs_from_site_packages_regex = re.compile(pattern) + return bool(self._allowed_eggs_from_site_packages_regex.match(name)) + + def _satisfied(self, req, source=None): + # We get all distributions that match the given requirement. If we are + # not supposed to include site-packages for the given egg, we also + # filter those out. Even if include_site_packages is False and so we + # have excluded site packages from the _env's paths (see + # Installer.__init__), we need to do the filtering here because an + # .egg-link, such as one for setuptools or zc.buildout installed by + # zc.buildout.buildout.Buildout.bootstrap, can indirectly include a + # path in our _site_packages. + dists = [dist for dist in self._env[req.project_name] if ( + dist in req and ( + dist.location not in self._site_packages or + self.allow_site_package_egg(dist.project_name)) + ) + ] + if not dists: + logger.debug('We have no distributions for %s that satisfies %r.', + req.project_name, str(req)) + + return None, self._obtain(req, source) + + # Note that dists are sorted from best to worst, as promised by + # env.__getitem__ + + for dist in dists: + if (dist.precedence == pkg_resources.DEVELOP_DIST and + dist.location not in self._site_packages): + # System eggs are sometimes installed as develop eggs. + # Those are not the kind of develop eggs we are looking for + # here: we want ones that the buildout itself has locally as + # develop eggs. + logger.debug('We have a develop egg: %s', dist) + return dist, None + + # Special common case, we have a specification for a single version: + specs = req.specs + if len(specs) == 1 and specs[0][0] == '==': + logger.debug('We have the distribution that satisfies %r.', + str(req)) + return dists[0], None + + if self._prefer_final: + fdists = [dist for dist in dists + if _final_version(dist.parsed_version) + ] + if fdists: + # There are final dists, so only use those + dists = fdists + + if not self._newest: + # We don't need the newest, so we'll use the newest one we + # find, which is the first returned by + # Environment.__getitem__. + return dists[0], None + + best_we_have = dists[0] # Because dists are sorted from best to worst + + # We have some installed distros. There might, theoretically, be + # newer ones. Let's find out which ones are available and see if + # any are newer. We only do this if we're willing to install + # something, which is only true if dest is not None: + + if self._dest is not None: + best_available = self._obtain(req, source) + else: + best_available = None + + if best_available is None: + # That's a bit odd. There aren't any distros available. + # We should use the best one we have that meets the requirement. + logger.debug( + 'There are no distros available that meet %r.\n' + 'Using our best, %s.', + str(req), best_available) + return best_we_have, None + + if self._prefer_final: + if _final_version(best_available.parsed_version): + if _final_version(best_we_have.parsed_version): + if (best_we_have.parsed_version + < + best_available.parsed_version + ): + return None, best_available + else: + return None, best_available + else: + if (not _final_version(best_we_have.parsed_version) + and + (best_we_have.parsed_version + < + best_available.parsed_version + ) + ): + return None, best_available + else: + if (best_we_have.parsed_version + < + best_available.parsed_version + ): + return None, best_available + + logger.debug( + 'We have the best distribution that satisfies %r.', + str(req)) + return best_we_have, None + + def _load_dist(self, dist): + dists = pkg_resources.Environment( + dist.location, + python=_get_version(self._executable), + )[dist.project_name] + assert len(dists) == 1 + return dists[0] + + def _call_easy_install(self, spec, ws, dest, dist): + + tmp = tempfile.mkdtemp(dir=dest) + try: + path = setuptools_loc + + args = ('-c', self._easy_install_cmd, '-mUNxd', _safe_arg(tmp)) + if not self._has_broken_dash_S: + args = ('-S',) + args + if self._always_unzip: + args += ('-Z', ) + level = logger.getEffectiveLevel() + if level > 0: + args += ('-q', ) + elif level < 0: + args += ('-v', ) + + args += (_safe_arg(spec), ) + + if level <= logging.DEBUG: + logger.debug('Running easy_install:\n%s "%s"\npath=%s\n', + self._executable, '" "'.join(args), path) + + if is_jython: + extra_env = dict(os.environ, PYTHONPATH=path) + else: + args += (dict(os.environ, PYTHONPATH=path), ) + + sys.stdout.flush() # We want any pending output first + + if is_jython: + exit_code = subprocess.Popen( + [_safe_arg(self._executable)] + list(args), + env=extra_env).wait() + else: + exit_code = os.spawnle( + os.P_WAIT, self._executable, _safe_arg (self._executable), + *args) + + dists = [] + env = pkg_resources.Environment( + [tmp], + python=_get_version(self._executable), + ) + for project in env: + dists.extend(env[project]) + + if exit_code: + logger.error( + "An error occurred when trying to install %s. " + "Look above this message for any errors that " + "were output by easy_install.", + dist) + + if not dists: + raise zc.buildout.UserError("Couldn't install: %s" % dist) + + if len(dists) > 1: + logger.warn("Installing %s\n" + "caused multiple distributions to be installed:\n" + "%s\n", + dist, '\n'.join(map(str, dists))) + else: + d = dists[0] + if d.project_name != dist.project_name: + logger.warn("Installing %s\n" + "Caused installation of a distribution:\n" + "%s\n" + "with a different project name.", + dist, d) + if d.version != dist.version: + logger.warn("Installing %s\n" + "Caused installation of a distribution:\n" + "%s\n" + "with a different version.", + dist, d) + + result = [] + for d in dists: + newloc = os.path.join(dest, os.path.basename(d.location)) + if os.path.exists(newloc): + if os.path.isdir(newloc): + shutil.rmtree(newloc) + else: + os.remove(newloc) + os.rename(d.location, newloc) + + [d] = pkg_resources.Environment( + [newloc], + python=_get_version(self._executable), + )[d.project_name] + + result.append(d) + + return result + + finally: + shutil.rmtree(tmp) + + def _obtain(self, requirement, source=None): + # initialize out index for this project: + index = self._index + + if index.obtain(requirement) is None: + # Nothing is available. + return None + + # Filter the available dists for the requirement and source flag. If + # we are not supposed to include site-packages for the given egg, we + # also filter those out. Even if include_site_packages is False and so + # we have excluded site packages from the _env's paths (see + # Installer.__init__), we need to do the filtering here because an + # .egg-link, such as one for setuptools or zc.buildout installed by + # zc.buildout.buildout.Buildout.bootstrap, can indirectly include a + # path in our _site_packages. + dists = [dist for dist in index[requirement.project_name] if ( + dist in requirement and ( + dist.location not in self._site_packages or + self.allow_site_package_egg(dist.project_name)) + and ( + (not source) or + (dist.precedence == pkg_resources.SOURCE_DIST)) + ) + ] + + # If we prefer final dists, filter for final and use the + # result if it is non empty. + if self._prefer_final: + fdists = [dist for dist in dists + if _final_version(dist.parsed_version) + ] + if fdists: + # There are final dists, so only use those + dists = fdists + + # Now find the best one: + best = [] + bestv = () + for dist in dists: + distv = dist.parsed_version + if distv > bestv: + best = [dist] + bestv = distv + elif distv == bestv: + best.append(dist) + + if not best: + return None + + if len(best) == 1: + return best[0] + + if self._download_cache: + for dist in best: + if (realpath(os.path.dirname(dist.location)) + == + self._download_cache + ): + return dist + + best.sort() + return best[-1] + + def _fetch(self, dist, tmp, download_cache): + if (download_cache + and (realpath(os.path.dirname(dist.location)) == download_cache) + ): + return dist + + new_location = self._index.download(dist.location, tmp) + if (download_cache + and (realpath(new_location) == realpath(dist.location)) + and os.path.isfile(new_location) + ): + # setuptools avoids making extra copies, but we want to copy + # to the download cache + shutil.copy2(new_location, tmp) + new_location = os.path.join(tmp, os.path.basename(new_location)) + + return dist.clone(location=new_location) + + def _get_dist(self, requirement, ws, always_unzip): + + __doing__ = 'Getting distribution for %r.', str(requirement) + + # Maybe an existing dist is already the best dist that satisfies the + # requirement + dist, avail = self._satisfied(requirement) + + if dist is None: + if self._dest is not None: + logger.info(*__doing__) + + # Retrieve the dist: + if avail is None: + raise MissingDistribution(requirement, ws) + + # We may overwrite distributions, so clear importer + # cache. + sys.path_importer_cache.clear() + + tmp = self._download_cache + if tmp is None: + tmp = tempfile.mkdtemp('get_dist') + + try: + dist = self._fetch(avail, tmp, self._download_cache) + + if dist is None: + raise zc.buildout.UserError( + "Couldn't download distribution %s." % avail) + + if dist.precedence == pkg_resources.EGG_DIST: + # It's already an egg, just fetch it into the dest + + newloc = os.path.join( + self._dest, os.path.basename(dist.location)) + + if os.path.isdir(dist.location): + # we got a directory. It must have been + # obtained locally. Just copy it. + shutil.copytree(dist.location, newloc) + else: + + if self._always_unzip: + should_unzip = True + else: + metadata = pkg_resources.EggMetadata( + zipimport.zipimporter(dist.location) + ) + should_unzip = ( + metadata.has_metadata('not-zip-safe') + or + not metadata.has_metadata('zip-safe') + ) + + if should_unzip: + setuptools.archive_util.unpack_archive( + dist.location, newloc) + else: + shutil.copyfile(dist.location, newloc) + + redo_pyc(newloc) + + # Getting the dist from the environment causes the + # distribution meta data to be read. Cloning isn't + # good enough. + dists = pkg_resources.Environment( + [newloc], + python=_get_version(self._executable), + )[dist.project_name] + else: + # It's some other kind of dist. We'll let easy_install + # deal with it: + dists = self._call_easy_install( + dist.location, ws, self._dest, dist) + for dist in dists: + redo_pyc(dist.location) + + finally: + if tmp != self._download_cache: + shutil.rmtree(tmp) + + self._env.scan([self._dest]) + dist = self._env.best_match(requirement, ws) + logger.info("Got %s.", dist) + + else: + dists = [dist] + + for dist in dists: + if (dist.has_metadata('dependency_links.txt') + and not self._install_from_cache + and self._use_dependency_links + ): + for link in dist.get_metadata_lines('dependency_links.txt'): + link = link.strip() + if link not in self._links: + logger.debug('Adding find link %r from %s', link, dist) + self._links.append(link) + self._index = _get_index(self._executable, + self._index_url, self._links, + self._allow_hosts, self._path) + + for dist in dists: + # Check whether we picked a version and, if we did, report it: + if not ( + dist.precedence == pkg_resources.DEVELOP_DIST + or + (len(requirement.specs) == 1 + and + requirement.specs[0][0] == '==') + ): + logger.debug('Picked: %s = %s', + dist.project_name, dist.version) + if not self._allow_picked_versions: + raise zc.buildout.UserError( + 'Picked: %s = %s' % (dist.project_name, dist.version) + ) + + return dists + + def _maybe_add_setuptools(self, ws, dist): + if dist.has_metadata('namespace_packages.txt'): + for r in dist.requires(): + if r.project_name in ('setuptools', 'distribute'): + break + else: + # We have a namespace package but no requirement for setuptools + if dist.precedence == pkg_resources.DEVELOP_DIST: + logger.warn( + "Develop distribution: %s\n" + "uses namespace packages but the distribution " + "does not require setuptools.", + dist) + requirement = self._constrain( + pkg_resources.Requirement.parse('setuptools') + ) + if ws.find(requirement) is None: + for dist in self._get_dist(requirement, ws, False): + ws.add(dist) + + + def _constrain(self, requirement): + if is_distribute and requirement.key == 'setuptools': + requirement = pkg_resources.Requirement.parse('distribute') + version = self._versions.get(requirement.project_name) + if version: + if version not in requirement: + logger.error("The version, %s, is not consistent with the " + "requirement, %r.", version, str(requirement)) + raise IncompatibleVersionError("Bad version", version) + + requirement = pkg_resources.Requirement.parse( + "%s[%s] ==%s" % (requirement.project_name, + ','.join(requirement.extras), + version)) + + return requirement + + def install(self, specs, working_set=None): + + logger.debug('Installing %s.', repr(specs)[1:-1]) + + path = self._path + destination = self._dest + if destination is not None and destination not in path: + path.insert(0, destination) + + requirements = [self._constrain(pkg_resources.Requirement.parse(spec)) + for spec in specs] + + + + if working_set is None: + ws = pkg_resources.WorkingSet([]) + else: + ws = working_set + + for requirement in requirements: + for dist in self._get_dist(requirement, ws, self._always_unzip): + ws.add(dist) + self._maybe_add_setuptools(ws, dist) + + # OK, we have the requested distributions and they're in the working + # set, but they may have unmet requirements. We'll resolve these + # requirements. This is code modified from + # pkg_resources.WorkingSet.resolve. We can't reuse that code directly + # because we have to constrain our requirements (see + # versions_section_ignored_for_dependency_in_favor_of_site_packages in + # zc.buildout.tests). + requirements.reverse() # Set up the stack. + processed = {} # This is a set of processed requirements. + best = {} # This is a mapping of key -> dist. + # Note that we don't use the existing environment, because we want + # to look for new eggs unless what we have is the best that + # matches the requirement. + env = pkg_resources.Environment(ws.entries) + while requirements: + # Process dependencies breadth-first. + req = self._constrain(requirements.pop(0)) + if req in processed: + # Ignore cyclic or redundant dependencies. + continue + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map. + dist = ws.by_key.get(req.key) + if dist is None: + try: + dist = best[req.key] = env.best_match(req, ws) + except pkg_resources.VersionConflict, err: + raise VersionConflict(err, ws) + if dist is None or ( + dist.location in self._site_packages and not + self.allow_site_package_egg(dist.project_name)): + # If we didn't find a distribution in the + # environment, or what we found is from site + # packages and not allowed to be there, try + # again. + if destination: + logger.debug('Getting required %r', str(req)) + else: + logger.debug('Adding required %r', str(req)) + _log_requirement(ws, req) + for dist in self._get_dist(req, + ws, self._always_unzip): + ws.add(dist) + self._maybe_add_setuptools(ws, dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency. + raise VersionConflict( + pkg_resources.VersionConflict(dist, req), ws) + requirements.extend(dist.requires(req.extras)[::-1]) + processed[req] = True + if dist.location in self._site_packages: + logger.debug('Egg from site-packages: %s', dist) + return ws + + def build(self, spec, build_ext): + + requirement = self._constrain(pkg_resources.Requirement.parse(spec)) + + dist, avail = self._satisfied(requirement, 1) + if dist is not None: + return [dist.location] + + # Retrieve the dist: + if avail is None: + raise zc.buildout.UserError( + "Couldn't find a source distribution for %r." + % str(requirement)) + + logger.debug('Building %r', spec) + + tmp = self._download_cache + if tmp is None: + tmp = tempfile.mkdtemp('get_dist') + + try: + dist = self._fetch(avail, tmp, self._download_cache) + + build_tmp = tempfile.mkdtemp('build') + try: + setuptools.archive_util.unpack_archive(dist.location, + build_tmp) + if os.path.exists(os.path.join(build_tmp, 'setup.py')): + base = build_tmp + else: + setups = glob.glob( + os.path.join(build_tmp, '*', 'setup.py')) + if not setups: + raise distutils.errors.DistutilsError( + "Couldn't find a setup script in %s" + % os.path.basename(dist.location) + ) + if len(setups) > 1: + raise distutils.errors.DistutilsError( + "Multiple setup scripts in %s" + % os.path.basename(dist.location) + ) + base = os.path.dirname(setups[0]) + + setup_cfg = os.path.join(base, 'setup.cfg') + if not os.path.exists(setup_cfg): + f = open(setup_cfg, 'w') + f.close() + setuptools.command.setopt.edit_config( + setup_cfg, dict(build_ext=build_ext)) + + dists = self._call_easy_install( + base, pkg_resources.WorkingSet(), + self._dest, dist) + + for dist in dists: + redo_pyc(dist.location) + + return [dist.location for dist in dists] + finally: + shutil.rmtree(build_tmp) + + finally: + if tmp != self._download_cache: + shutil.rmtree(tmp) + +def default_versions(versions=None): + old = Installer._versions + if versions is not None: + Installer._versions = versions + return old + +def download_cache(path=-1): + old = Installer._download_cache + if path != -1: + if path: + path = realpath(path) + Installer._download_cache = path + return old + +def install_from_cache(setting=None): + old = Installer._install_from_cache + if setting is not None: + Installer._install_from_cache = bool(setting) + return old + +def prefer_final(setting=None): + old = Installer._prefer_final + if setting is not None: + Installer._prefer_final = bool(setting) + return old + +def include_site_packages(setting=None): + old = Installer._include_site_packages + if setting is not None: + Installer._include_site_packages = bool(setting) + return old + +def allowed_eggs_from_site_packages(setting=None): + old = Installer._allowed_eggs_from_site_packages + if setting is not None: + Installer._allowed_eggs_from_site_packages = tuple(setting) + return old + +def use_dependency_links(setting=None): + old = Installer._use_dependency_links + if setting is not None: + Installer._use_dependency_links = bool(setting) + return old + +def allow_picked_versions(setting=None): + old = Installer._allow_picked_versions + if setting is not None: + Installer._allow_picked_versions = bool(setting) + return old + +def always_unzip(setting=None): + old = Installer._always_unzip + if setting is not None: + Installer._always_unzip = bool(setting) + return old + +def install(specs, dest, + links=(), index=None, + executable=sys.executable, always_unzip=None, + path=None, working_set=None, newest=True, versions=None, + use_dependency_links=None, allow_hosts=('*',), + include_site_packages=None, allowed_eggs_from_site_packages=None, + prefer_final=None): + installer = Installer( + dest, links, index, executable, always_unzip, path, newest, + versions, use_dependency_links, allow_hosts=allow_hosts, + include_site_packages=include_site_packages, + allowed_eggs_from_site_packages=allowed_eggs_from_site_packages, + prefer_final=prefer_final) + return installer.install(specs, working_set) + + +def build(spec, dest, build_ext, + links=(), index=None, + executable=sys.executable, + path=None, newest=True, versions=None, allow_hosts=('*',), + include_site_packages=None, allowed_eggs_from_site_packages=None): + installer = Installer( + dest, links, index, executable, True, path, newest, versions, + allow_hosts=allow_hosts, + include_site_packages=include_site_packages, + allowed_eggs_from_site_packages=allowed_eggs_from_site_packages) + return installer.build(spec, build_ext) + + + +def _rm(*paths): + for path in paths: + if os.path.isdir(path): + shutil.rmtree(path) + elif os.path.exists(path): + os.remove(path) + +def _copyeggs(src, dest, suffix, undo): + result = [] + undo.append(lambda : _rm(*result)) + for name in os.listdir(src): + if name.endswith(suffix): + new = os.path.join(dest, name) + _rm(new) + os.rename(os.path.join(src, name), new) + result.append(new) + + assert len(result) == 1, str(result) + undo.pop() + + return result[0] + +def develop(setup, dest, + build_ext=None, + executable=sys.executable): + + if os.path.isdir(setup): + directory = setup + setup = os.path.join(directory, 'setup.py') + else: + directory = os.path.dirname(setup) + + undo = [] + try: + if build_ext: + setup_cfg = os.path.join(directory, 'setup.cfg') + if os.path.exists(setup_cfg): + os.rename(setup_cfg, setup_cfg+'-develop-aside') + def restore_old_setup(): + if os.path.exists(setup_cfg): + os.remove(setup_cfg) + os.rename(setup_cfg+'-develop-aside', setup_cfg) + undo.append(restore_old_setup) + else: + open(setup_cfg, 'w') + undo.append(lambda: os.remove(setup_cfg)) + setuptools.command.setopt.edit_config( + setup_cfg, dict(build_ext=build_ext)) + + fd, tsetup = tempfile.mkstemp() + undo.append(lambda: os.remove(tsetup)) + undo.append(lambda: os.close(fd)) + + os.write(fd, runsetup_template % dict( + setuptools=setuptools_loc, + setupdir=directory, + setup=setup, + __file__ = setup, + )) + + tmp3 = tempfile.mkdtemp('build', dir=dest) + undo.append(lambda : shutil.rmtree(tmp3)) + + args = [ + zc.buildout.easy_install._safe_arg(tsetup), + '-q', 'develop', '-mxN', + '-d', _safe_arg(tmp3), + ] + + log_level = logger.getEffectiveLevel() + if log_level <= 0: + if log_level == 0: + del args[1] + else: + args[1] == '-v' + if log_level < logging.DEBUG: + logger.debug("in: %r\n%s", directory, ' '.join(args)) + + if is_jython: + assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0 + else: + assert os.spawnl(os.P_WAIT, executable, _safe_arg(executable), + *args) == 0 + + return _copyeggs(tmp3, dest, '.egg-link', undo) + + finally: + undo.reverse() + [f() for f in undo] + +def working_set(specs, executable, path, include_site_packages=None, + allowed_eggs_from_site_packages=None, prefer_final=None): + return install( + specs, None, executable=executable, path=path, + include_site_packages=include_site_packages, + allowed_eggs_from_site_packages=allowed_eggs_from_site_packages, + prefer_final=prefer_final) + +############################################################################ +# Script generation functions + +def scripts( + reqs, working_set, executable, dest, + scripts=None, + extra_paths=(), + arguments='', + interpreter=None, + initialization='', + relative_paths=False, + ): + """Generate scripts and/or an interpreter. + + See sitepackage_safe_scripts for a version that can be used with a Python + that has code installed in site-packages. It has more options and a + different approach. + """ + path = _get_path(working_set, extra_paths) + if initialization: + initialization = '\n'+initialization+'\n' + generated = _generate_scripts( + reqs, working_set, dest, path, scripts, relative_paths, + initialization, executable, arguments) + if interpreter: + sname = os.path.join(dest, interpreter) + spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths) + generated.extend( + _pyscript(spath, sname, executable, rpsetup)) + return generated + +# We need to give an alternate name to the ``scripts`` function so that it +# can be referenced within sitepackage_safe_scripts, which uses ``scripts`` +# as an argument name. +_original_scripts_function = scripts + +def sitepackage_safe_scripts( + dest, working_set, executable, site_py_dest, + reqs=(), + scripts=None, + interpreter=None, + extra_paths=(), + initialization='', + include_site_packages=False, + exec_sitecustomize=False, + relative_paths=False, + script_arguments='', + script_initialization='', + ): + """Generate scripts and/or an interpreter from a system Python. + + This accomplishes the same job as the ``scripts`` function, above, + but it does so in an alternative way that allows safely including + Python site packages, if desired, and choosing to execute the Python's + sitecustomize. + """ + if _has_broken_dash_S(executable): + if not include_site_packages: + warnings.warn(BROKEN_DASH_S_WARNING) + return _original_scripts_function( + reqs, working_set, executable, dest, scripts, extra_paths, + script_arguments, interpreter, initialization, relative_paths) + generated = [] + generated.append(_generate_sitecustomize( + site_py_dest, executable, initialization, exec_sitecustomize)) + generated.append(_generate_site( + site_py_dest, working_set, executable, extra_paths, + include_site_packages, relative_paths)) + script_initialization = _script_initialization_template % dict( + site_py_dest=site_py_dest, + script_initialization=script_initialization) + if not script_initialization.endswith('\n'): + script_initialization += '\n' + generated.extend(_generate_scripts( + reqs, working_set, dest, [site_py_dest], scripts, relative_paths, + script_initialization, executable, script_arguments, block_site=True)) + if interpreter: + generated.extend(_generate_interpreter( + interpreter, dest, executable, site_py_dest, relative_paths)) + return generated + +_script_initialization_template = ''' +import os +path = sys.path[0] +if os.environ.get('PYTHONPATH'): + path = os.pathsep.join([path, os.environ['PYTHONPATH']]) +os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ.get('PYTHONPATH', '') +os.environ['PYTHONPATH'] = path +import site # imports custom buildout-generated site.py +%(script_initialization)s''' + +# Utilities for the script generation functions. + +# These are shared by both ``scripts`` and ``sitepackage_safe_scripts`` + +def _get_path(working_set, extra_paths=()): + """Given working set and extra paths, return a normalized path list.""" + path = [dist.location for dist in working_set] + path.extend(extra_paths) + return map(realpath, path) + +def _generate_scripts(reqs, working_set, dest, path, scripts, relative_paths, + initialization, executable, arguments, + block_site=False): + """Generate scripts for the given requirements. + + - reqs is an iterable of string requirements or entry points. + - The requirements must be findable in the given working_set. + - The dest is the directory in which the scripts should be created. + - The path is a list of paths that should be added to sys.path. + - The scripts is an optional dictionary. If included, the keys should be + the names of the scripts that should be created, as identified in their + entry points; and the values should be the name the script should + actually be created with. + - relative_paths, if given, should be the path that is the root of the + buildout (the common path that should be the root of what is relative). + """ + if isinstance(reqs, str): + raise TypeError('Expected iterable of requirements or entry points,' + ' got string.') + generated = [] + entry_points = [] + for req in reqs: + if isinstance(req, str): + req = pkg_resources.Requirement.parse(req) + dist = working_set.find(req) + for name in pkg_resources.get_entry_map(dist, 'console_scripts'): + entry_point = dist.get_entry_info('console_scripts', name) + entry_points.append( + (name, entry_point.module_name, + '.'.join(entry_point.attrs)) + ) + else: + entry_points.append(req) + for name, module_name, attrs in entry_points: + if scripts is not None: + sname = scripts.get(name) + if sname is None: + continue + else: + sname = name + sname = os.path.join(dest, sname) + spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths) + generated.extend( + _script(sname, executable, rpsetup, spath, initialization, + module_name, attrs, arguments, block_site=block_site)) + return generated + +def _relative_path_and_setup(sname, path, + relative_paths=False, indent_level=1, + omit_os_import=False): + """Return a string of code of paths and of setup if appropriate. + + - sname is the full path to the script name to be created. + - path is the list of paths to be added to sys.path. + - relative_paths, if given, should be the path that is the root of the + buildout (the common path that should be the root of what is relative). + - indent_level is the number of four-space indents that the path should + insert before each element of the path. + """ + if relative_paths: + relative_paths = os.path.normcase(relative_paths) + sname = os.path.normcase(os.path.abspath(sname)) + spath = _format_paths( + [_relativitize(os.path.normcase(path_item), sname, relative_paths) + for path_item in path], indent_level=indent_level) + rpsetup = relative_paths_setup + if not omit_os_import: + rpsetup = '\n\nimport os\n' + rpsetup + for i in range(_relative_depth(relative_paths, sname)): + rpsetup += "\nbase = os.path.dirname(base)" + else: + spath = _format_paths((repr(p) for p in path), + indent_level=indent_level) + rpsetup = '' + return spath, rpsetup + +def _relative_depth(common, path): + """Return number of dirs separating ``path`` from ancestor, ``common``. + + For instance, if path is /foo/bar/baz/bing, and common is /foo, this will + return 2--in UNIX, the number of ".." to get from bing's directory + to foo. + + This is a helper for _relative_path_and_setup. + """ + n = 0 + while 1: + dirname = os.path.dirname(path) + if dirname == path: + raise AssertionError("dirname of %s is the same" % dirname) + if dirname == common: + break + n += 1 + path = dirname + return n + +def _relative_path(common, path): + """Return the relative path from ``common`` to ``path``. + + This is a helper for _relativitize, which is a helper to + _relative_path_and_setup. + """ + r = [] + while 1: + dirname, basename = os.path.split(path) + r.append(basename) + if dirname == common: + break + if dirname == path: + raise AssertionError("dirname of %s is the same" % dirname) + path = dirname + r.reverse() + return os.path.join(*r) + +def _relativitize(path, script, relative_paths): + """Return a code string for the given path. + + Path is relative to the base path ``relative_paths``if the common prefix + between ``path`` and ``script`` starts with ``relative_paths``. + """ + if path == script: + raise AssertionError("path == script") + common = os.path.dirname(os.path.commonprefix([path, script])) + if (common == relative_paths or + common.startswith(os.path.join(relative_paths, '')) + ): + return "join(base, %r)" % _relative_path(common, path) + else: + return repr(path) + +relative_paths_setup = """ +join = os.path.join +base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))""" + +def _write_script(full_name, contents, logged_type): + """Write contents of script in full_name, logging the action. + + The only tricky bit in this function is that it supports Windows by + creating exe files using a pkg_resources helper. + """ + generated = [] + script_name = full_name + if is_win32: + script_name += '-script.py' + # Generate exe file and give the script a magic name. + exe = full_name + '.exe' + new_data = pkg_resources.resource_string('setuptools', 'cli.exe') + if not os.path.exists(exe) or (open(exe, 'rb').read() != new_data): + # Only write it if it's different. + open(exe, 'wb').write(new_data) + generated.append(exe) + changed = not (os.path.exists(script_name) and + open(script_name).read() == contents) + if changed: + open(script_name, 'w').write(contents) + try: + os.chmod(script_name, 0755) + except (AttributeError, os.error): + pass + logger.info("Generated %s %r.", logged_type, full_name) + generated.append(script_name) + return generated + +def _format_paths(paths, indent_level=1): + """Format paths for inclusion in a script.""" + separator = ',\n' + indent_level * ' ' + return separator.join(paths) + +def _script(dest, executable, relative_paths_setup, path, initialization, + module_name, attrs, arguments, block_site=False): + if block_site: + dash_S = ' -S' + else: + dash_S = '' + contents = script_template % dict( + python=_safe_arg(executable), + dash_S=dash_S, + path=path, + module_name=module_name, + attrs=attrs, + arguments=arguments, + initialization=initialization, + relative_paths_setup=relative_paths_setup, + ) + return _write_script(dest, contents, 'script') + +if is_jython and jython_os_name == 'linux': + script_header = '#!/usr/bin/env %(python)s%(dash_S)s' +else: + script_header = '#!%(python)s%(dash_S)s' + +sys_path_template = '''\ +import sys +sys.path[0:0] = [ + %s, + ] +''' + +script_template = script_header + '''\ +%(relative_paths_setup)s + +import sys +sys.path[0:0] = [ + %(path)s, + ] + +%(initialization)s +import %(module_name)s + +if __name__ == '__main__': + %(module_name)s.%(attrs)s(%(arguments)s) +''' + +# These are used only by the older ``scripts`` function. + +def _pyscript(path, dest, executable, rsetup): + contents = py_script_template % dict( + python=_safe_arg(executable), + dash_S='', + path=path, + relative_paths_setup=rsetup, + ) + return _write_script(dest, contents, 'interpreter') + +py_script_template = script_header + '''\ +%(relative_paths_setup)s + +import sys + +sys.path[0:0] = [ + %(path)s, + ] + +_interactive = True +if len(sys.argv) > 1: + _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:') + _interactive = False + for (_opt, _val) in _options: + if _opt == '-i': + _interactive = True + elif _opt == '-c': + exec _val + elif _opt == '-m': + sys.argv[1:] = _args + _args = [] + __import__("runpy").run_module( + _val, {}, "__main__", alter_sys=True) + + if _args: + sys.argv[:] = _args + __file__ = _args[0] + del _options, _args + execfile(__file__) + +if _interactive: + del _interactive + __import__("code").interact(banner="", local=globals()) +''' + +# These are used only by the newer ``sitepackage_safe_scripts`` function. + +def _get_module_file(executable, name, silent=False): + """Return a module's file path. + + - executable is a path to the desired Python executable. + - name is the name of the (pure, not C) Python module. + """ + cmd = [executable, "-Sc", + "import imp; " + "fp, path, desc = imp.find_module(%r); " + "fp.close(); " + "print path" % (name,)] + env = os.environ.copy() + # We need to make sure that PYTHONPATH, which will often be set to + # include a custom buildout-generated site.py, is not set, or else + # we will not get an accurate value for the "real" site.py and + # sitecustomize.py. + env.pop('PYTHONPATH', None) + _proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + stdout, stderr = _proc.communicate(); + if _proc.returncode: + if not silent: + logger.info( + 'Could not find file for module %s:\n%s', name, stderr) + return None + # else: ... + res = stdout.strip() + if res.endswith('.pyc') or res.endswith('.pyo'): + raise RuntimeError('Cannot find uncompiled version of %s' % (name,)) + if not os.path.exists(res): + raise RuntimeError( + 'File does not exist for module %s:\n%s' % (name, res)) + return res + +def _generate_sitecustomize(dest, executable, initialization='', + exec_sitecustomize=False): + """Write a sitecustomize file with optional custom initialization. + + The created script will execute the underlying Python's + sitecustomize if exec_sitecustomize is True. + """ + sitecustomize_path = os.path.join(dest, 'sitecustomize.py') + sitecustomize = open(sitecustomize_path, 'w') + if initialization: + sitecustomize.write(initialization + '\n') + if exec_sitecustomize: + real_sitecustomize_path = _get_module_file( + executable, 'sitecustomize', silent=True) + if real_sitecustomize_path: + real_sitecustomize = open(real_sitecustomize_path, 'r') + sitecustomize.write( + '\n# The following is from\n# %s\n' % + (real_sitecustomize_path,)) + sitecustomize.write(real_sitecustomize.read()) + real_sitecustomize.close() + sitecustomize.close() + return sitecustomize_path + +def _generate_site(dest, working_set, executable, extra_paths=(), + include_site_packages=False, relative_paths=False): + """Write a site.py file with eggs from working_set. + + extra_paths will be added to the path. If include_site_packages is True, + paths from the underlying Python will be added. + """ + path = _get_path(working_set, extra_paths) + site_path = os.path.join(dest, 'site.py') + original_path_setup = preamble = '' + if include_site_packages: + stdlib, site_paths = _get_system_paths(executable) + # We want to make sure that paths from site-packages, such as those + # allowed by allowed_eggs_from_site_packages, always come last, or + # else site-packages paths may include packages that mask the eggs we + # really want. + path = [p for p in path if p not in site_paths] + # Now we set up the code we need. + original_path_setup = original_path_snippet % ( + _format_paths((repr(p) for p in site_paths), 2),) + distribution = working_set.find( + pkg_resources.Requirement.parse('setuptools')) + if distribution is not None: + # We need to worry about namespace packages. + if relative_paths: + location = _relativitize( + distribution.location, + os.path.normcase(os.path.abspath(site_path)), + relative_paths) + else: + location = repr(distribution.location) + preamble = namespace_include_site_packages_setup % (location,) + original_path_setup = ( + addsitedir_namespace_originalpackages_snippet + + original_path_setup) + else: + preamble = '\n setuptools_path = None' + egg_path_string, relative_preamble = _relative_path_and_setup( + site_path, path, relative_paths, indent_level=2, omit_os_import=True) + if relative_preamble: + relative_preamble = '\n'.join( + [(line and ' %s' % (line,) or line) + for line in relative_preamble.split('\n')]) + preamble = relative_preamble + preamble + addsitepackages_marker = 'def addsitepackages(' + enableusersite_marker = 'ENABLE_USER_SITE = ' + successful_rewrite = False + real_site_path = _get_module_file(executable, 'site') + real_site = open(real_site_path, 'r') + site = open(site_path, 'w') + try: + for line in real_site.readlines(): + if line.startswith(enableusersite_marker): + site.write(enableusersite_marker) + site.write('False # buildout does not support user sites.\n') + elif line.startswith(addsitepackages_marker): + site.write(addsitepackages_script % ( + preamble, egg_path_string, original_path_setup)) + site.write(line[len(addsitepackages_marker):]) + successful_rewrite = True + else: + site.write(line) + finally: + site.close() + real_site.close() + if not successful_rewrite: + raise RuntimeError( + 'Buildout did not successfully rewrite %s to %s' % + (real_site_path, site_path)) + return site_path + +namespace_include_site_packages_setup = ''' + setuptools_path = %s + sys.path.append(setuptools_path) + known_paths.add(os.path.normcase(setuptools_path)) + import pkg_resources''' + +addsitedir_namespace_originalpackages_snippet = ''' + pkg_resources.working_set.add_entry(sitedir)''' + +original_path_snippet = ''' + sys.__egginsert = len(buildout_paths) # Support distribute. + original_paths = [ + %s + ] + for path in original_paths: + if path == setuptools_path or path not in known_paths: + addsitedir(path, known_paths)''' + +addsitepackages_script = '''\ +def addsitepackages(known_paths): + """Add site packages, as determined by zc.buildout. + + See original_addsitepackages, below, for the original version."""%s + buildout_paths = [ + %s + ] + for path in buildout_paths: + sitedir, sitedircase = makepath(path) + if not sitedircase in known_paths and os.path.exists(sitedir): + sys.path.append(sitedir) + known_paths.add(sitedircase)%s + return known_paths + +def original_addsitepackages(''' + +def _generate_interpreter(name, dest, executable, site_py_dest, + relative_paths=False): + """Write an interpreter script, using the site.py approach.""" + full_name = os.path.join(dest, name) + site_py_dest_string, rpsetup = _relative_path_and_setup( + full_name, [site_py_dest], relative_paths, omit_os_import=True) + if rpsetup: + rpsetup += "\n" + if sys.platform == 'win32': + windows_import = '\nimport subprocess' + # os.exec* is a mess on Windows, particularly if the path + # to the executable has spaces and the Python is using MSVCRT. + # The standard fix is to surround the executable's path with quotes, + # but that has been unreliable in testing. + # + # Here's a demonstration of the problem. Given a Python + # compiled with a MSVCRT-based compiler, such as the free Visual + # C++ 2008 Express Edition, and an executable path with spaces + # in it such as the below, we see the following. + # + # >>> import os + # >>> p0 = 'C:\\Documents and Settings\\Administrator\\My Documents\\Downloads\\Python-2.6.4\\PCbuild\\python.exe' + # >>> os.path.exists(p0) + # True + # >>> os.execv(p0, []) + # Traceback (most recent call last): + # File "<stdin>", line 1, in <module> + # OSError: [Errno 22] Invalid argument + # + # That seems like a standard problem. The standard solution is + # to quote the path (see, for instance + # http://bugs.python.org/issue436259). However, this solution, + # and other variations, fail: + # + # >>> p1 = '"C:\\Documents and Settings\\Administrator\\My Documents\\Downloads\\Python-2.6.4\\PCbuild\\python.exe"' + # >>> os.execv(p1, []) + # Traceback (most recent call last): + # File "<stdin>", line 1, in <module> + # OSError: [Errno 22] Invalid argument + # + # We simply use subprocess instead, since it handles everything + # nicely, and the transparency of exec* (that is, not running, + # perhaps unexpectedly, in a subprocess) is arguably not a + # necessity, at least for many use cases. + execute = 'subprocess.call(argv, env=environ)' + else: + windows_import = '' + execute = 'os.execve(sys.executable, argv, environ)' + contents = interpreter_template % dict( + python=_safe_arg(executable), + dash_S=' -S', + site_dest=site_py_dest_string, + relative_paths_setup=rpsetup, + windows_import=windows_import, + execute=execute, + ) + return _write_script(full_name, contents, 'interpreter') + +interpreter_template = script_header + ''' +import os +import sys%(windows_import)s +%(relative_paths_setup)s +argv = [sys.executable] + sys.argv[1:] +environ = os.environ.copy() +path = %(site_dest)s +if environ.get('PYTHONPATH'): + path = os.pathsep.join([path, environ['PYTHONPATH']]) +environ['PYTHONPATH'] = path +%(execute)s +''' + +# End of script generation code. +############################################################################ + +runsetup_template = """ +import sys +sys.path.insert(0, %(setupdir)r) +sys.path.insert(0, %(setuptools)r) +import os, setuptools + +__file__ = %(__file__)r + +os.chdir(%(setupdir)r) +sys.argv[0] = %(setup)r +execfile(%(setup)r) +""" + + +class VersionConflict(zc.buildout.UserError): + + def __init__(self, err, ws): + ws = list(ws) + ws.sort() + self.err, self.ws = err, ws + + def __str__(self): + existing_dist, req = self.err + result = ["There is a version conflict.", + "We already have: %s" % existing_dist, + ] + for dist in self.ws: + if req in dist.requires(): + result.append("but %s requires %r." % (dist, str(req))) + return '\n'.join(result) + + +class MissingDistribution(zc.buildout.UserError): + + def __init__(self, req, ws): + ws = list(ws) + ws.sort() + self.data = req, ws + + def __str__(self): + req, ws = self.data + return "Couldn't find a distribution for %r." % str(req) + +def _log_requirement(ws, req): + ws = list(ws) + ws.sort() + for dist in ws: + if req in dist.requires(): + logger.debug(" required by %s." % dist) + +def _fix_file_links(links): + for link in links: + if link.startswith('file://') and link[-1] != '/': + if os.path.isdir(link[7:]): + # work around excessive restriction in setuptools: + link += '/' + yield link + +_final_parts = '*final-', '*final' +def _final_version(parsed_version): + for part in parsed_version: + if (part[:1] == '*') and (part not in _final_parts): + return False + return True + +def redo_pyc(egg): + if not os.path.isdir(egg): + return + for dirpath, dirnames, filenames in os.walk(egg): + for filename in filenames: + if not filename.endswith('.py'): + continue + filepath = os.path.join(dirpath, filename) + if not (os.path.exists(filepath+'c') + or os.path.exists(filepath+'o')): + # If it wasn't compiled, it may not be compilable + continue + + # OK, it looks like we should try to compile. + + # Remove old files. + for suffix in 'co': + if os.path.exists(filepath+suffix): + os.remove(filepath+suffix) + + # Compile under current optimization + try: + py_compile.compile(filepath) + except py_compile.PyCompileError: + logger.warning("Couldn't compile %s", filepath) + else: + # Recompile under other optimization. :) + args = [_safe_arg(sys.executable)] + if __debug__: + args.append('-O') + args.extend(['-m', 'py_compile', _safe_arg(filepath)]) + + if is_jython: + subprocess.call([sys.executable, args]) + else: + os.spawnv(os.P_WAIT, sys.executable, args) + |