source: TI12-security/trunk/NDGSecurity/python/buildout/ndgsecurity/eggs/zc.buildout-1.2.1-py2.5.egg/zc/buildout/easy_install.py @ 7081

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/NDGSecurity/python/buildout/ndgsecurity/eggs/zc.buildout-1.2.1-py2.5.egg/zc/buildout/easy_install.py@7081
Revision 7081, 39.8 KB checked in by pjkersha, 10 years ago (diff)
  • Property svn:keywords set to Id
Line 
1#############################################################################
2#
3# Copyright (c) 2005 Zope Corporation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Python easy_install API
15
16This module provides a high-level Python API for installing packages.
17It doesn't install scripts.  It uses setuptools and requires it to be
18installed.
19
20$Id$
21"""
22
23import distutils.errors
24import glob
25import logging
26import os
27import pkg_resources
28import py_compile
29import re
30import setuptools.archive_util
31import setuptools.command.setopt
32import setuptools.package_index
33import shutil
34import subprocess
35import sys
36import tempfile
37import urlparse
38import zc.buildout
39import zipimport
40
41_oprp = getattr(os.path, 'realpath', lambda path: path)
42def realpath(path):
43    return os.path.normcase(os.path.abspath(_oprp(path)))
44
45default_index_url = os.environ.get(
46    'buildout-testing-index-url',
47    'http://pypi.python.org/simple',
48    )
49
50logger = logging.getLogger('zc.buildout.easy_install')
51
52url_match = re.compile('[a-z0-9+.-]+://').match
53
54is_win32 = sys.platform == 'win32'
55is_jython = sys.platform.startswith('java')
56
57if is_jython:
58    import subprocess
59    import java.lang.System
60    jython_os_name = (java.lang.System.getProperties()['os.name']).lower()
61
62
63setuptools_loc = pkg_resources.working_set.find(
64    pkg_resources.Requirement.parse('setuptools')
65    ).location
66
67# Include buildout and setuptools eggs in paths
68buildout_and_setuptools_path = [
69    setuptools_loc,
70    pkg_resources.working_set.find(
71        pkg_resources.Requirement.parse('zc.buildout')).location,
72    ]
73
74class IncompatibleVersionError(zc.buildout.UserError):
75    """A specified version is incompatible with a given requirement.
76    """
77
78_versions = {sys.executable: '%d.%d' % sys.version_info[:2]}
79def _get_version(executable):
80    try:
81        return _versions[executable]
82    except KeyError:
83        cmd = _safe_arg(executable) + ' -V'
84        p = subprocess.Popen(cmd,
85                             shell=True,
86                             stdin=subprocess.PIPE,
87                             stdout=subprocess.PIPE,
88                             stderr=subprocess.STDOUT,
89                             close_fds=not is_win32)
90        i, o = (p.stdin, p.stdout)
91        i.close()
92        version = o.read().strip()
93        o.close()
94        pystring, version = version.split()
95        assert pystring == 'Python'
96        version = re.match('(\d[.]\d)([.].*\d)?$', version).group(1)
97        _versions[executable] = version
98        return version
99
100FILE_SCHEME = re.compile('file://', re.I).match
101
102class AllowHostsPackageIndex(setuptools.package_index.PackageIndex):
103    """Will allow urls that are local to the system.
104
105    No matter what is allow_hosts.
106    """
107    def url_ok(self, url, fatal=False):
108        if FILE_SCHEME(url):
109            return True
110        return setuptools.package_index.PackageIndex.url_ok(self, url, False)
111
112
113_indexes = {}
114def _get_index(executable, index_url, find_links, allow_hosts=('*',)):
115    key = executable, index_url, tuple(find_links)
116    index = _indexes.get(key)
117    if index is not None:
118        return index
119
120    if index_url is None:
121        index_url = default_index_url
122    index = AllowHostsPackageIndex(
123        index_url, hosts=allow_hosts, python=_get_version(executable)
124        )
125
126    if find_links:
127        index.add_find_links(find_links)
128
129    _indexes[key] = index
130    return index
131
132clear_index_cache = _indexes.clear
133
134if is_win32:
135    # work around spawn lamosity on windows
136    # XXX need safe quoting (see the subproces.list2cmdline) and test
137    def _safe_arg(arg):
138        return '"%s"' % arg
139else:
140    _safe_arg = str
141
142_easy_install_cmd = _safe_arg(
143    'from setuptools.command.easy_install import main; main()'
144    )
145
146class Installer:
147
148    _versions = {}
149    _download_cache = None
150    _install_from_cache = False
151    _prefer_final = True
152    _use_dependency_links = True
153    _allow_picked_versions = True
154    _always_unzip = False
155
156    def __init__(self,
157                 dest=None,
158                 links=(),
159                 index=None,
160                 executable=sys.executable,
161                 always_unzip=None,
162                 path=None,
163                 newest=True,
164                 versions=None,
165                 use_dependency_links=None,
166                 allow_hosts=('*',)
167                 ):
168        self._dest = dest
169        self._allow_hosts = allow_hosts
170
171        if self._install_from_cache:
172            if not self._download_cache:
173                raise ValueError("install_from_cache set to true with no"
174                                 " download cache")
175            links = ()
176            index = 'file://' + self._download_cache
177
178        if use_dependency_links is not None:
179            self._use_dependency_links = use_dependency_links
180        self._links = links = list(_fix_file_links(links))
181        if self._download_cache and (self._download_cache not in links):
182            links.insert(0, self._download_cache)
183
184        self._index_url = index
185        self._executable = executable
186        if always_unzip is not None:
187            self._always_unzip = always_unzip
188        path = (path and path[:] or []) + buildout_and_setuptools_path
189        if dest is not None and dest not in path:
190            path.insert(0, dest)
191        self._path = path
192        if self._dest is None:
193            newest = False
194        self._newest = newest
195        self._env = pkg_resources.Environment(path,
196                                              python=_get_version(executable))
197        self._index = _get_index(executable, index, links, self._allow_hosts)
198
199        if versions is not None:
200            self._versions = versions
201
202    def _satisfied(self, req, source=None):
203        dists = [dist for dist in self._env[req.project_name] if dist in req]
204        if not dists:
205            logger.debug('We have no distributions for %s that satisfies %r.',
206                         req.project_name, str(req))
207
208            return None, self._obtain(req, source)
209
210        # Note that dists are sorted from best to worst, as promised by
211        # env.__getitem__
212
213        for dist in dists:
214            if (dist.precedence == pkg_resources.DEVELOP_DIST):
215                logger.debug('We have a develop egg: %s', dist)
216                return dist, None
217
218        # Special common case, we have a specification for a single version:
219        specs = req.specs
220        if len(specs) == 1 and specs[0][0] == '==':
221            logger.debug('We have the distribution that satisfies %r.',
222                         str(req))
223            return dists[0], None
224
225        if self._prefer_final:
226            fdists = [dist for dist in dists
227                      if _final_version(dist.parsed_version)
228                      ]
229            if fdists:
230                # There are final dists, so only use those
231                dists = fdists
232
233        if not self._newest:
234            # We don't need the newest, so we'll use the newest one we
235            # find, which is the first returned by
236            # Environment.__getitem__.
237            return dists[0], None
238
239        best_we_have = dists[0] # Because dists are sorted from best to worst
240
241        # We have some installed distros.  There might, theoretically, be
242        # newer ones.  Let's find out which ones are available and see if
243        # any are newer.  We only do this if we're willing to install
244        # something, which is only true if dest is not None:
245
246        if self._dest is not None:
247            best_available = self._obtain(req, source)
248        else:
249            best_available = None
250
251        if best_available is None:
252            # That's a bit odd.  There aren't any distros available.
253            # We should use the best one we have that meets the requirement.
254            logger.debug(
255                'There are no distros available that meet %r.\n'
256                'Using our best, %s.',
257                str(req), best_available)
258            return best_we_have, None
259
260        if self._prefer_final:
261            if _final_version(best_available.parsed_version):
262                if _final_version(best_we_have.parsed_version):
263                    if (best_we_have.parsed_version
264                        <
265                        best_available.parsed_version
266                        ):
267                        return None, best_available
268                else:
269                    return None, best_available
270            else:
271                if (not _final_version(best_we_have.parsed_version)
272                    and
273                    (best_we_have.parsed_version
274                     <
275                     best_available.parsed_version
276                     )
277                    ):
278                    return None, best_available
279        else:
280            if (best_we_have.parsed_version
281                <
282                best_available.parsed_version
283                ):
284                return None, best_available
285
286        logger.debug(
287            'We have the best distribution that satisfies %r.',
288            str(req))
289        return best_we_have, None
290
291    def _load_dist(self, dist):
292        dists = pkg_resources.Environment(
293            dist.location,
294            python=_get_version(self._executable),
295            )[dist.project_name]
296        assert len(dists) == 1
297        return dists[0]
298
299    def _call_easy_install(self, spec, ws, dest, dist):
300
301        tmp = tempfile.mkdtemp(dir=dest)
302        try:
303            path = self._get_dist(
304                self._constrain(pkg_resources.Requirement.parse('setuptools')),
305                ws, False,
306                )[0].location
307
308            args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(tmp))
309            if self._always_unzip:
310                args += ('-Z', )
311            level = logger.getEffectiveLevel()
312            if level > 0:
313                args += ('-q', )
314            elif level < 0:
315                args += ('-v', )
316
317            args += (_safe_arg(spec), )
318
319            if level <= logging.DEBUG:
320                logger.debug('Running easy_install:\n%s "%s"\npath=%s\n',
321                             self._executable, '" "'.join(args), path)
322
323            if is_jython:
324                extra_env = dict(os.environ, PYTHONPATH=path)
325            else:
326                args += (dict(os.environ, PYTHONPATH=path), )
327
328            sys.stdout.flush() # We want any pending output first
329
330            if is_jython:
331                exit_code = subprocess.Popen(
332                [_safe_arg(self._executable)] + list(args),
333                env=extra_env).wait()
334            else:
335                exit_code = os.spawnle(
336                    os.P_WAIT, self._executable, _safe_arg (self._executable),
337                    *args)
338
339            dists = []
340            env = pkg_resources.Environment(
341                [tmp],
342                python=_get_version(self._executable),
343                )
344            for project in env:
345                dists.extend(env[project])
346
347            if exit_code:
348                logger.error(
349                    "An error occured when trying to install %s."
350                    "Look above this message for any errors that"
351                    "were output by easy_install.",
352                    dist)
353
354            if not dists:
355                raise zc.buildout.UserError("Couldn't install: %s" % dist)
356
357            if len(dists) > 1:
358                logger.warn("Installing %s\n"
359                            "caused multiple distributions to be installed:\n"
360                            "%s\n",
361                            dist, '\n'.join(map(str, dists)))
362            else:
363                d = dists[0]
364                if d.project_name != dist.project_name:
365                    logger.warn("Installing %s\n"
366                                "Caused installation of a distribution:\n"
367                                "%s\n"
368                                "with a different project name.",
369                                dist, d)
370                if d.version != dist.version:
371                    logger.warn("Installing %s\n"
372                                "Caused installation of a distribution:\n"
373                                "%s\n"
374                                "with a different version.",
375                                dist, d)
376
377            result = []
378            for d in dists:
379                newloc = os.path.join(dest, os.path.basename(d.location))
380                if os.path.exists(newloc):
381                    if os.path.isdir(newloc):
382                        shutil.rmtree(newloc)
383                    else:
384                        os.remove(newloc)
385                os.rename(d.location, newloc)
386
387                [d] = pkg_resources.Environment(
388                    [newloc],
389                    python=_get_version(self._executable),
390                    )[d.project_name]
391
392                result.append(d)
393
394            return result
395
396        finally:
397            shutil.rmtree(tmp)
398
399    def _obtain(self, requirement, source=None):
400        # initialize out index for this project:
401        index = self._index
402
403        if index.obtain(requirement) is None:
404            # Nothing is available.
405            return None
406
407        # Filter the available dists for the requirement and source flag
408        dists = [dist for dist in index[requirement.project_name]
409                 if ((dist in requirement)
410                     and
411                     ((not source) or
412                      (dist.precedence == pkg_resources.SOURCE_DIST)
413                      )
414                     )
415                 ]
416
417        # If we prefer final dists, filter for final and use the
418        # result if it is non empty.
419        if self._prefer_final:
420            fdists = [dist for dist in dists
421                      if _final_version(dist.parsed_version)
422                      ]
423            if fdists:
424                # There are final dists, so only use those
425                dists = fdists
426
427        # Now find the best one:
428        best = []
429        bestv = ()
430        for dist in dists:
431            distv = dist.parsed_version
432            if distv > bestv:
433                best = [dist]
434                bestv = distv
435            elif distv == bestv:
436                best.append(dist)
437
438        if not best:
439            return None
440
441        if len(best) == 1:
442            return best[0]
443
444        if self._download_cache:
445            for dist in best:
446                if (realpath(os.path.dirname(dist.location))
447                    ==
448                    self._download_cache
449                    ):
450                    return dist
451
452        best.sort()
453        return best[-1]
454
455    def _fetch(self, dist, tmp, download_cache):
456        if (download_cache
457            and (realpath(os.path.dirname(dist.location)) == download_cache)
458            ):
459            return dist
460
461        new_location = self._index.download(dist.location, tmp)
462        if (download_cache
463            and (realpath(new_location) == realpath(dist.location))
464            and os.path.isfile(new_location)
465            ):
466            # setuptools avoids making extra copies, but we want to copy
467            # to the download cache
468            shutil.copy2(new_location, tmp)
469            new_location = os.path.join(tmp, os.path.basename(new_location))
470
471        return dist.clone(location=new_location)
472
473    def _get_dist(self, requirement, ws, always_unzip):
474
475        __doing__ = 'Getting distribution for %r.', str(requirement)
476
477        # Maybe an existing dist is already the best dist that satisfies the
478        # requirement
479        dist, avail = self._satisfied(requirement)
480
481        if dist is None:
482            if self._dest is not None:
483                logger.info(*__doing__)
484
485            # Retrieve the dist:
486            if avail is None:
487                raise MissingDistribution(requirement, ws)
488
489            # We may overwrite distributions, so clear importer
490            # cache.
491            sys.path_importer_cache.clear()
492
493            tmp = self._download_cache
494            if tmp is None:
495                tmp = tempfile.mkdtemp('get_dist')
496
497            try:
498                dist = self._fetch(avail, tmp, self._download_cache)
499
500                if dist is None:
501                    raise zc.buildout.UserError(
502                        "Couln't download distribution %s." % avail)
503
504                if dist.precedence == pkg_resources.EGG_DIST:
505                    # It's already an egg, just fetch it into the dest
506
507                    newloc = os.path.join(
508                        self._dest, os.path.basename(dist.location))
509
510                    if os.path.isdir(dist.location):
511                        # we got a directory. It must have been
512                        # obtained locally.  Just copy it.
513                        shutil.copytree(dist.location, newloc)
514                    else:
515
516                        if self._always_unzip:
517                            should_unzip = True
518                        else:
519                            metadata = pkg_resources.EggMetadata(
520                                zipimport.zipimporter(dist.location)
521                                )
522                            should_unzip = (
523                                metadata.has_metadata('not-zip-safe')
524                                or
525                                not metadata.has_metadata('zip-safe')
526                                )
527
528                        if should_unzip:
529                            setuptools.archive_util.unpack_archive(
530                                dist.location, newloc)
531                        else:
532                            shutil.copyfile(dist.location, newloc)
533
534                    redo_pyc(newloc)
535
536                    # Getting the dist from the environment causes the
537                    # distribution meta data to be read.  Cloning isn't
538                    # good enough.
539                    dists = pkg_resources.Environment(
540                        [newloc],
541                        python=_get_version(self._executable),
542                        )[dist.project_name]
543                else:
544                    # It's some other kind of dist.  We'll let easy_install
545                    # deal with it:
546                    dists = self._call_easy_install(
547                        dist.location, ws, self._dest, dist)
548                    for dist in dists:
549                        redo_pyc(dist.location)
550
551            finally:
552                if tmp != self._download_cache:
553                    shutil.rmtree(tmp)
554
555            self._env.scan([self._dest])
556            dist = self._env.best_match(requirement, ws)
557            logger.info("Got %s.", dist)
558
559        else:
560            dists = [dist]
561
562        for dist in dists:
563            if (dist.has_metadata('dependency_links.txt')
564                and not self._install_from_cache
565                and self._use_dependency_links
566                ):
567                for link in dist.get_metadata_lines('dependency_links.txt'):
568                    link = link.strip()
569                    if link not in self._links:
570                        logger.debug('Adding find link %r from %s', link, dist)
571                        self._links.append(link)
572                        self._index = _get_index(self._executable,
573                                                 self._index_url, self._links,
574                                                 self._allow_hosts)
575
576        for dist in dists:
577            # Check whether we picked a version and, if we did, report it:
578            if not (
579                dist.precedence == pkg_resources.DEVELOP_DIST
580                or
581                (len(requirement.specs) == 1
582                 and
583                 requirement.specs[0][0] == '==')
584                ):
585                logger.debug('Picked: %s = %s',
586                             dist.project_name, dist.version)
587                if not self._allow_picked_versions:
588                    raise zc.buildout.UserError(
589                        'Picked: %s = %s' % (dist.project_name, dist.version)
590                        )
591
592        return dists
593
594    def _maybe_add_setuptools(self, ws, dist):
595        if dist.has_metadata('namespace_packages.txt'):
596            for r in dist.requires():
597                if r.project_name == 'setuptools':
598                    break
599            else:
600                # We have a namespace package but no requirement for setuptools
601                if dist.precedence == pkg_resources.DEVELOP_DIST:
602                    logger.warn(
603                        "Develop distribution: %s\n"
604                        "uses namespace packages but the distribution "
605                        "does not require setuptools.",
606                        dist)
607                requirement = self._constrain(
608                    pkg_resources.Requirement.parse('setuptools')
609                    )
610                if ws.find(requirement) is None:
611                    for dist in self._get_dist(requirement, ws, False):
612                        ws.add(dist)
613
614
615    def _constrain(self, requirement):
616        version = self._versions.get(requirement.project_name)
617        if version:
618            if version not in requirement:
619                logger.error("The version, %s, is not consistent with the "
620                             "requirement, %r.", version, str(requirement))
621                raise IncompatibleVersionError("Bad version", version)
622
623            requirement = pkg_resources.Requirement.parse(
624                "%s ==%s" % (requirement.project_name, version))
625
626        return requirement
627
628    def install(self, specs, working_set=None):
629
630        logger.debug('Installing %s.', repr(specs)[1:-1])
631
632        path = self._path
633        dest = self._dest
634        if dest is not None and dest not in path:
635            path.insert(0, dest)
636
637        requirements = [self._constrain(pkg_resources.Requirement.parse(spec))
638                        for spec in specs]
639
640
641
642        if working_set is None:
643            ws = pkg_resources.WorkingSet([])
644        else:
645            ws = working_set
646
647        for requirement in requirements:
648            for dist in self._get_dist(requirement, ws, self._always_unzip):
649                ws.add(dist)
650                self._maybe_add_setuptools(ws, dist)
651
652        # OK, we have the requested distributions and they're in the working
653        # set, but they may have unmet requirements.  We'll simply keep
654        # trying to resolve requirements, adding missing requirements as they
655        # are reported.
656        #
657        # Note that we don't pass in the environment, because we want
658        # to look for new eggs unless what we have is the best that
659        # matches the requirement.
660        while 1:
661            try:
662                ws.resolve(requirements)
663            except pkg_resources.DistributionNotFound, err:
664                [requirement] = err
665                requirement = self._constrain(requirement)
666                if dest:
667                    logger.debug('Getting required %r', str(requirement))
668                else:
669                    logger.debug('Adding required %r', str(requirement))
670                _log_requirement(ws, requirement)
671
672                for dist in self._get_dist(requirement, ws, self._always_unzip
673                                           ):
674
675                    ws.add(dist)
676                    self._maybe_add_setuptools(ws, dist)
677            except pkg_resources.VersionConflict, err:
678                raise VersionConflict(err, ws)
679            else:
680                break
681
682        return ws
683
684    def build(self, spec, build_ext):
685
686        requirement = self._constrain(pkg_resources.Requirement.parse(spec))
687
688        dist, avail = self._satisfied(requirement, 1)
689        if dist is not None:
690            return [dist.location]
691
692        # Retrieve the dist:
693        if avail is None:
694            raise zc.buildout.UserError(
695                "Couldn't find a source distribution for %r."
696                % str(requirement))
697
698        logger.debug('Building %r', spec)
699
700        tmp = self._download_cache
701        if tmp is None:
702            tmp = tempfile.mkdtemp('get_dist')
703
704        try:
705            dist = self._fetch(avail, tmp, self._download_cache)
706
707            build_tmp = tempfile.mkdtemp('build')
708            try:
709                setuptools.archive_util.unpack_archive(dist.location,
710                                                       build_tmp)
711                if os.path.exists(os.path.join(build_tmp, 'setup.py')):
712                    base = build_tmp
713                else:
714                    setups = glob.glob(
715                        os.path.join(build_tmp, '*', 'setup.py'))
716                    if not setups:
717                        raise distutils.errors.DistutilsError(
718                            "Couldn't find a setup script in %s"
719                            % os.path.basename(dist.location)
720                            )
721                    if len(setups) > 1:
722                        raise distutils.errors.DistutilsError(
723                            "Multiple setup scripts in %s"
724                            % os.path.basename(dist.location)
725                            )
726                    base = os.path.dirname(setups[0])
727
728                setup_cfg = os.path.join(base, 'setup.cfg')
729                if not os.path.exists(setup_cfg):
730                    f = open(setup_cfg, 'w')
731                    f.close()
732                setuptools.command.setopt.edit_config(
733                    setup_cfg, dict(build_ext=build_ext))
734
735                dists = self._call_easy_install(
736                    base, pkg_resources.WorkingSet(),
737                    self._dest, dist)
738
739                for dist in dists:
740                    redo_pyc(dist.location)
741
742                return [dist.location for dist in dists]
743            finally:
744                shutil.rmtree(build_tmp)
745
746        finally:
747            if tmp != self._download_cache:
748                shutil.rmtree(tmp)
749
750def default_versions(versions=None):
751    old = Installer._versions
752    if versions is not None:
753        Installer._versions = versions
754    return old
755
756def download_cache(path=-1):
757    old = Installer._download_cache
758    if path != -1:
759        if path:
760            path = realpath(path)
761        Installer._download_cache = path
762    return old
763
764def install_from_cache(setting=None):
765    old = Installer._install_from_cache
766    if setting is not None:
767        Installer._install_from_cache = bool(setting)
768    return old
769
770def prefer_final(setting=None):
771    old = Installer._prefer_final
772    if setting is not None:
773        Installer._prefer_final = bool(setting)
774    return old
775
776def use_dependency_links(setting=None):
777    old = Installer._use_dependency_links
778    if setting is not None:
779        Installer._use_dependency_links = bool(setting)
780    return old
781
782def allow_picked_versions(setting=None):
783    old = Installer._allow_picked_versions
784    if setting is not None:
785        Installer._allow_picked_versions = bool(setting)
786    return old
787
788def always_unzip(setting=None):
789    old = Installer._always_unzip
790    if setting is not None:
791        Installer._always_unzip = bool(setting)
792    return old
793
794def install(specs, dest,
795            links=(), index=None,
796            executable=sys.executable, always_unzip=None,
797            path=None, working_set=None, newest=True, versions=None,
798            use_dependency_links=None, allow_hosts=('*',)):
799    installer = Installer(dest, links, index, executable, always_unzip, path,
800                          newest, versions, use_dependency_links,
801                          allow_hosts=allow_hosts)
802    return installer.install(specs, working_set)
803
804
805def build(spec, dest, build_ext,
806          links=(), index=None,
807          executable=sys.executable,
808          path=None, newest=True, versions=None, allow_hosts=('*',)):
809    installer = Installer(dest, links, index, executable, True, path, newest,
810                          versions, allow_hosts=allow_hosts)
811    return installer.build(spec, build_ext)
812
813
814
815def _rm(*paths):
816    for path in paths:
817        if os.path.isdir(path):
818            shutil.rmtree(path)
819        elif os.path.exists(path):
820            os.remove(path)
821
822def _copyeggs(src, dest, suffix, undo):
823    result = []
824    undo.append(lambda : _rm(*result))
825    for name in os.listdir(src):
826        if name.endswith(suffix):
827            new = os.path.join(dest, name)
828            _rm(new)
829            os.rename(os.path.join(src, name), new)
830            result.append(new)
831
832    assert len(result) == 1, str(result)
833    undo.pop()
834
835    return result[0]
836
837def develop(setup, dest,
838            build_ext=None,
839            executable=sys.executable):
840
841    if os.path.isdir(setup):
842        directory = setup
843        setup = os.path.join(directory, 'setup.py')
844    else:
845        directory = os.path.dirname(setup)
846
847    undo = []
848    try:
849        if build_ext:
850            setup_cfg = os.path.join(directory, 'setup.cfg')
851            if os.path.exists(setup_cfg):
852                os.rename(setup_cfg, setup_cfg+'-develop-aside')
853                def restore_old_setup():
854                    if os.path.exists(setup_cfg):
855                        os.remove(setup_cfg)
856                    os.rename(setup_cfg+'-develop-aside', setup_cfg)
857                undo.append(restore_old_setup)
858            else:
859                open(setup_cfg, 'w')
860                undo.append(lambda: os.remove(setup_cfg))
861            setuptools.command.setopt.edit_config(
862                setup_cfg, dict(build_ext=build_ext))
863
864        fd, tsetup = tempfile.mkstemp()
865        undo.append(lambda: os.remove(tsetup))
866        undo.append(lambda: os.close(fd))
867
868        os.write(fd, runsetup_template % dict(
869            setuptools=setuptools_loc,
870            setupdir=directory,
871            setup=setup,
872            __file__ = setup,
873            ))
874
875        tmp3 = tempfile.mkdtemp('build', dir=dest)
876        undo.append(lambda : shutil.rmtree(tmp3))
877
878        args = [
879            zc.buildout.easy_install._safe_arg(tsetup),
880            '-q', 'develop', '-mxN',
881            '-d', _safe_arg(tmp3),
882            ]
883
884        log_level = logger.getEffectiveLevel()
885        if log_level <= 0:
886            if log_level == 0:
887                del args[1]
888            else:
889                args[1] == '-v'
890        if log_level < logging.DEBUG:
891            logger.debug("in: %r\n%s", directory, ' '.join(args))
892
893        if is_jython:
894            assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0
895        else:
896            assert os.spawnl(os.P_WAIT, executable, _safe_arg(executable),
897                             *args) == 0
898
899        return _copyeggs(tmp3, dest, '.egg-link', undo)
900
901    finally:
902        undo.reverse()
903        [f() for f in undo]
904
905
906def working_set(specs, executable, path):
907    return install(specs, None, executable=executable, path=path)
908
909def scripts(reqs, working_set, executable, dest,
910            scripts=None,
911            extra_paths=(),
912            arguments='',
913            interpreter=None,
914            initialization='',
915            relative_paths=False,
916            ):
917
918    path = [dist.location for dist in working_set]
919    path.extend(extra_paths)
920    path = map(realpath, path)
921
922    generated = []
923
924    if isinstance(reqs, str):
925        raise TypeError('Expected iterable of requirements or entry points,'
926                        ' got string.')
927
928    if initialization:
929        initialization = '\n'+initialization+'\n'
930
931    entry_points = []
932    for req in reqs:
933        if isinstance(req, str):
934            req = pkg_resources.Requirement.parse(req)
935            dist = working_set.find(req)
936            for name in pkg_resources.get_entry_map(dist, 'console_scripts'):
937                entry_point = dist.get_entry_info('console_scripts', name)
938                entry_points.append(
939                    (name, entry_point.module_name,
940                     '.'.join(entry_point.attrs))
941                    )
942        else:
943            entry_points.append(req)
944
945    for name, module_name, attrs in entry_points:
946        if scripts is not None:
947            sname = scripts.get(name)
948            if sname is None:
949                continue
950        else:
951            sname = name
952
953        sname = os.path.join(dest, sname)
954        spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
955
956        generated.extend(
957            _script(module_name, attrs, spath, sname, executable, arguments,
958                    initialization, rpsetup)
959            )
960
961    if interpreter:
962        sname = os.path.join(dest, interpreter)
963        spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
964        generated.extend(_pyscript(spath, sname, executable, rpsetup))
965
966    return generated
967
968def _relative_path_and_setup(sname, path, relative_paths):
969    if relative_paths:
970        sname = os.path.abspath(sname)
971        spath = ',\n  '.join(
972            [_relativitize(path_item, sname, relative_paths)
973             for path_item in path]
974            )
975        rpsetup = relative_paths_setup
976        for i in range(_relative_depth(relative_paths, sname)):
977            rpsetup += "base = os.path.dirname(base)\n"
978    else:
979        spath = repr(path)[1:-1].replace(', ', ',\n  ')
980        rpsetup = ''
981    return spath, rpsetup
982
983
984def _relative_depth(common, path):
985    n = 0
986    while 1:
987        dirname = os.path.dirname(path)
988        if dirname == path:
989            raise AssertionError("dirname of %s is the same" % dirname)
990        if dirname == common:
991            break
992        n += 1
993        path = dirname
994    return n
995
996def _relative_path(common, path):
997    r = []
998    while 1:
999        dirname, basename = os.path.split(path)
1000        r.append(basename)
1001        if dirname == common:
1002            break
1003        if dirname == path:
1004            raise AssertionError("dirname of %s is the same" % dirname)
1005        path = dirname
1006    r.reverse()
1007    return os.path.join(*r)
1008
1009def _relativitize(path, script, relative_paths):
1010    if path == script:
1011        raise AssertionError("path == script")
1012    common = os.path.dirname(os.path.commonprefix([path, script]))
1013    if (common == relative_paths or
1014        common.startswith(os.path.join(relative_paths, ''))
1015        ):
1016        return "join(base, %r)" % _relative_path(common, path)
1017    else:
1018        return repr(path)
1019
1020
1021relative_paths_setup = """
1022import os
1023
1024join = os.path.join
1025base = os.path.dirname(__file__)
1026"""
1027
1028def _script(module_name, attrs, path, dest, executable, arguments,
1029            initialization, rsetup):
1030    generated = []
1031    script = dest
1032    if is_win32:
1033        dest += '-script.py'
1034
1035    contents = script_template % dict(
1036        python = _safe_arg(executable),
1037        path = path,
1038        module_name = module_name,
1039        attrs = attrs,
1040        arguments = arguments,
1041        initialization = initialization,
1042        relative_paths_setup = rsetup,
1043        )
1044    changed = not (os.path.exists(dest) and open(dest).read() == contents)
1045
1046    if is_win32:
1047        # generate exe file and give the script a magic name:
1048        exe = script+'.exe'
1049        new_data = pkg_resources.resource_string('setuptools', 'cli.exe')
1050        if not os.path.exists(exe) or (open(exe, 'rb').read() != new_data):
1051            # Only write it if it's different.
1052            open(exe, 'wb').write(new_data)
1053        generated.append(exe)
1054
1055    if changed:
1056        open(dest, 'w').write(contents)
1057        logger.info("Generated script %r.", script)
1058
1059        try:
1060            os.chmod(dest, 0755)
1061        except (AttributeError, os.error):
1062            pass
1063
1064    generated.append(dest)
1065    return generated
1066
1067if is_jython and jython_os_name == 'linux':
1068    script_header = '#!/usr/bin/env %(python)s'
1069else:
1070    script_header = '#!%(python)s'
1071
1072
1073script_template = script_header + '''\
1074
1075%(relative_paths_setup)s
1076import sys
1077sys.path[0:0] = [
1078  %(path)s,
1079  ]
1080%(initialization)s
1081import %(module_name)s
1082
1083if __name__ == '__main__':
1084    %(module_name)s.%(attrs)s(%(arguments)s)
1085'''
1086
1087
1088def _pyscript(path, dest, executable, rsetup):
1089    generated = []
1090    script = dest
1091    if is_win32:
1092        dest += '-script.py'
1093
1094    contents = py_script_template % dict(
1095        python = _safe_arg(executable),
1096        path = path,
1097        relative_paths_setup = rsetup,
1098        )
1099    changed = not (os.path.exists(dest) and open(dest).read() == contents)
1100
1101    if is_win32:
1102        # generate exe file and give the script a magic name:
1103        exe = script + '.exe'
1104        open(exe, 'wb').write(
1105            pkg_resources.resource_string('setuptools', 'cli.exe')
1106            )
1107        generated.append(exe)
1108
1109    if changed:
1110        open(dest, 'w').write(contents)
1111        try:
1112            os.chmod(dest,0755)
1113        except (AttributeError, os.error):
1114            pass
1115        logger.info("Generated interpreter %r.", script)
1116
1117    generated.append(dest)
1118    return generated
1119
1120py_script_template = script_header + '''\
1121
1122%(relative_paths_setup)s
1123import sys
1124
1125sys.path[0:0] = [
1126  %(path)s,
1127  ]
1128
1129_interactive = True
1130if len(sys.argv) > 1:
1131    import getopt
1132    _options, _args = getopt.getopt(sys.argv[1:], 'ic:')
1133    _interactive = False
1134    for (_opt, _val) in _options:
1135        if _opt == '-i':
1136            _interactive = True
1137        elif _opt == '-c':
1138            exec _val
1139
1140    if _args:
1141        sys.argv[:] = _args
1142        execfile(sys.argv[0])
1143
1144if _interactive:
1145    import code
1146    code.interact(banner="", local=globals())
1147'''
1148
1149runsetup_template = """
1150import sys
1151sys.path.insert(0, %(setupdir)r)
1152sys.path.insert(0, %(setuptools)r)
1153import os, setuptools
1154
1155__file__ = %(__file__)r
1156
1157os.chdir(%(setupdir)r)
1158sys.argv[0] = %(setup)r
1159execfile(%(setup)r)
1160"""
1161
1162class VersionConflict(zc.buildout.UserError):
1163
1164    def __init__(self, err, ws):
1165        ws = list(ws)
1166        ws.sort()
1167        self.err, self.ws = err, ws
1168
1169    def __str__(self):
1170        existing_dist, req = self.err
1171        result = ["There is a version conflict.",
1172                  "We already have: %s" % existing_dist,
1173                  ]
1174        for dist in self.ws:
1175            if req in dist.requires():
1176                result.append("but %s requires %r." % (dist, str(req)))
1177        return '\n'.join(result)
1178
1179class MissingDistribution(zc.buildout.UserError):
1180
1181    def __init__(self, req, ws):
1182        ws = list(ws)
1183        ws.sort()
1184        self.data = req, ws
1185
1186    def __str__(self):
1187        req, ws = self.data
1188        return "Couldn't find a distribution for %r." % str(req)
1189
1190def _log_requirement(ws, req):
1191    ws = list(ws)
1192    ws.sort()
1193    for dist in ws:
1194        if req in dist.requires():
1195            logger.debug("  required by %s." % dist)
1196
1197def _fix_file_links(links):
1198    for link in links:
1199        if link.startswith('file://') and link[-1] != '/':
1200            if os.path.isdir(link[7:]):
1201                # work around excessive restriction in setuptools:
1202                link += '/'
1203        yield link
1204
1205_final_parts = '*final-', '*final'
1206def _final_version(parsed_version):
1207    for part in parsed_version:
1208        if (part[:1] == '*') and (part not in _final_parts):
1209            return False
1210    return True
1211
1212def redo_pyc(egg):
1213    if not os.path.isdir(egg):
1214        return
1215    for dirpath, dirnames, filenames in os.walk(egg):
1216        for filename in filenames:
1217            if not filename.endswith('.py'):
1218                continue
1219            filepath = os.path.join(dirpath, filename)
1220            if not (os.path.exists(filepath+'c')
1221                    or os.path.exists(filepath+'o')):
1222                # If it wasn't compiled, it may not be compilable
1223                continue
1224
1225            # OK, it looks like we should try to compile.
1226
1227            # Remove old files.
1228            for suffix in 'co':
1229                if os.path.exists(filepath+suffix):
1230                    os.remove(filepath+suffix)
1231
1232            # Compile under current optimization
1233            try:
1234                py_compile.compile(filepath)
1235            except py_compile.PyCompileError:
1236                logger.warning("Couldn't compile %s", filepath)
1237            else:
1238                # Recompile under other optimization. :)
1239                args = [_safe_arg(sys.executable)]
1240                if __debug__:
1241                    args.append('-O')
1242                args.extend(['-m', 'py_compile', _safe_arg(filepath)])
1243
1244                if is_jython:
1245                    subprocess.call([sys.executable, args])
1246                else:
1247                    os.spawnv(os.P_WAIT, sys.executable, args)
1248
Note: See TracBrowser for help on using the repository browser.