diff options
-rwxr-xr-x | EasyInstall.txt | 25 | ||||
-rwxr-xr-x | setuptools/command/develop.py | 2 | ||||
-rwxr-xr-x | setuptools/command/easy_install.py | 85 | ||||
-rwxr-xr-x | site.py | 18 |
4 files changed, 96 insertions, 34 deletions
diff --git a/EasyInstall.txt b/EasyInstall.txt index f457150e..85a201b4 100755 --- a/EasyInstall.txt +++ b/EasyInstall.txt @@ -465,6 +465,15 @@ the project name, e.g.:: Dealing with Installation Conflicts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(NOTE: As of 0.6a11, this section is obsolete; it is retained here only so that +people using older versions of EasyInstall can consult it. As of version +0.6a11, installation conflicts are handled automatically without deleting the +old or system-installed packages, and without ignoring the issue. Instead, +eggs are automatically shifted to the front of ``sys.path`` using special +code added to the ``easy-install.pth`` file. So, if you are using version +0.6a11 or better of setuptools, you do not need to worry about conflicts, +and the following issues do not apply to you.) + EasyInstall installs distributions in a "managed" way, such that each distribution can be independently activated or deactivated on ``sys.path``. However, packages that were not installed by EasyInstall are "unmanaged", @@ -715,7 +724,9 @@ Command-Line Options package not being available locally, or due to the use of the ``--update`` or ``-U`` option. -``--delete-conflicting, -D`` (New in 0.5a9) +``--delete-conflicting, -D`` (Removed in 0.6a11) + (As of 0.6a11, this option is no longer necessary; please do not use it!) + If you are replacing a package that was previously installed *without* using EasyInstall, the old version may end up on ``sys.path`` before the version being installed with EasyInstall. EasyInstall will normally abort @@ -724,7 +735,9 @@ Command-Line Options option, however, EasyInstall will attempt to delete the files or directories itself, and then proceed with the installation. -``--ignore-conflicts-at-my-risk`` (New in 0.5a9) +``--ignore-conflicts-at-my-risk`` (Removed in 0.6a11) + (As of 0.6a11, this option is no longer necessary; please do not use it!) + Ignore conflicting packages and proceed with installation anyway, even though it means the package probably won't work properly. If the conflicting package is in a directory you can't write to, this may be your @@ -1066,6 +1079,14 @@ Known Issues time out or be missing a file. 0.6a11 + * Added automatic handling of installation conflicts. Eggs are now shifted to + the front of sys.path, in an order consistent with where they came from, + making EasyInstall seamlessly co-operate with system package managers. + + The ``--delete-conflicting`` and ``--ignore-conflicts-at-my-risk`` options + are now no longer necessary, and will generate warnings at the end of a + run if you use them. + * Don't recursively traverse subdirectories given to ``--find-links``. 0.6a10 diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 774b6b5f..f38506bb 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -24,6 +24,7 @@ class develop(easy_install): self.uninstall_link() else: self.install_for_development() + self.warn_deprecated_options() def initialize_options(self): self.uninstall = None @@ -38,7 +39,6 @@ class develop(easy_install): - def finalize_options(self): ei = self.get_finalized_command("egg_info") if ei.broken_egg_info: diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index b79b0d51..828fb17f 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -9,7 +9,7 @@ file, or visit the `EasyInstall home page`__. __ http://peak.telecommunity.com/DevCenter/EasyInstall """ -import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat +import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random from glob import glob from setuptools import Command from setuptools.sandbox import run_setup @@ -55,10 +55,9 @@ class easy_install(Command): ("always-copy", "a", "Copy all needed packages to install dir"), ("index-url=", "i", "base URL of Python Package Index"), ("find-links=", "f", "additional URL(s) to search for packages"), - ("delete-conflicting", "D", "delete old packages that get in the way"), + ("delete-conflicting", "D", "no longer needed; don't use this"), ("ignore-conflicts-at-my-risk", None, - "install even if old packages are in the way, even though it " - "most likely means the new package won't work."), + "no longer needed; don't use this"), ("build-directory=", "b", "download/extract/build in DIR; keep the results"), ('optimize=', 'O', @@ -80,6 +79,7 @@ class easy_install(Command): negative_opt = {'always-unzip': 'zip-ok'} create_index = PackageIndex + def initialize_options(self): self.zip_ok = None self.install_dir = self.script_dir = self.exclude_scripts = None @@ -221,28 +221,28 @@ class easy_install(Command): "writing list of installed files to '%s'" % self.record ) + self.warn_deprecated_options() finally: log.set_verbosity(self.distribution.verbose) - def pseudo_tempname(self): """Return a pseudo-tempname base in the install directory. - This code is intentionally naive; if a malicious party can write to the target directory you're already in deep doodoo. """ try: pid = os.getpid() except: - import random pid = random.randint(0,sys.maxint) return os.path.join(self.install_dir, "test-easy-install-%s" % pid) - - - - - + def warn_deprecated_options(self): + if self.delete_conflicting or self.ignore_conflicts_at_my_risk: + log.warn( + "Note: The -D, --delete-conflicting and" + " --ignore-conflicts-at-my-risk no longer have any purpose" + " and should not be used." + ) def check_site_dir(self): """Verify that self.install_dir is .pth-capable dir, if needed""" @@ -786,6 +786,7 @@ Please make the appropriate changes for your system and try again. def check_conflicts(self, dist): """Verify that there are no conflicting "old-style" packages""" + return dist # XXX temporarily disable until new strategy is stable from imp import find_module, get_suffixes from glob import glob @@ -812,7 +813,6 @@ Please make the appropriate changes for your system and try again. blockers.append(filename) elif ext in exts and base!='site': # XXX ugh blockers.append(os.path.join(path,filename)) - if blockers: self.found_conflicts(dist, blockers) @@ -1072,16 +1072,18 @@ Please make the appropriate changes for your system and try again.""" % ( sitepy = os.path.join(self.install_dir, "site.py") source = resource_string(Requirement.parse("setuptools"), "site.py") + current = "" if os.path.exists(sitepy): log.debug("Checking existing site.py in %s", self.install_dir) current = open(sitepy,'rb').read() - if current != source: + if not current.startswith('def __boot():'): raise DistutilsError( "%s is not a setuptools-generated site.py; please" " remove it." % sitepy ) - else: + + if current != source: log.info("Creating %s", sitepy) if not self.dry_run: ensure_directory(sitepy) @@ -1103,8 +1105,6 @@ Please make the appropriate changes for your system and try again.""" % ( - - INSTALL_SCHEMES = dict( posix = dict( install_dir = '$base/lib/python$py_version_short/site-packages', @@ -1323,9 +1323,13 @@ class PthDistributions(Environment): def _load(self): self.paths = [] + saw_import = False seen = {} if os.path.isfile(self.filename): for line in open(self.filename,'rt'): + if line.startswith('import'): + saw_import = True + continue path = line.rstrip() self.paths.append(path) if not path.strip() or path.strip().startswith('#'): @@ -1339,17 +1343,41 @@ class PthDistributions(Environment): continue seen[path] = 1 - while self.paths and not self.paths[-1].strip(): self.paths.pop() + if self.paths and not saw_import: + self.dirty = True # ensure anything we touch has import wrappers + + while self.paths and not self.paths[-1].strip(): + self.paths.pop() + + def save(self): """Write changed .pth file back to disk""" - if self.dirty: + if not self.dirty: + return + + data = '\n'.join(self.paths) + if data: log.debug("Saving %s", self.filename) - data = '\n'.join(self.paths+['']) + data = ( + "import sys; sys.__plen = len(sys.path)\n" + "%s\n" + "import sys; new=sys.path[sys.__plen:];" + " del sys.path[sys.__plen:];" + " p=getattr(sys,'__egginsert',0); sys.path[p:p]=new;" + " sys.__egginsert = p+len(new)\n" + ) % data + if os.path.islink(self.filename): os.unlink(self.filename) - f = open(self.filename,'wt'); f.write(data); f.close() - self.dirty = False + f = open(self.filename,'wb') + f.write(data); f.close() + + elif os.path.exists(self.filename): + log.debug("Deleting empty %s", self.filename) + os.unlink(self.filename) + + self.dirty = False def add(self,dist): """Add `dist` to the distribution map""" @@ -1392,6 +1420,19 @@ def auto_chmod(func, arg, exc): + + + + + + + + + + + + + def get_script_args(dist, executable=sys_executable): """Yield write_script() argument tuples for a distribution's entrypoints""" spec = str(dist.as_requirement()) @@ -41,12 +41,16 @@ def __boot(): known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp + oldpos = getattr(sys,'__egginsert',0) # save old insertion position + sys.__egginsert = 0 # and reset the current one + for item in PYTHONPATH: addsitedir(item) + + sys.__egginsert += oldpos # restore effective old position d,nd = makepath(stdpath[0]) insert_at = None - skipped = [] new_path = [] for item in sys.path: @@ -54,22 +58,15 @@ def __boot(): if np==nd and insert_at is None: # We've hit the first 'system' path entry, so added entries go here - new_path.extend(skipped) insert_at = len(new_path) - skipped = [] - if np in known_paths: - # Old path, just copy + if np in known_paths or insert_at is None: new_path.append(item) - elif insert_at is None: - # New path before the insert point, buffer it - skipped.append(item) else: # new path after the insert point, back-insert it new_path.insert(insert_at, item) insert_at += 1 - new_path.extend(skipped) sys.path[:] = new_path if __name__=='site': @@ -80,3 +77,6 @@ if __name__=='site': + + + |