aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xEasyInstall.txt25
-rwxr-xr-xsetuptools/command/develop.py2
-rwxr-xr-xsetuptools/command/easy_install.py85
-rwxr-xr-xsite.py18
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())
diff --git a/site.py b/site.py
index 2e229842..80e084b2 100755
--- a/site.py
+++ b/site.py
@@ -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':
+
+
+