]> git.rkrishnan.org Git - tahoe-lafs/zfec.git/blob - zfec/setuptools-0.6c16dev3.egg/setuptools/command/easy_install.py
8ac42b4d29e9a1ad8785384b6d1ff77d1a4ba3cb
[tahoe-lafs/zfec.git] / zfec / setuptools-0.6c16dev3.egg / setuptools / command / easy_install.py
1 #!python
2 """\
3 Easy Install
4 ------------
5
6 A tool for doing automatic download/extract/build of distutils-based Python
7 packages.  For detailed documentation, see the accompanying EasyInstall.txt
8 file, or visit the `EasyInstall home page`__.
9
10 __ http://peak.telecommunity.com/DevCenter/EasyInstall
11 """
12 import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
13 from glob import glob
14 from setuptools import Command, _dont_write_bytecode
15 from setuptools import __version__ as setuptools_version
16 from setuptools.sandbox import run_setup
17 from distutils import log, dir_util
18 from distutils.sysconfig import get_python_lib
19 from distutils.errors import DistutilsArgError, DistutilsOptionError, \
20     DistutilsError
21 from setuptools.archive_util import unpack_archive
22 from setuptools.package_index import PackageIndex, parse_bdist_wininst
23 from setuptools.package_index import URL_SCHEME
24 from setuptools.command import bdist_egg, egg_info
25 from pkg_resources import *
26 sys_executable = os.path.normpath(sys.executable)
27
28 __all__ = [
29     'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
30     'main', 'get_exe_prefixes',
31 ]
32
33 def samefile(p1,p2):
34     if hasattr(os.path,'samefile') and (
35         os.path.exists(p1) and os.path.exists(p2)
36     ):
37         return os.path.samefile(p1,p2)
38     return (
39         os.path.normpath(os.path.normcase(p1)) ==
40         os.path.normpath(os.path.normcase(p2))
41     )
42
43 class easy_install(Command):
44     """Manage a download/build/install process"""
45     description = "Find/get/install Python packages"
46     command_consumes_arguments = True
47
48     user_options = [
49         ('prefix=', None, "installation prefix"),
50         ("zip-ok", "z", "install package as a zipfile"),
51         ("multi-version", "m", "make apps have to require() a version"),
52         ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),
53         ("install-dir=", "d", "install package to DIR"),
54         ("script-dir=", "s", "install scripts to DIR"),
55         ("exclude-scripts", "x", "Don't install scripts"),
56         ("always-copy", "a", "Copy all needed packages to install dir"),
57         ("index-url=", "i", "base URL of Python Package Index"),
58         ("find-links=", "f", "additional URL(s) to search for packages"),
59         ("delete-conflicting", "D", "no longer needed; don't use this"),
60         ("ignore-conflicts-at-my-risk", None,
61             "no longer needed; don't use this"),
62         ("build-directory=", "b",
63             "download/extract/build in DIR; keep the results"),
64         ('optimize=', 'O',
65          "also compile with optimization: -O1 for \"python -O\", "
66          "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
67         ('record=', None,
68          "filename in which to record list of installed files"),
69         ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
70         ('site-dirs=','S',"list of directories where .pth files work"),
71         ('editable', 'e', "Install specified packages in editable form"),
72         ('no-deps', 'N', "don't install dependencies"),
73         ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
74         ('local-snapshots-ok', 'l', "allow building eggs from local checkouts"),
75     ]
76     boolean_options = [
77         'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
78         'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable',
79         'no-deps', 'local-snapshots-ok',
80     ]
81     negative_opt = {'always-unzip': 'zip-ok'}
82     create_index = PackageIndex
83
84     def initialize_options(self):
85         self.zip_ok = self.local_snapshots_ok = None
86         self.install_dir = self.script_dir = self.exclude_scripts = None
87         self.index_url = None
88         self.find_links = None
89         self.build_directory = None
90         self.args = None
91         self.optimize = self.record = None
92         self.upgrade = self.always_copy = self.multi_version = None
93         self.editable = self.no_deps = self.allow_hosts = None
94         self.root = self.prefix = self.no_report = None
95
96         # Options not specifiable via command line
97         self.package_index = None
98         self.pth_file = self.always_copy_from = None
99         self.delete_conflicting = None
100         self.ignore_conflicts_at_my_risk = None
101         self.site_dirs = None
102         self.installed_projects = {}
103         self.sitepy_installed = False
104         # Always read easy_install options, even if we are subclassed, or have
105         # an independent instance created.  This ensures that defaults will
106         # always come from the standard configuration file(s)' "easy_install"
107         # section, even if this is a "develop" or "install" command, or some
108         # other embedding.
109         self._dry_run = None
110         self.verbose = self.distribution.verbose
111         self.distribution._set_command_options(
112             self, self.distribution.get_option_dict('easy_install')
113         )
114
115     def delete_blockers(self, blockers):
116         for filename in blockers:
117             if os.path.exists(filename) or os.path.islink(filename):
118                 log.info("Deleting %s", filename)
119                 if not self.dry_run:
120                     if os.path.isdir(filename) and not os.path.islink(filename):
121                         rmtree(filename)
122                     else:
123                         os.unlink(filename)
124
125     def finalize_options(self):
126         self._expand('install_dir','script_dir','build_directory','site_dirs')
127         # If a non-default installation directory was specified, default the
128         # script directory to match it.
129         if self.script_dir is None:
130             self.script_dir = self.install_dir
131
132         # Let install_dir get set by install_lib command, which in turn
133         # gets its info from the install command, and takes into account
134         # --prefix and --home and all that other crud.
135         self.set_undefined_options('install_lib',
136             ('install_dir','install_dir')
137         )
138         # Likewise, set default script_dir from 'install_scripts.install_dir'
139         self.set_undefined_options('install_scripts',
140             ('install_dir', 'script_dir')
141         )
142         # default --record from the install command
143         self.set_undefined_options('install', ('record', 'record'))
144         normpath = map(normalize_path, sys.path)
145         self.all_site_dirs = get_site_dirs()
146         if self.site_dirs is not None:
147             site_dirs = [
148                 os.path.expanduser(s.strip()) for s in self.site_dirs.split(',')
149             ]
150             for d in site_dirs:
151                 if not os.path.isdir(d):
152                     log.warn("%s (in --site-dirs) does not exist", d)
153                 elif normalize_path(d) not in normpath:
154                     raise DistutilsOptionError(
155                         d+" (in --site-dirs) is not on sys.path"
156                     )
157                 else:
158                     self.all_site_dirs.append(normalize_path(d))
159         if not self.editable: self.check_site_dir()
160         self.index_url = self.index_url or "http://pypi.python.org/simple"
161         self.shadow_path = self.all_site_dirs[:]
162         for path_item in self.install_dir, normalize_path(self.script_dir):
163             if path_item not in self.shadow_path:
164                 self.shadow_path.insert(0, path_item)
165
166         if self.allow_hosts is not None:
167             hosts = [s.strip() for s in self.allow_hosts.split(',')]
168         else:
169             hosts = ['*']
170         if self.package_index is None:
171             self.package_index = self.create_index(
172                 self.index_url, search_path = self.shadow_path+sys.path, hosts=hosts,
173             )
174         self.local_index = Environment(self.shadow_path+sys.path)
175
176         if self.find_links is not None:
177             if isinstance(self.find_links, basestring):
178                 self.find_links = self.find_links.split()
179         else:
180             self.find_links = []
181         if self.local_snapshots_ok:
182             self.package_index.scan_egg_links(self.shadow_path+sys.path)
183         self.package_index.add_find_links(self.find_links)
184         self.set_undefined_options('install_lib', ('optimize','optimize'))
185         if not isinstance(self.optimize,int):
186             try:
187                 self.optimize = int(self.optimize)
188                 if not (0 <= self.optimize <= 2): raise ValueError
189             except ValueError:
190                 raise DistutilsOptionError("--optimize must be 0, 1, or 2")
191
192         if self.delete_conflicting and self.ignore_conflicts_at_my_risk:
193             raise DistutilsOptionError(
194                 "Can't use both --delete-conflicting and "
195                 "--ignore-conflicts-at-my-risk at the same time"
196             )
197         if self.editable and not self.build_directory:
198             raise DistutilsArgError(
199                 "Must specify a build directory (-b) when using --editable"
200             )
201         if not self.args:
202             raise DistutilsArgError(
203                 "No urls, filenames, or requirements specified (see --help)")
204
205         self.outputs = []
206
207     def run(self):
208         if self.verbose!=self.distribution.verbose:
209             log.set_verbosity(self.verbose)
210         try:
211             for spec in self.args:
212                 self.easy_install(spec, not self.no_deps)
213             if self.record:
214                 outputs = self.outputs
215                 if self.root:               # strip any package prefix
216                     root_len = len(self.root)
217                     for counter in xrange(len(outputs)):
218                         outputs[counter] = outputs[counter][root_len:]
219                 from distutils import file_util
220                 self.execute(
221                     file_util.write_file, (self.record, outputs),
222                     "writing list of installed files to '%s'" %
223                     self.record
224                 )
225             self.warn_deprecated_options()
226         finally:
227             log.set_verbosity(self.distribution.verbose)
228
229     def pseudo_tempname(self):
230         """Return a pseudo-tempname base in the install directory.
231         This code is intentionally naive; if a malicious party can write to
232         the target directory you're already in deep doodoo.
233         """
234         try:
235             pid = os.getpid()
236         except:
237             pid = random.randint(0,sys.maxint)
238         return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
239
240     def warn_deprecated_options(self):
241         if self.delete_conflicting or self.ignore_conflicts_at_my_risk:
242             log.warn(
243                 "Note: The -D, --delete-conflicting and"
244                 " --ignore-conflicts-at-my-risk no longer have any purpose"
245                 " and should not be used."
246             )
247
248     def check_site_dir(self):
249         """Verify that self.install_dir is .pth-capable dir, if needed"""
250         instdir = normalize_path(self.install_dir)
251         pth_file = os.path.join(instdir,'easy-install.pth')
252
253         # mkdir it if necessary
254         try:
255             os.makedirs(instdir)
256         except OSError:
257             # Oh well -- hopefully this error simply means that it is already there.
258             # If not the subsequent write test will identify the problem.
259             pass
260         # add it to site dirs
261         self.all_site_dirs.append(instdir)
262
263         # Is it a configured, PYTHONPATH, implicit, or explicit site dir?
264         is_site_dir = instdir in self.all_site_dirs
265
266         if not is_site_dir and not self.multi_version:
267             # No?  Then directly test whether it does .pth file processing
268             is_site_dir = self.check_pth_processing()
269         else:
270             # make sure we can write to target dir
271             testfile = self.pseudo_tempname()+'.write-test'
272             test_exists = os.path.exists(testfile)
273             try:
274                 if test_exists: os.unlink(testfile)
275                 open(testfile,'w').close()
276                 os.unlink(testfile)
277             except (OSError,IOError):
278                 self.cant_write_to_target()
279
280         if not is_site_dir and not self.multi_version:
281             # Can't install non-multi to non-site dir
282             log.warn(self.no_default_version_msg())
283
284         if is_site_dir:
285             if self.pth_file is None:
286                 self.pth_file = PthDistributions(pth_file, self.all_site_dirs)
287         else:
288             self.pth_file = None
289
290         if self.multi_version and not os.path.exists(pth_file):
291             self.sitepy_installed = True    # don't need site.py in this case
292             self.pth_file = None            # and don't create a .pth file
293         self.install_dir = instdir
294
295     def cant_write_to_target(self):
296         msg = """can't create or remove files in install directory
297
298 The following error occurred while trying to add or remove files in the
299 installation directory:
300
301     %s
302
303 The installation directory you specified (via --install-dir, --prefix, or
304 the distutils default setting) was:
305
306     %s
307 """     % (sys.exc_info()[1], self.install_dir,)
308
309         if not os.path.exists(self.install_dir):
310             msg += """
311 This directory does not currently exist.  Please create it and try again, or
312 choose a different installation directory (using the -d or --install-dir
313 option).
314 """
315         else:
316             msg += """
317 Perhaps your account does not have write access to this directory?  If the
318 installation directory is a system-owned directory, you may need to sign in
319 as the administrator or "root" account.  If you do not have administrative
320 access to this machine, you may wish to choose a different installation
321 directory, preferably one that is listed in your PYTHONPATH environment
322 variable.
323
324 For information on other options, you may wish to consult the
325 documentation at:
326
327   http://peak.telecommunity.com/EasyInstall.html
328
329 Please make the appropriate changes for your system and try again.
330 """
331         raise DistutilsError(msg)
332
333
334
335
336     def check_pth_processing(self):
337         """Empirically verify whether .pth files are supported in inst. dir"""
338         instdir = self.install_dir
339         log.info("Checking .pth file support in %s", instdir)
340         pth_file = self.pseudo_tempname()+".pth"
341         ok_file = pth_file+'.ok'
342         ok_exists = os.path.exists(ok_file)
343         try:
344             if ok_exists: os.unlink(ok_file)
345             f = open(pth_file,'w')
346         except (OSError,IOError):
347             self.cant_write_to_target()
348         else:
349             try:
350                 f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,))
351                 f.close(); f=None
352                 executable = sys.executable
353                 if os.name=='nt':
354                     dirname,basename = os.path.split(executable)
355                     alt = os.path.join(dirname,'pythonw.exe')
356                     if basename.lower()=='python.exe' and os.path.exists(alt):
357                         # use pythonw.exe to avoid opening a console window
358                         executable = alt
359
360                 from distutils.spawn import spawn
361                 spawn([executable,'-E','-c','pass'],0)
362
363                 if os.path.exists(ok_file):
364                     log.info(
365                         "TEST PASSED: %s appears to support .pth files",
366                         instdir
367                     )
368                     return True
369             finally:
370                 if f: f.close()
371                 if os.path.exists(ok_file): os.unlink(ok_file)
372                 if os.path.exists(pth_file): os.unlink(pth_file)
373         if not self.multi_version:
374             log.warn("TEST FAILED: %s does NOT support .pth files", instdir)
375         return False
376
377     def install_egg_scripts(self, dist):
378         """Write all the scripts for `dist`, unless scripts are excluded"""
379         if not self.exclude_scripts and dist.metadata_isdir('scripts'):
380             for script_name in dist.metadata_listdir('scripts'):
381                 self.install_script(
382                     dist, script_name,
383                     dist.get_metadata('scripts/'+script_name)
384                 )
385         self.install_wrapper_scripts(dist)
386
387     def add_output(self, path):
388         if os.path.isdir(path):
389             for base, dirs, files in os.walk(path):
390                 for filename in files:
391                     self.outputs.append(os.path.join(base,filename))
392         else:
393             self.outputs.append(path)
394
395     def not_editable(self, spec):
396         if self.editable:
397             raise DistutilsArgError(
398                 "Invalid argument %r: you can't use filenames or URLs "
399                 "with --editable (except via the --find-links option)."
400                 % (spec,)
401             )
402
403     def check_editable(self,spec):
404         if not self.editable:
405             return
406
407         if os.path.exists(os.path.join(self.build_directory, spec.key)):
408             raise DistutilsArgError(
409                 "%r already exists in %s; can't do a checkout there" %
410                 (spec.key, self.build_directory)
411             )
412
413
414
415
416
417
418     def easy_install(self, spec, deps=False):
419         tmpdir = tempfile.mkdtemp(prefix="easy_install-")
420         download = None
421         if not self.editable: self.install_site_py()
422
423         try:
424             if not isinstance(spec,Requirement):
425                 if URL_SCHEME(spec):
426                     # It's a url, download it to tmpdir and process
427                     self.not_editable(spec)
428                     download = self.package_index.download(spec, tmpdir)
429                     return self.install_item(None, download, tmpdir, deps, True)
430
431                 elif os.path.exists(spec):
432                     # Existing file or directory, just process it directly
433                     self.not_editable(spec)
434                     return self.install_item(None, spec, tmpdir, deps, True)
435                 else:
436                     spec = parse_requirement_arg(spec)
437
438             self.check_editable(spec)
439             dist = self.package_index.fetch_distribution(
440                 spec, tmpdir, self.upgrade, self.editable, not self.always_copy,
441                 self.local_index
442             )
443             if dist is None:
444                 msg = "Could not find suitable distribution for %r" % spec
445                 if self.always_copy:
446                     msg+=" (--always-copy skips system and development eggs)"
447                 raise DistutilsError(msg)
448             elif dist.precedence==DEVELOP_DIST:
449                 # .egg-info dists don't need installing, just process deps
450                 self.process_distribution(spec, dist, deps, "Using")
451                 return dist
452             else:
453                 return self.install_item(spec, dist.location, tmpdir, deps)
454
455         finally:
456             if os.path.exists(tmpdir):
457                 rmtree(tmpdir)
458
459     def install_item(self, spec, download, tmpdir, deps, install_needed=False):
460
461         # Installation is also needed if file in tmpdir or is not an egg
462         install_needed = install_needed or self.always_copy
463         install_needed = install_needed or os.path.dirname(download) == tmpdir
464         install_needed = install_needed or not download.endswith('.egg')
465         install_needed = install_needed or (
466             self.always_copy_from is not None and
467             os.path.dirname(normalize_path(download)) ==
468             normalize_path(self.always_copy_from)
469         )
470
471         if spec and not install_needed:
472             # at this point, we know it's a local .egg, we just don't know if
473             # it's already installed.
474             for dist in self.local_index[spec.project_name]:
475                 if dist.location==download:
476                     break
477             else:
478                 install_needed = True   # it's not in the local index
479
480         log.info("Processing %s", os.path.basename(download))
481
482         if install_needed:
483             dists = self.install_eggs(spec, download, tmpdir)
484             for dist in dists:
485                 self.process_distribution(spec, dist, deps)
486         else:
487             dists = [self.check_conflicts(self.egg_distribution(download))]
488             self.process_distribution(spec, dists[0], deps, "Using")
489
490         if spec is not None:
491             for dist in dists:
492                 if dist in spec:
493                     return dist
494
495
496
497
498
499
500     def process_distribution(self, requirement, dist, deps=True, *info):
501         self.update_pth(dist)
502         self.package_index.add(dist)
503         self.local_index.add(dist)
504         self.install_egg_scripts(dist)
505         self.installed_projects[dist.key] = dist
506         log.info(self.installation_report(requirement, dist, *info))
507         if dist.has_metadata('dependency_links.txt'):
508             self.package_index.add_find_links(
509                 dist.get_metadata_lines('dependency_links.txt')
510             )
511         if not deps and not self.always_copy:
512             return
513         elif requirement is not None and dist.key != requirement.key:
514             log.warn("Skipping dependencies for %s", dist)
515             return  # XXX this is not the distribution we were looking for
516         elif requirement is None or dist not in requirement:
517             # if we wound up with a different version, resolve what we've got
518             distreq = dist.as_requirement()
519             requirement = requirement or distreq
520             requirement = Requirement(
521                 distreq.project_name, distreq.specs, requirement.extras
522             )
523         log.info("Processing dependencies for %s", requirement)
524         try:
525             distros = WorkingSet([]).resolve(
526                 [requirement], self.local_index, self.easy_install
527             )
528         except DistributionNotFound, e:
529             raise DistutilsError(
530                 "Could not find required distribution %s" % e.args
531             )
532         except VersionConflict, e:
533             raise DistutilsError(
534                 "Installed distribution %s conflicts with requirement %s"
535                 % e.args
536             )
537         if self.always_copy or self.always_copy_from:
538             # Force all the relevant distros to be copied or activated
539             for dist in distros:
540                 if dist.key not in self.installed_projects:
541                     self.easy_install(dist.as_requirement())
542         log.info("Finished processing dependencies for %s", requirement)
543
544     def should_unzip(self, dist):
545         if self.zip_ok is not None:
546             return not self.zip_ok
547         if dist.has_metadata('not-zip-safe'):
548             return True
549         if not dist.has_metadata('zip-safe'):
550             return True
551         return False
552
553     def maybe_move(self, spec, dist_filename, setup_base):
554         dst = os.path.join(self.build_directory, spec.key)
555         if os.path.exists(dst):
556             log.warn(
557                "%r already exists in %s; build directory %s will not be kept",
558                spec.key, self.build_directory, setup_base
559             )
560             return setup_base
561         if os.path.isdir(dist_filename):
562             setup_base = dist_filename
563         else:
564             if os.path.dirname(dist_filename)==setup_base:
565                 os.unlink(dist_filename)   # get it out of the tmp dir
566             contents = os.listdir(setup_base)
567             if len(contents)==1:
568                 dist_filename = os.path.join(setup_base,contents[0])
569                 if os.path.isdir(dist_filename):
570                     # if the only thing there is a directory, move it instead
571                     setup_base = dist_filename
572         ensure_directory(dst); shutil.move(setup_base, dst)
573         return dst
574
575     def install_wrapper_scripts(self, dist):
576         if not self.exclude_scripts:
577             for args in get_script_args(dist, script_dir=self.script_dir):
578                 self.write_script(*args)
579
580
581
582     def install_script(self, dist, script_name, script_text, dev_path=None):
583         """Generate a legacy script wrapper and install it"""
584         spec = str(dist.as_requirement())
585         is_script = is_python_script(script_text, script_name)
586
587         requires = [spec] + [str(r) for r in dist.requires()]
588         if is_script and dev_path:
589             script_text = get_script_header(script_text) + (
590                 "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n"
591                 "__requires__ = %(requires)r\n"
592                 "from pkg_resources import require; require(%(spec)r)\n"
593                 "del require\n"
594                 "__file__ = %(dev_path)r\n"
595                 "execfile(__file__)\n"
596             ) % locals()
597         elif is_script:
598             script_text = get_script_header(script_text) + (
599                 "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n"
600                 "__requires__ = %(requires)r\n"
601                 "import pkg_resources\n"
602                 "pkg_resources.run_script(%(spec)r, %(script_name)r)\n"
603             ) % locals()
604         self.write_script(script_name, script_text, 'b')
605
606     def write_script(self, script_name, contents, mode="t", blockers=()):
607         """Write an executable file to the scripts directory"""
608         self.delete_blockers(   # clean up old .py/.pyw w/o a script
609             [os.path.join(self.script_dir,x) for x in blockers])
610         log.info("Installing %s script to %s", script_name, self.script_dir)
611         target = os.path.join(self.script_dir, script_name)
612         self.add_output(target)
613
614         if not self.dry_run:
615             ensure_directory(target)
616             f = open(target,"w"+mode)
617             f.write(contents)
618             f.close()
619             chmod(target,0755)
620
621
622
623
624     def install_eggs(self, spec, dist_filename, tmpdir):
625         # .egg dirs or files are already built, so just return them
626         if dist_filename.lower().endswith('.egg'):
627             return [self.install_egg(dist_filename, tmpdir)]
628         elif dist_filename.lower().endswith('.exe'):
629             return [self.install_exe(dist_filename, tmpdir)]
630
631         # Anything else, try to extract and build
632         setup_base = tmpdir
633         if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'):
634             unpack_archive(dist_filename, tmpdir, self.unpack_progress)
635         elif os.path.isdir(dist_filename):
636             setup_base = os.path.abspath(dist_filename)
637
638         if (setup_base.startswith(tmpdir)   # something we downloaded
639             and self.build_directory and spec is not None
640         ):
641             setup_base = self.maybe_move(spec, dist_filename, setup_base)
642
643         # Find the setup.py file
644         setup_script = os.path.join(setup_base, 'setup.py')
645
646         if not os.path.exists(setup_script):
647             setups = glob(os.path.join(setup_base, '*', 'setup.py'))
648             if not setups:
649                 raise DistutilsError(
650                     "Couldn't find a setup script in %s" % os.path.abspath(dist_filename)
651                 )
652             if len(setups)>1:
653                 raise DistutilsError(
654                     "Multiple setup scripts in %s" % os.path.abspath(dist_filename)
655                 )
656             setup_script = setups[0]
657
658         # Now run it, and return the result
659         if self.editable:
660             log.info(self.report_editable(spec, setup_script))
661             return []
662         else:
663             return self.build_and_install(setup_script, setup_base)
664
665     def egg_distribution(self, egg_path):
666         if os.path.isdir(egg_path):
667             metadata = PathMetadata(egg_path,os.path.join(egg_path,'EGG-INFO'))
668         else:
669             metadata = EggMetadata(zipimport.zipimporter(egg_path))
670         return Distribution.from_filename(egg_path,metadata=metadata)
671
672     def install_egg(self, egg_path, tmpdir):
673         destination = os.path.join(self.install_dir,os.path.basename(egg_path))
674         destination = os.path.abspath(destination)
675         if not self.dry_run:
676             ensure_directory(destination)
677
678         dist = self.egg_distribution(egg_path)
679         self.check_conflicts(dist)
680         if not samefile(egg_path, destination):
681             if os.path.isdir(destination) and not os.path.islink(destination):
682                 dir_util.remove_tree(destination, dry_run=self.dry_run)
683             elif os.path.exists(destination):
684                 self.execute(os.unlink,(destination,),"Removing "+destination)
685             uncache_zipdir(destination)
686             if os.path.isdir(egg_path):
687                 if egg_path.startswith(tmpdir):
688                     f,m = shutil.move, "Moving"
689                 else:
690                     f,m = shutil.copytree, "Copying"
691             elif self.should_unzip(dist):
692                 self.mkpath(destination)
693                 f,m = self.unpack_and_compile, "Extracting"
694             elif egg_path.startswith(tmpdir):
695                 f,m = shutil.move, "Moving"
696             else:
697                 f,m = shutil.copy2, "Copying"
698
699             self.execute(f, (egg_path, destination),
700                 (m+" %s to %s") %
701                 (os.path.basename(egg_path),os.path.dirname(destination)))
702
703         self.add_output(destination)
704         return self.egg_distribution(destination)
705
706     def install_exe(self, dist_filename, tmpdir):
707         # See if it's valid, get data
708         cfg = extract_wininst_cfg(dist_filename)
709         if cfg is None:
710             raise DistutilsError(
711                 "%s is not a valid distutils Windows .exe" % dist_filename
712             )
713         # Create a dummy distribution object until we build the real distro
714         dist = Distribution(None,
715             project_name=cfg.get('metadata','name'),
716             version=cfg.get('metadata','version'), platform="win32"
717         )
718
719         # Convert the .exe to an unpacked egg
720         egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg')
721         egg_tmp  = egg_path+'.tmp'
722         egg_info = os.path.join(egg_tmp, 'EGG-INFO')
723         pkg_inf = os.path.join(egg_info, 'PKG-INFO')
724         ensure_directory(pkg_inf)   # make sure EGG-INFO dir exists
725         dist._provider = PathMetadata(egg_tmp, egg_info)    # XXX
726         self.exe_to_egg(dist_filename, egg_tmp)
727
728         # Write EGG-INFO/PKG-INFO
729         if not os.path.exists(pkg_inf):
730             f = open(pkg_inf,'w')
731             f.write('Metadata-Version: 1.0\n')
732             for k,v in cfg.items('metadata'):
733                 if k!='target_version':
734                     f.write('%s: %s\n' % (k.replace('_','-').title(), v))
735             f.close()
736         script_dir = os.path.join(egg_info,'scripts')
737         self.delete_blockers(   # delete entry-point scripts to avoid duping
738             [os.path.join(script_dir,args[0]) for args in get_script_args(dist)]
739         )
740         # Build .egg file from tmpdir
741         bdist_egg.make_zipfile(
742             egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run
743         )
744         # install the .egg
745         return self.install_egg(egg_path, tmpdir)
746
747     def exe_to_egg(self, dist_filename, egg_tmp):
748         """Extract a bdist_wininst to the directories an egg would use"""
749         # Check for .pth file and set up prefix translations
750         prefixes = get_exe_prefixes(dist_filename)
751         to_compile = []
752         native_libs = []
753         top_level = {}
754         def process(src,dst):
755             s = src.lower()
756             for old,new in prefixes:
757                 if s.startswith(old):
758                     src = new+src[len(old):]
759                     parts = src.split('/')
760                     dst = os.path.join(egg_tmp, *parts)
761                     dl = dst.lower()
762                     if dl.endswith('.pyd') or dl.endswith('.dll'):
763                         parts[-1] = bdist_egg.strip_module(parts[-1])
764                         top_level[os.path.splitext(parts[0])[0]] = 1
765                         native_libs.append(src)
766                     elif dl.endswith('.py') and old!='SCRIPTS/':
767                         top_level[os.path.splitext(parts[0])[0]] = 1
768                         to_compile.append(dst)
769                     return dst
770             if not src.endswith('.pth'):
771                 log.warn("WARNING: can't process %s", src)
772             return None
773         # extract, tracking .pyd/.dll->native_libs and .py -> to_compile
774         unpack_archive(dist_filename, egg_tmp, process)
775         stubs = []
776         for res in native_libs:
777             if res.lower().endswith('.pyd'):    # create stubs for .pyd's
778                 parts = res.split('/')
779                 resource = parts[-1]
780                 parts[-1] = bdist_egg.strip_module(parts[-1])+'.py'
781                 pyfile = os.path.join(egg_tmp, *parts)
782                 to_compile.append(pyfile); stubs.append(pyfile)
783                 bdist_egg.write_stub(resource, pyfile)
784         self.byte_compile(to_compile)   # compile .py's
785         bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'),
786             bdist_egg.analyze_egg(egg_tmp, stubs))  # write zip-safety flag
787
788         for name in 'top_level','native_libs':
789             if locals()[name]:
790                 txt = os.path.join(egg_tmp, 'EGG-INFO', name+'.txt')
791                 if not os.path.exists(txt):
792                     open(txt,'w').write('\n'.join(locals()[name])+'\n')
793
794     def check_conflicts(self, dist):
795         """Verify that there are no conflicting "old-style" packages"""
796
797         return dist     # XXX temporarily disable until new strategy is stable
798         from imp import find_module, get_suffixes
799         from glob import glob
800
801         blockers = []
802         names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr
803
804         exts = {'.pyc':1, '.pyo':1}     # get_suffixes() might leave one out
805         for ext,mode,typ in get_suffixes():
806             exts[ext] = 1
807
808         for path,files in expand_paths([self.install_dir]+self.all_site_dirs):
809             for filename in files:
810                 base,ext = os.path.splitext(filename)
811                 if base in names:
812                     if not ext:
813                         # no extension, check for package
814                         try:
815                             f, filename, descr = find_module(base, [path])
816                         except ImportError:
817                             continue
818                         else:
819                             if f: f.close()
820                             if filename not in blockers:
821                                 blockers.append(filename)
822                     elif ext in exts and base!='site':  # XXX ugh
823                         blockers.append(os.path.join(path,filename))
824         if blockers:
825             self.found_conflicts(dist, blockers)
826
827         return dist
828
829     def found_conflicts(self, dist, blockers):
830         if self.delete_conflicting:
831             log.warn("Attempting to delete conflicting packages:")
832             return self.delete_blockers(blockers)
833
834         msg = """\
835 -------------------------------------------------------------------------
836 CONFLICT WARNING:
837
838 The following modules or packages have the same names as modules or
839 packages being installed, and will be *before* the installed packages in
840 Python's search path.  You MUST remove all of the relevant files and
841 directories before you will be able to use the package(s) you are
842 installing:
843
844    %s
845
846 """ % '\n   '.join(blockers)
847
848         if self.ignore_conflicts_at_my_risk:
849             msg += """\
850 (Note: you can run EasyInstall on '%s' with the
851 --delete-conflicting option to attempt deletion of the above files
852 and/or directories.)
853 """ % dist.project_name
854         else:
855             msg += """\
856 Note: you can attempt this installation again with EasyInstall, and use
857 either the --delete-conflicting (-D) option or the
858 --ignore-conflicts-at-my-risk option, to either delete the above files
859 and directories, or to ignore the conflicts, respectively.  Note that if
860 you ignore the conflicts, the installed package(s) may not work.
861 """
862         msg += """\
863 -------------------------------------------------------------------------
864 """
865         sys.stderr.write(msg)
866         sys.stderr.flush()
867         if not self.ignore_conflicts_at_my_risk:
868             raise DistutilsError("Installation aborted due to conflicts")
869
870     def installation_report(self, req, dist, what="Installed"):
871         """Helpful installation message for display to package users"""
872         msg = "\n%(what)s %(eggloc)s%(extras)s"
873         if self.multi_version and not self.no_report:
874             msg += """
875
876 Because this distribution was installed --multi-version, before you can
877 import modules from this package in an application, you will need to
878 'import pkg_resources' and then use a 'require()' call similar to one of
879 these examples, in order to select the desired version:
880
881     pkg_resources.require("%(name)s")  # latest installed version
882     pkg_resources.require("%(name)s==%(version)s")  # this exact version
883     pkg_resources.require("%(name)s>=%(version)s")  # this version or higher
884 """
885             if self.install_dir not in map(normalize_path,sys.path):
886                 msg += """
887
888 Note also that the installation directory must be on sys.path at runtime for
889 this to work.  (e.g. by being the application's script directory, by being on
890 PYTHONPATH, or by being added to sys.path by your code.)
891 """
892         eggloc = dist.location
893         name = dist.project_name
894         version = dist.version
895         extras = '' # TODO: self.report_extras(req, dist)
896         return msg % locals()
897
898     def report_editable(self, spec, setup_script):
899         dirname = os.path.dirname(setup_script)
900         python = sys.executable
901         return """\nExtracted editable version of %(spec)s to %(dirname)s
902
903 If it uses setuptools in its setup script, you can activate it in
904 "development" mode by going to that directory and running::
905
906     %(python)s setup.py develop
907
908 See the setuptools documentation for the "develop" command for more info.
909 """ % locals()
910
911     def run_setup(self, setup_script, setup_base, args):
912         sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
913         sys.modules.setdefault('distutils.command.egg_info', egg_info)
914
915         args = list(args)
916         if self.verbose>2:
917             v = 'v' * (self.verbose - 1)
918             args.insert(0,'-'+v)
919         elif self.verbose<2:
920             args.insert(0,'-q')
921         if self.dry_run:
922             args.insert(0,'-n')
923         log.info(
924             "Running %s %s", setup_script[len(setup_base)+1:], ' '.join(args)
925         )
926         try:
927             run_setup(setup_script, args)
928         except SystemExit, v:
929             raise DistutilsError("Setup script exited with %s" % (v.args[0],))
930
931     def build_and_install(self, setup_script, setup_base):
932         args = ['bdist_egg', '--dist-dir']
933         dist_dir = tempfile.mkdtemp(
934             prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)
935         )
936         try:
937             args.append(dist_dir)
938             self.run_setup(setup_script, setup_base, args)
939             all_eggs = Environment([dist_dir])
940             eggs = []
941             for key in all_eggs:
942                 for dist in all_eggs[key]:
943                     eggs.append(self.install_egg(dist.location, setup_base))
944             if not eggs and not self.dry_run:
945                 log.warn("No eggs found in %s (setup script problem?)",
946                     dist_dir)
947             return eggs
948         finally:
949             rmtree(dist_dir)
950             log.set_verbosity(self.verbose) # restore our log verbosity
951
952     def update_pth(self,dist):
953         if self.pth_file is None:
954             return
955
956         for d in self.pth_file[dist.key]:    # drop old entries
957             if self.multi_version or d.location != dist.location:
958                 log.info("Removing %s from easy-install.pth file", d)
959                 self.pth_file.remove(d)
960                 if d.location in self.shadow_path:
961                     self.shadow_path.remove(d.location)
962
963         if not self.multi_version:
964             if dist.location in self.pth_file.paths:
965                 log.info(
966                     "%s is already the active version in easy-install.pth",
967                     dist
968                 )
969             else:
970                 log.info("Adding %s to easy-install.pth file", dist)
971                 self.pth_file.add(dist) # add new entry
972                 if dist.location not in self.shadow_path:
973                     self.shadow_path.append(dist.location)
974
975         if not self.dry_run:
976
977             self.pth_file.save()
978
979             if dist.key=='setuptools':
980                 # Ensure that setuptools itself never becomes unavailable!
981                 # XXX should this check for latest version?
982                 filename = os.path.join(self.install_dir,'setuptools.pth')
983                 if os.path.islink(filename): os.unlink(filename)
984                 f = open(filename, 'wt')
985                 f.write(self.pth_file.make_relative(dist.location)+'\n')
986                 f.close()
987
988     def unpack_progress(self, src, dst):
989         # Progress filter for unpacking
990         log.debug("Unpacking %s to %s", src, dst)
991         return dst     # only unpack-and-compile skips files for dry run
992
993     def unpack_and_compile(self, egg_path, destination):
994         to_compile = []; to_chmod = []
995
996         def pf(src,dst):
997             if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
998                 to_compile.append(dst)
999             elif dst.endswith('.dll') or dst.endswith('.so'):
1000                 to_chmod.append(dst)
1001             self.unpack_progress(src,dst)
1002             return not self.dry_run and dst or None
1003
1004         unpack_archive(egg_path, destination, pf)
1005         self.byte_compile(to_compile)
1006         if not self.dry_run:
1007             for f in to_chmod:
1008                 mode = ((os.stat(f)[stat.ST_MODE]) | 0555) & 07755
1009                 chmod(f, mode)
1010
1011     def byte_compile(self, to_compile):
1012         if _dont_write_bytecode:
1013             self.warn('byte-compiling is disabled, skipping.')
1014             return
1015         from distutils.util import byte_compile
1016         try:
1017             # try to make the byte compile messages quieter
1018             log.set_verbosity(self.verbose - 1)
1019
1020             byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)
1021             if self.optimize:
1022                 byte_compile(
1023                     to_compile, optimize=self.optimize, force=1,
1024                     dry_run=self.dry_run
1025                 )
1026         finally:
1027             log.set_verbosity(self.verbose)     # restore original verbosity
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037     def no_default_version_msg(self):
1038         return """bad install directory or PYTHONPATH
1039
1040 You are attempting to install a package to a directory that is not
1041 on PYTHONPATH and which Python does not read ".pth" files from.  The
1042 installation directory you specified (via --install-dir, --prefix, or
1043 the distutils default setting) was:
1044
1045     %s
1046
1047 and your PYTHONPATH environment variable currently contains:
1048
1049     %r
1050
1051 Here are some of your options for correcting the problem:
1052
1053 * You can choose a different installation directory, i.e., one that is
1054   on PYTHONPATH or supports .pth files
1055
1056 * You can add the installation directory to the PYTHONPATH environment
1057   variable.  (It must then also be on PYTHONPATH whenever you run
1058   Python and want to use the package(s) you are installing.)
1059
1060 * You can set up the installation directory to support ".pth" files by
1061   using one of the approaches described here:
1062
1063   http://peak.telecommunity.com/EasyInstall.html#custom-installation-locations
1064
1065 Proceeding to install.  Please remember that unless you make one of
1066 these changes you will not be able to run the installed code.
1067 """ % (
1068         self.install_dir, os.environ.get('PYTHONPATH','')
1069     )
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080     def install_site_py(self):
1081         """Make sure there's a site.py in the target dir, if needed"""
1082
1083         if self.sitepy_installed:
1084             return  # already did it, or don't need to
1085
1086         sitepy = os.path.join(self.install_dir, "site.py")
1087         source = resource_string("setuptools", "site-patch.py")
1088         current = ""
1089
1090         if os.path.exists(sitepy):
1091             log.debug("Checking existing site.py in %s", self.install_dir)
1092             current = open(sitepy,'rb').read()
1093             if not current.startswith('def __boot():'):
1094                 print ("\n"
1095                        "***********************************************************************\n"
1096                        "Warning: %s is not a\n"
1097                        "setuptools-generated site.py. It will not be overwritten.\n"
1098                        "***********************************************************************\n"
1099                       ) % (sitepy,)
1100                 self.sitepy_installed = True
1101                 return
1102
1103         if current != source:
1104             log.info("Creating %s", sitepy)
1105             if not self.dry_run:
1106                 ensure_directory(sitepy)
1107                 f = open(sitepy,'wb')
1108                 f.write(source)
1109                 f.close()
1110             self.byte_compile([sitepy])
1111
1112         self.sitepy_installed = True
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125     INSTALL_SCHEMES = dict(
1126         posix = dict(
1127             install_dir = '$base/lib/python$py_version_short/site-packages',
1128             script_dir  = '$base/bin',
1129         ),
1130     )
1131
1132     DEFAULT_SCHEME = dict(
1133         install_dir = '$base/Lib/site-packages',
1134         script_dir  = '$base/Scripts',
1135     )
1136
1137     def _expand(self, *attrs):
1138         config_vars = self.get_finalized_command('install').config_vars
1139
1140         if self.prefix:
1141             # Set default install_dir/scripts from --prefix
1142             config_vars = config_vars.copy()
1143             config_vars['base'] = self.prefix
1144             scheme = self.INSTALL_SCHEMES.get(os.name,self.DEFAULT_SCHEME)
1145             for attr,val in scheme.items():
1146                 if getattr(self,attr,None) is None:
1147                     setattr(self,attr,val)
1148
1149         from distutils.util import subst_vars
1150         for attr in attrs:
1151             val = getattr(self, attr)
1152             if val is not None:
1153                 val = subst_vars(val, config_vars)
1154                 if os.name == 'posix':
1155                     val = os.path.expanduser(val)
1156                 setattr(self, attr, val)
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166 def get_site_dirs():
1167     # return a list of 'site' dirs
1168     sitedirs = filter(None,os.environ.get('PYTHONPATH','').split(os.pathsep))
1169     prefixes = [sys.prefix]
1170     if sys.exec_prefix != sys.prefix:
1171         prefixes.append(sys.exec_prefix)
1172     for prefix in prefixes:
1173         if prefix:
1174             if sys.platform in ('os2emx', 'riscos'):
1175                 sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))
1176             elif os.sep == '/':
1177                 sitedirs.extend([os.path.join(prefix,
1178                                          "lib",
1179                                          "python" + sys.version[:3],
1180                                          "site-packages"),
1181                             os.path.join(prefix, "lib", "site-python")])
1182             else:
1183                 sitedirs.extend(
1184                     [prefix, os.path.join(prefix, "lib", "site-packages")]
1185                 )
1186             if sys.platform == 'darwin':
1187                 # for framework builds *only* we add the standard Apple
1188                 # locations. Currently only per-user, but /Library and
1189                 # /Network/Library could be added too
1190                 if 'Python.framework' in prefix:
1191                     home = os.environ.get('HOME')
1192                     if home:
1193                         sitedirs.append(
1194                             os.path.join(home,
1195                                          'Library',
1196                                          'Python',
1197                                          sys.version[:3],
1198                                          'site-packages'))
1199     for plat_specific in (0,1):
1200         site_lib = get_python_lib(plat_specific)
1201         if site_lib not in sitedirs: sitedirs.append(site_lib)
1202
1203     sitedirs = map(normalize_path, sitedirs)
1204     return sitedirs
1205
1206
1207 def expand_paths(inputs):
1208     """Yield sys.path directories that might contain "old-style" packages"""
1209
1210     seen = {}
1211
1212     for dirname in inputs:
1213         dirname = normalize_path(dirname)
1214         if dirname in seen:
1215             continue
1216
1217         seen[dirname] = 1
1218         if not os.path.isdir(dirname):
1219             continue
1220
1221         files = os.listdir(dirname)
1222         yield dirname, files
1223
1224         for name in files:
1225             if not name.endswith('.pth'):
1226                 # We only care about the .pth files
1227                 continue
1228             if name in ('easy-install.pth','setuptools.pth'):
1229                 # Ignore .pth files that we control
1230                 continue
1231
1232             # Read the .pth file
1233             f = open(os.path.join(dirname,name))
1234             lines = list(yield_lines(f))
1235             f.close()
1236
1237             # Yield existing non-dupe, non-import directory lines from it
1238             for line in lines:
1239                 if not line.startswith("import"):
1240                     line = normalize_path(line.rstrip())
1241                     if line not in seen:
1242                         seen[line] = 1
1243                         if not os.path.isdir(line):
1244                             continue
1245                         yield line, os.listdir(line)
1246
1247
1248 def extract_wininst_cfg(dist_filename):
1249     """Extract configuration data from a bdist_wininst .exe
1250
1251     Returns a ConfigParser.RawConfigParser, or None
1252     """
1253     f = open(dist_filename,'rb')
1254     try:
1255         endrec = zipfile._EndRecData(f)
1256         if endrec is None:
1257             return None
1258
1259         prepended = (endrec[9] - endrec[5]) - endrec[6]
1260         if prepended < 12:  # no wininst data here
1261             return None
1262         f.seek(prepended-12)
1263
1264         import struct, StringIO, ConfigParser
1265         tag, cfglen, bmlen = struct.unpack("<iii",f.read(12))
1266         if tag not in (0x1234567A, 0x1234567B):
1267             return None     # not a valid tag
1268
1269         f.seek(prepended-(12+cfglen))
1270         cfg = ConfigParser.RawConfigParser({'version':'','target_version':''})
1271         try:
1272             cfg.readfp(StringIO.StringIO(f.read(cfglen).split(chr(0),1)[0]))
1273         except ConfigParser.Error:
1274             return None
1275         if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
1276             return None
1277         return cfg
1278
1279     finally:
1280         f.close()
1281
1282
1283
1284
1285
1286
1287
1288
1289 def get_exe_prefixes(exe_filename):
1290     """Get exe->egg path translations for a given .exe file"""
1291
1292     prefixes = [
1293         ('PURELIB/', ''), ('PLATLIB/pywin32_system32', ''),
1294         ('PLATLIB/', ''),
1295         ('SCRIPTS/', 'EGG-INFO/scripts/')
1296     ]
1297     z = zipfile.ZipFile(exe_filename)
1298     try:
1299         for info in z.infolist():
1300             name = info.filename
1301             parts = name.split('/')
1302             if len(parts)==3 and parts[2]=='PKG-INFO':
1303                 if parts[1].endswith('.egg-info'):
1304                     prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/'))
1305                     break
1306             if len(parts)!=2 or not name.endswith('.pth'):
1307                 continue
1308             if name.endswith('-nspkg.pth'):
1309                 continue
1310             if parts[0].upper() in ('PURELIB','PLATLIB'):
1311                 for pth in yield_lines(z.read(name)):
1312                     pth = pth.strip().replace('\\','/')
1313                     if not pth.startswith('import'):
1314                         prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
1315     finally:
1316         z.close()
1317     prefixes = [(x.lower(),y) for x, y in prefixes]
1318     prefixes.sort(); prefixes.reverse()
1319     return prefixes
1320
1321
1322 def parse_requirement_arg(spec):
1323     try:
1324         return Requirement.parse(spec)
1325     except ValueError:
1326         raise DistutilsError(
1327             "Not a URL, existing file, or requirement spec: %r" % (spec,)
1328         )
1329
1330 class PthDistributions(Environment):
1331     """A .pth file with Distribution paths in it"""
1332
1333     dirty = False
1334
1335     def __init__(self, filename, sitedirs=()):
1336         self.filename = filename; self.sitedirs=map(normalize_path, sitedirs)
1337         self.basedir = normalize_path(os.path.dirname(self.filename))
1338         self._load(); Environment.__init__(self, [], None, None)
1339         for path in yield_lines(self.paths):
1340             map(self.add, find_distributions(path, True))
1341
1342     def _load(self):
1343         self.paths = []
1344         saw_import = False
1345         seen = dict.fromkeys(self.sitedirs)
1346         if os.path.isfile(self.filename):
1347             for line in open(self.filename,'rt'):
1348                 if line.startswith('import'):
1349                     saw_import = True
1350                     continue
1351                 path = line.rstrip()
1352                 self.paths.append(path)
1353                 if not path.strip() or path.strip().startswith('#'):
1354                     continue
1355                 # skip non-existent paths, in case somebody deleted a package
1356                 # manually, and duplicate paths as well
1357                 path = self.paths[-1] = normalize_path(
1358                     os.path.join(self.basedir,path)
1359                 )
1360                 if not os.path.exists(path) or path in seen:
1361                     self.paths.pop()    # skip it
1362                     self.dirty = True   # we cleaned up, so we're dirty now :)
1363                     continue
1364                 seen[path] = 1
1365
1366         if self.paths and not saw_import:
1367             self.dirty = True   # ensure anything we touch has import wrappers
1368         while self.paths and not self.paths[-1].strip():
1369             self.paths.pop()
1370
1371     def save(self):
1372         """Write changed .pth file back to disk"""
1373         if not self.dirty:
1374             return
1375
1376         data = '\n'.join(map(self.make_relative,self.paths))
1377         if data:
1378             log.debug("Saving %s", self.filename)
1379             data = (
1380                 "import sys; sys.__plen = len(sys.path)\n"
1381                 "%s\n"
1382                 "import sys; new=sys.path[sys.__plen:];"
1383                 " del sys.path[sys.__plen:];"
1384                 " p=getattr(sys,'__egginsert',len(os.environ.get('PYTHONPATH','').split(os.pathsep))); sys.path[p:p]=new;"
1385                 " sys.__egginsert = p+len(new)\n"
1386             ) % data
1387
1388             if os.path.islink(self.filename):
1389                 os.unlink(self.filename)
1390             f = open(self.filename,'wb')
1391             f.write(data); f.close()
1392
1393         elif os.path.exists(self.filename):
1394             log.debug("Deleting empty %s", self.filename)
1395             os.unlink(self.filename)
1396
1397         self.dirty = False
1398
1399     def add(self,dist):
1400         """Add `dist` to the distribution map"""
1401         if dist.location not in self.paths and dist.location not in self.sitedirs:
1402             self.paths.append(dist.location); self.dirty = True
1403         Environment.add(self,dist)
1404
1405     def remove(self,dist):
1406         """Remove `dist` from the distribution map"""
1407         while dist.location in self.paths:
1408             self.paths.remove(dist.location); self.dirty = True
1409         Environment.remove(self,dist)
1410
1411
1412     def make_relative(self,path):
1413         npath, last = os.path.split(normalize_path(path))
1414         baselen = len(self.basedir)
1415         parts = [last]
1416         sep = os.altsep=='/' and '/' or os.sep
1417         while len(npath)>=baselen:
1418             if npath==self.basedir:
1419                 parts.append(os.curdir)
1420                 parts.reverse()
1421                 return sep.join(parts)
1422             npath, last = os.path.split(npath)
1423             parts.append(last)
1424         else:
1425             return path
1426
1427 def get_script_header(script_text, executable=sys_executable, wininst=False):
1428     """Create a #! line, getting options (if any) from script_text"""
1429     from distutils.command.build_scripts import first_line_re
1430     first = (script_text+'\n').splitlines()[0]
1431     match = first_line_re.match(first)
1432     options = ''
1433     if match:
1434         options = match.group(1) or ''
1435         if options: options = ' '+options
1436     if wininst:
1437         executable = "python.exe"
1438     else:
1439         executable = nt_quote_arg(executable)
1440     hdr = "#!%(executable)s%(options)s\n" % locals()
1441     if unicode(hdr,'ascii','ignore').encode('ascii') != hdr:
1442         # Non-ascii path to sys.executable, use -x to prevent warnings
1443         if options:
1444             if options.strip().startswith('-'):
1445                 options = ' -x'+options.strip()[1:]
1446             # else: punt, we can't do it, let the warning happen anyway
1447         else:
1448             options = ' -x'
1449     executable = fix_jython_executable(executable, options)
1450     hdr = "#!%(executable)s%(options)s\n" % locals()
1451     return hdr
1452
1453 def auto_chmod(func, arg, exc):
1454     if func is os.remove and os.name=='nt':
1455         chmod(arg, stat.S_IWRITE)
1456         return func(arg)
1457     exc = sys.exc_info()
1458     raise exc[0], (exc[1][0], exc[1][1] + (" %s %s" % (func,arg)))
1459
1460 def uncache_zipdir(path):
1461     """Ensure that the importer caches dont have stale info for `path`"""
1462     from zipimport import _zip_directory_cache as zdc
1463     _uncache(path, zdc)
1464     _uncache(path, sys.path_importer_cache)
1465
1466 def _uncache(path, cache):
1467     if path in cache:
1468         del cache[path]
1469     else:
1470         path = normalize_path(path)
1471         for p in cache:
1472             if normalize_path(p)==path:
1473                 del cache[p]
1474                 return
1475
1476 def is_python(text, filename='<string>'):
1477     "Is this string a valid Python script?"
1478     try:
1479         compile(text, filename, 'exec')
1480     except (SyntaxError, TypeError):
1481         return False
1482     else:
1483         return True
1484
1485 def is_sh(executable):
1486     """Determine if the specified executable is a .sh (contains a #! line)"""
1487     try:
1488         fp = open(executable)
1489         magic = fp.read(2)
1490         fp.close()
1491     except (OSError,IOError): return executable
1492     return magic == '#!'
1493
1494 def nt_quote_arg(arg):
1495     """Quote a command line argument according to Windows parsing rules"""
1496
1497     result = []
1498     needquote = False
1499     nb = 0
1500
1501     needquote = (" " in arg) or ("\t" in arg)
1502     if needquote:
1503         result.append('"')
1504
1505     for c in arg:
1506         if c == '\\':
1507             nb += 1
1508         elif c == '"':
1509             # double preceding backslashes, then add a \"
1510             result.append('\\' * (nb*2) + '\\"')
1511             nb = 0
1512         else:
1513             if nb:
1514                 result.append('\\' * nb)
1515                 nb = 0
1516             result.append(c)
1517
1518     if nb:
1519         result.append('\\' * nb)
1520
1521     if needquote:
1522         result.append('\\' * nb)    # double the trailing backslashes
1523         result.append('"')
1524
1525     return ''.join(result)
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535 def is_python_script(script_text, filename):
1536     """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
1537     """
1538     if filename.endswith('.py') or filename.endswith('.pyw'):
1539         return True     # extension says it's Python
1540     if is_python(script_text, filename):
1541         return True     # it's syntactically valid Python
1542     if script_text.startswith('#!'):
1543         # It begins with a '#!' line, so check if 'python' is in it somewhere
1544         return 'python' in script_text.splitlines()[0].lower()
1545
1546     return False    # Not any Python I can recognize
1547
1548 try:
1549     from os import chmod as _chmod
1550 except ImportError:
1551     # Jython compatibility
1552     def _chmod(*args): pass
1553
1554 def chmod(path, mode):
1555     log.debug("changing mode of %s to %o", path, mode)
1556     try:
1557         _chmod(path, mode)
1558     except os.error, e:
1559         log.debug("chmod failed: %s", e)
1560
1561 def fix_jython_executable(executable, options):
1562     if sys.platform.startswith('java') and is_sh(executable):
1563         # Workaround Jython's sys.executable being a .sh (an invalid
1564         # shebang line interpreter)
1565         if options:
1566             # Can't apply the workaround, leave it broken
1567             log.warn("WARNING: Unable to adapt shebang line for Jython,"
1568                              " the following script is NOT executable\n"
1569                      "         see http://bugs.jython.org/issue1112 for"
1570                              " more information.")
1571         else:
1572             return '/usr/bin/env %s' % executable
1573     return executable
1574
1575
1576 def get_script_args(dist, executable=sys_executable, wininst=False, script_dir=None):
1577     """Yield write_script() argument tuples for a distribution's entrypoints"""
1578     spec = str(dist.as_requirement())
1579     requires = [spec] + [str(r) for r in dist.requires()]
1580     header = get_script_header("", executable, wininst)
1581     generated_by = "# generated by zetuptoolz %s" % (setuptools_version,)
1582
1583     for group in 'console_scripts', 'gui_scripts':
1584         for name, ep in dist.get_entry_map(group).items():
1585             script_head, script_tail = ((
1586                 "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n"
1587                 "%(generated_by)s\n"
1588                 "__requires__ = %(requires)r\n"
1589                 "import sys\n"
1590                 "from pkg_resources import load_entry_point\n"
1591                 "\n"
1592             ) % locals(), (
1593                 "sys.exit(\n"
1594                 "   load_entry_point(%(spec)r, %(group)r, %(name)r)()\n"
1595                 ")\n"
1596             ) % locals())
1597
1598             if wininst or sys.platform == "win32":
1599                 # On Windows/wininst, add a .py[w] extension. Delete any existing
1600                 # -script.py[w], .exe, and .exe.manifest.
1601                 if group=='gui_scripts':
1602                     ext = '.pyw'
1603                     old = ['','.pyw','-script.pyw','.exe','.exe.manifest']
1604                     which_python = 'pythonw.exe'
1605                     new_header = re.sub('(?i)python.exe', which_python, header)
1606                 else:
1607                     ext = '.pyscript'
1608                     old = ['','.pyscript','.py','.pyc','.pyo','-script.py','.exe','.exe.manifest']
1609                     which_python = 'python.exe'
1610                     new_header = re.sub('(?i)pythonw.exe', which_python, header)
1611
1612                 len_ext = len(ext)
1613                 script_head += (
1614                     "# If this script doesn't work for you, make sure that the %(ext)s\n"
1615                     "# extension is included in the PATHEXT environment variable, and is\n"
1616                     "# associated with %(which_python)s in the registry.\n"
1617                     "\n"
1618                     "if sys.argv[0].endswith(%(ext)r):\n"
1619                     "    sys.argv[0] = sys.argv[0][:-%(len_ext)r]\n"
1620                     "\n"
1621                 ) % locals()
1622
1623                 if os.path.exists(new_header[2:-1]) or sys.platform != 'win32':
1624                     hdr = new_header
1625                 else:
1626                     hdr = header
1627                 yield (name+ext, hdr + script_head + script_tail, 't', [name+x for x in old])
1628
1629                 # Also write a shell script that runs the .pyscript, for cygwin.
1630                 #
1631                 # We can't use a Python script, because the Python interpreter that we want
1632                 # to use is the native Windows one, which won't understand a cygwin path.
1633                 # Windows paths written with forward slashes are universally understood
1634                 # (by native Python, cygwin Python, and bash), so we'll use 'cygpath -m' to
1635                 # get the directory from which the script was run in that form. This makes
1636                 # the cygwin script and .pyscript position-independent, provided they are
1637                 # in the same directory.
1638
1639                 def quote_path(s):
1640                     return "\\'".join("'" + p.replace('\\', '/') + "'" for p in s.split("'"))
1641
1642                 pyscript = quote_path("/"+name+ext)
1643                 python_path = quote_path(sys.executable)
1644                 shell_script_text = (
1645                     '#!/bin/sh\n'
1646                     '%(generated_by)s\n'
1647                     '\n'
1648                     'ScriptDir=`cygpath -m "$0/.."`\n'
1649                     '%(python_path)s "${ScriptDir}"%(pyscript)s "$@"\n'
1650                 ) % locals()
1651                 yield (name, shell_script_text, 'b')
1652             else:
1653                 # On other platforms, we assume the right thing to do is to
1654                 # just write the stub with no extension.
1655                 yield (name, header + script_head + script_tail)
1656
1657
1658 def rmtree(path, ignore_errors=False, onerror=auto_chmod):
1659     """Recursively delete a directory tree.
1660
1661     This code is taken from the Python 2.4 version of 'shutil', because
1662     the 2.3 version doesn't really work right.
1663     """
1664     if ignore_errors:
1665         def onerror(*args):
1666             pass
1667     elif onerror is None:
1668         def onerror(*args):
1669             raise
1670     names = []
1671     try:
1672         names = os.listdir(path)
1673     except os.error, err:
1674         onerror(os.listdir, path, sys.exc_info())
1675     for name in names:
1676         fullname = os.path.join(path, name)
1677         try:
1678             mode = os.lstat(fullname).st_mode
1679         except os.error:
1680             mode = 0
1681         if stat.S_ISDIR(mode):
1682             rmtree(fullname, ignore_errors, onerror)
1683         else:
1684             try:
1685                 os.remove(fullname)
1686             except os.error, err:
1687                 onerror(os.remove, fullname, sys.exc_info())
1688     try:
1689         os.rmdir(path)
1690     except os.error:
1691         onerror(os.rmdir, path, sys.exc_info())
1692
1693 def bootstrap():
1694     # This function is called when setuptools*.egg is run using /bin/sh
1695     import setuptools; argv0 = os.path.dirname(setuptools.__path__[0])
1696     sys.argv[0] = argv0; sys.argv.append(argv0); main()
1697
1698
1699 def main(argv=None, **kw):
1700     from setuptools import setup
1701     from setuptools.dist import Distribution
1702     import distutils.core
1703
1704     USAGE = """\
1705 usage: %(script)s [options] requirement_or_url ...
1706    or: %(script)s --help
1707 """
1708
1709     def gen_usage (script_name):
1710         script = os.path.basename(script_name)
1711         return USAGE % vars()
1712
1713     def with_ei_usage(f):
1714         old_gen_usage = distutils.core.gen_usage
1715         try:
1716             distutils.core.gen_usage = gen_usage
1717             return f()
1718         finally:
1719             distutils.core.gen_usage = old_gen_usage
1720
1721     class DistributionWithoutHelpCommands(Distribution):
1722         common_usage = ""
1723         def _show_help(self,*args,**kw):
1724             with_ei_usage(lambda: Distribution._show_help(self,*args,**kw))
1725
1726     if argv is None:
1727         argv = sys.argv[1:]
1728
1729     with_ei_usage(lambda:
1730         setup(
1731             script_args = ['-q','easy_install', '-v']+argv,
1732             script_name = sys.argv[0] or 'easy_install',
1733             distclass=DistributionWithoutHelpCommands, **kw
1734         )
1735     )
1736
1737
1738
1739