[Python-checkins] python/nondist/sandbox/setuptools site.py, NONE, 1.1 virtual-python.py, NONE, 1.1 EasyInstall.txt, 1.64, 1.65 api_tests.txt, 1.6, 1.7 pkg_resources.py, 1.73, 1.74 pkg_resources.txt, 1.16, 1.17 setup.py, 1.44, 1.45

pje@users.sourceforge.net pje at users.sourceforge.net
Mon Oct 17 04:26:44 CEST 2005


Update of /cvsroot/python/python/nondist/sandbox/setuptools
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16625

Modified Files:
	EasyInstall.txt api_tests.txt pkg_resources.py 
	pkg_resources.txt setup.py 
Added Files:
	site.py virtual-python.py 
Log Message:
Significantly enhanced support and docs for "non-root" installation, 
including both "virtual" and PYTHONPATH-based installs.  The activation
precedence of distributions has also changed so that PYTHONPATH-based
non-root installs can include eggs that override system-defined packages
(whether managed or unmanaged).  This version should eliminate most 
common installation complaints from non-root Python users.
Note: this version includes a hacked 'site.py' to support processing 
.pth files in directories that come *before* site-packages on sys.path.
However, because of its placement, it should only come into play when
a user puts the setuptools .egg file *directly* on PYTHONPATH, so it
doesn't affect "virtual" or "root" installations.  It's strictly to 
provide support for luddites who refuse to give up their 
existing non-root PYTHONPATH setup unless you pry it from their cold, 
dead hands.  :)


--- NEW FILE: site.py ---
"""Append module search paths for third-party packages to sys.path.

****************************************************************
* This module is automatically imported during initialization, *
* if you add the setuptools egg to PYTHONPATH (to support the  *
* simple non-root installation mode)                           *
****************************************************************

In earlier versions of Python (up to 1.5a3), scripts or modules that
needed to use site-specific modules would place ``import site''
somewhere near the top of their code.  Because of the automatic
import, this is no longer necessary (but code that does it still
works).

This will append site-specific paths to the module search path.  On
Unix, it starts with sys.prefix and sys.exec_prefix (if different) and
appends lib/python<version>/site-packages as well as lib/site-python.
On other platforms (mainly Mac and Windows), it uses just sys.prefix
(and sys.exec_prefix, if different, but this is unlikely).  The
resulting directories, if they exist, are appended to sys.path, and
also inspected for path configuration files.

A path configuration file is a file whose name has the form
<package>.pth; its contents are additional directories (one per line)
to be added to sys.path.  Non-existing directories (or
non-directories) are never added to sys.path; no directory is added to
sys.path more than once.  Blank lines and lines beginning with
'#' are skipped. Lines starting with 'import' are executed.

For example, suppose sys.prefix and sys.exec_prefix are set to
/usr/local and there is a directory /usr/local/lib/python1.5/site-packages
with three subdirectories, foo, bar and spam, and two path
configuration files, foo.pth and bar.pth.  Assume foo.pth contains the
following:

  # foo package configuration
  foo
  bar
  bletch

and bar.pth contains:

  # bar package configuration
  bar

Then the following directories are added to sys.path, in this order:

  /usr/local/lib/python1.5/site-packages/bar
  /usr/local/lib/python1.5/site-packages/foo

Note that bletch is omitted because it doesn't exist; bar precedes foo
because bar.pth comes alphabetically before foo.pth; and spam is
omitted because it is not mentioned in either path configuration file.

After these path manipulations, an attempt is made to import a module
named sitecustomize, which can perform arbitrary additional
site-specific customizations.  If this import fails with an
ImportError exception, it is silently ignored.

"""

import sys
import os
import __builtin__


def makepath(*paths):
    dir = os.path.abspath(os.path.join(*paths))
    return dir, os.path.normcase(dir)

def abs__file__():
    """Set all module' __file__ attribute to an absolute path"""
    for m in sys.modules.values():
        try:
            m.__file__ = os.path.abspath(m.__file__)
        except AttributeError:
            continue

def removeduppaths():
    """ Remove duplicate entries from sys.path along with making them
    absolute"""
    # This ensures that the initial path provided by the interpreter contains
    # only absolute pathnames, even if we're running from the build directory.
    L = []
    known_paths = {}
    for dir in sys.path:
        # Filter out duplicate paths (on case-insensitive file systems also
        # if they only differ in case); turn relative paths into absolute
        # paths.
        dir, dircase = makepath(dir)
        if not dircase in known_paths:
            L.append(dir)
            known_paths[dircase] = 1
    sys.path[:] = L
    return known_paths

# XXX This should not be part of site.py, since it is needed even when
# using the -S option for Python.  See http://www.python.org/sf/586680
def addbuilddir():
    """Append ./build/lib.<platform> in case we're running in the build dir
    (especially for Guido :-)"""
    from distutils.util import get_platform
    s = "build/lib.%s-%.3s" % (get_platform(), sys.version)
    s = os.path.join(os.path.dirname(sys.path[-1]), s)
    sys.path.append(s)

def _init_pathinfo():
    """Return a set containing all existing directory entries from sys.path"""
    d = {}
    for dir in sys.path:
        try:
            if os.path.isdir(dir):
                dir, dircase = makepath(dir)
                d[dircase] = 1
        except TypeError:
            continue
    return d

def addpackage(sitedir, name, known_paths):
    """Add a new path to known_paths by combining sitedir and 'name' or execute
    sitedir if it starts with 'import'"""
    if known_paths is None:
        known_paths = _init_pathinfo()
        reset = 1
    else:
        reset = 0
    fullname = os.path.join(sitedir, name)
    try:
        f = open(fullname, "rU")
    except IOError:
        return
    try:
        for line in f:
            if line.startswith("#"):
                continue
            if line.startswith("import"):
                exec line
                continue
            line = line.rstrip()
            dir, dircase = makepath(sitedir, line)
            if not dircase in known_paths and os.path.exists(dir):
                sys.path.append(dir)
                known_paths[dircase] = 1
    finally:
        f.close()
    if reset:
        known_paths = None
    return known_paths

def addsitedir(sitedir, known_paths=None):
    """Add 'sitedir' argument to sys.path if missing and handle .pth files in
    'sitedir'"""
    if known_paths is None:
        known_paths = _init_pathinfo()
        reset = 1
    else:
        reset = 0
    sitedir, sitedircase = makepath(sitedir)
    if not sitedircase in known_paths:
        sys.path.append(sitedir)        # Add path component
    try:
        names = os.listdir(sitedir)
    except os.error:
        return
    names.sort()
    for name in names:
        if name.endswith(os.extsep + "pth"):
            addpackage(sitedir, name, known_paths)
    if reset:
        known_paths = None
    return known_paths

def addsitepackages(known_paths):
    """Add site-packages (and possibly site-python) to sys.path"""
    prefixes = [sys.prefix]
    if sys.exec_prefix != sys.prefix:
        prefixes.append(sys.exec_prefix)
    for prefix in prefixes:
        if prefix:
            if sys.platform in ('os2emx', 'riscos'):
                sitedirs = [os.path.join(prefix, "Lib", "site-packages")]
            elif os.sep == '/':
                sitedirs = [os.path.join(prefix,
                                         "lib",
                                         "python" + sys.version[:3],
                                         "site-packages"),
                            os.path.join(prefix, "lib", "site-python")]
            else:
                sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")]
            if sys.platform == 'darwin':
                # for framework builds *only* we add the standard Apple
                # locations. Currently only per-user, but /Library and
                # /Network/Library could be added too
                if 'Python.framework' in prefix:
                    home = os.environ.get('HOME')
                    if home:
                        sitedirs.append(
                            os.path.join(home,
                                         'Library',
                                         'Python',
                                         sys.version[:3],
                                         'site-packages'))
            for sitedir in sys.path:
                if sitedir and os.path.isdir(sitedir):
                    addsitedir(sitedir, known_paths)
    return None


def setBEGINLIBPATH():
    """The OS/2 EMX port has optional extension modules that do double duty
    as DLLs (and must use the .DLL file extension) for other extensions.
    The library search path needs to be amended so these will be found
    during module import.  Use BEGINLIBPATH so that these are at the start
    of the library search path.

    """
    dllpath = os.path.join(sys.prefix, "Lib", "lib-dynload")
    libpath = os.environ['BEGINLIBPATH'].split(';')
    if libpath[-1]:
        libpath.append(dllpath)
    else:
        libpath[-1] = dllpath
    os.environ['BEGINLIBPATH'] = ';'.join(libpath)


def setquit():
    """Define new built-ins 'quit' and 'exit'.
    These are simply strings that display a hint on how to exit.

    """
    if os.sep == ':':
        exit = 'Use Cmd-Q to quit.'
    elif os.sep == '\\':
        exit = 'Use Ctrl-Z plus Return to exit.'
    else:
        exit = 'Use Ctrl-D (i.e. EOF) to exit.'
    __builtin__.quit = __builtin__.exit = exit


class _Printer(object):
    """interactive prompt objects for printing the license text, a list of
    contributors and the copyright notice."""

    MAXLINES = 23

    def __init__(self, name, data, files=(), dirs=()):
        self.__name = name
        self.__data = data
        self.__files = files
        self.__dirs = dirs
        self.__lines = None

    def __setup(self):
        if self.__lines:
            return
        data = None
        for dir in self.__dirs:
            for filename in self.__files:
                filename = os.path.join(dir, filename)
                try:
                    fp = file(filename, "rU")
                    data = fp.read()
                    fp.close()
                    break
                except IOError:
                    pass
            if data:
                break
        if not data:
            data = self.__data
        self.__lines = data.split('\n')
        self.__linecnt = len(self.__lines)

    def __repr__(self):
        self.__setup()
        if len(self.__lines) <= self.MAXLINES:
            return "\n".join(self.__lines)
        else:
            return "Type %s() to see the full %s text" % ((self.__name,)*2)

    def __call__(self):
        self.__setup()
        prompt = 'Hit Return for more, or q (and Return) to quit: '
        lineno = 0
        while 1:
            try:
                for i in range(lineno, lineno + self.MAXLINES):
                    print self.__lines[i]
            except IndexError:
                break
            else:
                lineno += self.MAXLINES
                key = None
                while key is None:
                    key = raw_input(prompt)
                    if key not in ('', 'q'):
                        key = None
                if key == 'q':
                    break

def setcopyright():
    """Set 'copyright' and 'credits' in __builtin__"""
    __builtin__.copyright = _Printer("copyright", sys.copyright)
    if sys.platform[:4] == 'java':
        __builtin__.credits = _Printer(
            "credits",
            "Jython is maintained by the Jython developers (www.jython.org).")
    else:
        __builtin__.credits = _Printer("credits", """\
    Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
    for supporting Python development.  See www.python.org for more information.""")
    here = os.path.dirname(os.__file__)
    __builtin__.license = _Printer(
        "license", "See http://www.python.org/%.3s/license.html" % sys.version,
        ["LICENSE.txt", "LICENSE"],
        [os.path.join(here, os.pardir), here, os.curdir])


class _Helper(object):
    """Define the built-in 'help'.
    This is a wrapper around pydoc.help (with a twist).

    """

    def __repr__(self):
        return "Type help() for interactive help, " \
               "or help(object) for help about object."
    def __call__(self, *args, **kwds):
        import pydoc
        return pydoc.help(*args, **kwds)

def sethelper():
    __builtin__.help = _Helper()

def aliasmbcs():
    """On Windows, some default encodings are not provided by Python,
    while they are always available as "mbcs" in each locale. Make
    them usable by aliasing to "mbcs" in such a case."""
    if sys.platform == 'win32':
        import locale, codecs
        enc = locale.getdefaultlocale()[1]
        if enc.startswith('cp'):            # "cp***" ?
            try:
                codecs.lookup(enc)
            except LookupError:
                import encodings
                encodings._cache[enc] = encodings._unknown
                encodings.aliases.aliases[enc] = 'mbcs'

def setencoding():
    """Set the string encoding used by the Unicode implementation.  The
    default is 'ascii', but if you're willing to experiment, you can
    change this."""
    encoding = "ascii" # Default value set by _PyUnicode_Init()
    if 0:
        # Enable to support locale aware default string encodings.
        import locale
        loc = locale.getdefaultlocale()
        if loc[1]:
            encoding = loc[1]
    if 0:
        # Enable to switch off string to Unicode coercion and implicit
        # Unicode to string conversion.
        encoding = "undefined"
    if encoding != "ascii":
        # On Non-Unicode builds this will raise an AttributeError...
        sys.setdefaultencoding(encoding) # Needs Python Unicode build !


def execsitecustomize():
    """Run custom site specific code, if available."""
    try:
        import sitecustomize
    except ImportError:
        pass


def main():
    abs__file__()
    paths_in_sys = removeduppaths()
    if (os.name == "posix" and sys.path and
        os.path.basename(sys.path[-1]) == "Modules"):
        addbuilddir()
    paths_in_sys = addsitepackages(paths_in_sys)
    if sys.platform == 'os2emx':
        setBEGINLIBPATH()
    setquit()
    setcopyright()
    sethelper()
    aliasmbcs()
    setencoding()
    execsitecustomize()
    # Remove sys.setdefaultencoding() so that users cannot change the
    # encoding after initialization.  The test for presence is needed when
    # this module is run as a script, because this code is executed twice.
    if hasattr(sys, "setdefaultencoding"):
        del sys.setdefaultencoding

main()

def _test():
    print "sys.path = ["
    for dir in sys.path:
        print "    %r," % (dir,)
    print "]"

if __name__ == '__main__':
    _test()

--- NEW FILE: virtual-python.py ---
"""Create a "virtual" Python installation

Based on a script created by Ian Bicking."""

import sys, os, optparse, shutil
join = os.path.join
py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])

def mkdir(path):
    if not os.path.exists(path):
        print 'Creating %s' % path
        os.makedirs(path)
    else:
        if verbose:
            print 'Directory %s already exists'

def symlink(src, dest):
    if not os.path.exists(dest):
        if verbose:
            print 'Creating symlink %s' % dest
        os.symlink(src, dest)
    else:
        print 'Symlink %s already exists' % dest


def rmtree(dir):
    if os.path.exists(dir):
        print 'Deleting tree %s' % dir
        shutil.rmtree(dir)
    else:
        if verbose:
            print 'Do not need to delete %s; already gone' % dir

def make_exe(fn):
    if os.name == 'posix':
        oldmode = os.stat(fn).st_mode & 07777
        newmode = (oldmode | 0555) & 07777
        os.chmod(fn, newmode)
        if verbose:
            print 'Changed mode of %s to %s' % (fn, oct(newmode))

def main():
    if os.name != 'posix':
        print "This script only works on Unix-like platforms, sorry."
        return

    parser = optparse.OptionParser()
    
    parser.add_option('-v', '--verbose', action='count', dest='verbose',
        default=0, help="Increase verbosity")
        
    parser.add_option('--prefix', dest="prefix", default='~',
        help="The base directory to install to (default ~)")
        
    parser.add_option('--clear', dest='clear', action='store_true',
        help="Clear out the non-root install and start from scratch")
        
    parser.add_option('--no-site-packages', dest='no_site_packages',
        action='store_true',
        help="Don't copy the contents of the global site-packages dir to the "
             "non-root site-packages")
    
    options, args = parser.parse_args()
    global verbose

    home_dir = os.path.expanduser(options.prefix)
    lib_dir = join(home_dir, 'lib', py_version)
    inc_dir = join(home_dir, 'include', py_version)
    bin_dir = join(home_dir, 'bin')

    if sys.executable.startswith(bin_dir):
        print 'Please use the *system* python to run this script'
        return

    verbose = options.verbose
    assert not args, "No arguments allowed"
        
    if options.clear:
        rmtree(lib_dir)
        rmtree(inc_dir)
        print 'Not deleting', bin_dir

    prefix = sys.prefix
    mkdir(lib_dir)
    stdlib_dir = join(prefix, 'lib', py_version)
    for fn in os.listdir(stdlib_dir):
        if fn != 'site-packages':
            symlink(join(stdlib_dir, fn), join(lib_dir, fn))

    mkdir(join(lib_dir, 'site-packages'))
    if not options.no_site_packages:
        for fn in os.listdir(join(stdlib_dir, 'site-packages')):
            symlink(join(stdlib_dir, 'site-packages', fn),
                    join(lib_dir, 'site-packages', fn))

    mkdir(inc_dir)
    stdinc_dir = join(prefix, 'include', py_version)
    for fn in os.listdir(stdinc_dir):
        symlink(join(stdinc_dir, fn), join(inc_dir, fn))

    if sys.exec_prefix != sys.prefix:
        exec_dir = join(sys.exec_prefix, 'lib', py_version)
        for fn in os.listdir(exec_dir):
            symlink(join(exec_dir, fn), join(lib_dir, fn))

    mkdir(bin_dir)
    print 'Copying %s to %s' % (sys.executable, bin_dir)
    py_executable = join(bin_dir, 'python')
    if sys.executable != py_executable:
        shutil.copyfile(sys.executable, py_executable)
        make_exe(py_executable)

    pydistutils = os.path.expanduser('~/.pydistutils.cfg')
    if os.path.exists(pydistutils):
        print 'Please make sure you remove any previous custom paths from'
        print "your", pydistutils, "file."

    print "You're now ready to download ez_setup.py, and run"
    print py_executable, "ez_setup.py"
      
if __name__ == '__main__':
    main()


Index: EasyInstall.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/EasyInstall.txt,v
retrieving revision 1.64
retrieving revision 1.65
diff -u -d -r1.64 -r1.65
--- EasyInstall.txt	16 Oct 2005 20:45:28 -0000	1.64
+++ EasyInstall.txt	17 Oct 2005 02:26:39 -0000	1.65
@@ -25,6 +25,8 @@
 ====================
 
 
+.. _installation instructions:
+
 Installing "Easy Install"
 -------------------------
 
@@ -718,12 +720,46 @@
 now or in the future.)
 
 If you are on a Linux, BSD, Cygwin, or other similar Unix-like operating
-system, you should create a ``~/lib/python2.x/site-packages`` directory
-instead.  (Note: Ian Bicking has created a script that can automate most of the
-process that follows; see http://svn.colorstudy.com/home/ianb/non_root_python.py
-for details.)
+system, you have a couple of different options.  You can create a "virtual"
+Python installation, which uses its own library directories and some symlinks
+to the site-wide Python.  Or, you can use a "traditional" ``PYTHONPATH``-based
+installation, which isn't as flexible, but which you may find more familiar,
+especially if you already have a custom ``PYTHONPATH`` set up.
 
-You will need to know your Python version's ``sys.prefix`` and
+
+Creating a "Virtual" Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the simplest case, your virtual Python installation will live under the
+``~/lib/python2.x``, ``~/include/python2.x``, and ``~/bin`` directories.  Just
+download `virtual-python.py`_ and run it using the site-wide Python.  If you
+want to customize the location, you can use the ``--prefix`` option to specify
+an installation base directory in place of ``~``.  (Use ``--help`` to get the
+complete list of options.)
+
+.. _virtual-python.py: http://peak.telecommunity.com/dist/virtual-python.py
+
+When you're done, you'll have a ``~/bin/python`` executable that's linked to
+the local Python installation and inherits all its current libraries, but which
+allows you to add as many new libraries as you want.  Simply use this new
+Python in place of your system-defined one, and you can modify it as you like
+without breaking anything that relies on the system Python.  You'll also still
+need to follow the standard `installation instructions`_ to install setuptools
+and EasyInstall, using your new ``~/bin/python`` executable in place of the
+system Python.
+
+Note that if you were previously setting a ``PYTHONPATH`` and/or had other
+special configuration options in your ``~/.pydistutils.cfg``, you may need to
+remove these settings *before* running ``virtual-python.py``.  You should
+also make sure that the ``~/bin`` directory (or whatever directory you choose)
+is on your ``PATH``, because that is where EasyInstall will install new Python
+scripts.
+
+If you'd prefer to do the installation steps by hand, or just want to know what
+the script will do, here are the steps.  (If you don't care how it works, you
+can just skip the rest of this section.)
+
+First, you will need to know your Python version's ``sys.prefix`` and
 ``sys.exec_prefix``, which you can find out by running::
 
     python -c "import sys; print sys.prefix; print sys.exec_prefix"
@@ -761,13 +797,43 @@
 Do NOT use a symlink!  The Python binary must be copied or hardlinked,
 otherwise it will use the system ``site-packages`` directory and not yours.
 
-Note that if you were previously setting a ``PYTHONPATH`` and/or had other
-special configuration options in your ``~/.pydistutils.cfg``, you may need to
-remove these settings and relocate any older installed modules to your
-new ``~/lib/python2.x/site-packages`` directory.  Also note that you must now
-make sure to use the ``~/bin/python`` executable instead of the system Python,
-and ideally you should put the ``~/bin`` directory first on your ``PATH`` as
-well, because that is where EasyInstall will install new Python scripts.
+You can now proceed with the standard `installation instructions`_.
+
+
+"Traditional" ``PYTHONPATH``-based Installation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This installation method is not as robust or as flexible as `creating a
+"virtual" python`_ installation, as it uses various tricks to fool Python into
+processing ``.pth`` files where it normally wouldn't.  We suggest you try the
+virtual Python approach first, as we are providing this method mainly for
+people who just can't get past their unshakeable belief that creating a virtual
+python is somehow "Not Right", or that putting stuff on ``PYTHONPATH`` "Should
+Just Work, Darnit."  So, if you're not one of those people, you don't
+need these instructions.  :-)
+
+Assuming that you want to install packages in a directory called ``~/py-lib``,
+and scripts in ``~/bin``, here's what you need to do:
+
+First, edit ``~/.pydistutils.cfg`` to include these settings::
+
+    [install]
+    install_lib = ~/py-lib
+    install_scripts = ~/bin
+    
+    [easy_install]
+    site_dirs = ~/py_lib
+
+Be sure to do this *before* you try to run the ``ez_setup.py`` installation
+script.  Then, follow the standard `installation instructions`_, but take
+careful note of the full pathname of the ``.egg`` file that gets installed, so
+that you can add it to your ``PYTHONPATH``, along with ``~/py_lib``.
+
+You *must* add the setuptools egg file to your ``PYTHONPATH`` manually, or it
+will not work, and neither will any other packages you install with
+EasyInstall.  You will not, however, have to manually add any other
+packages to the ``PYTHONPATH``; EasyInstall will take care of them for you, as
+long as the setuptools egg is explicitly listed in ``PYTHONPATH``.
 
 
 Release Notes/Change History
@@ -778,6 +844,12 @@
    time out or be missing a file.
 
 0.6a6
+ * Added support for "traditional" PYTHONPATH-based non-root installation, and
+   also the convenient ``virtual-python.py`` script, based on a contribution
+   by Ian Bicking.  The setuptools egg now contains a hacked ``site`` module
+   that makes the PYTHONPATH-based approach work with .pth files, so that you
+   can get the full EasyInstall feature set on such installations.
+
  * Added ``--no-deps`` option.
 
  * Improved Windows ``.exe`` script wrappers so that the script can have the

Index: api_tests.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/api_tests.txt,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- api_tests.txt	13 Aug 2005 23:04:08 -0000	1.6
+++ api_tests.txt	17 Oct 2005 02:26:39 -0000	1.7
@@ -182,12 +182,12 @@
     ['...example.com...', '...pkg_resources...', '...pkg_resources...']
 
 And you can specify the path entry a distribution was found under, using the
-optional second parameter to ``add()``
+optional second parameter to ``add()``::
 
+    >>> ws = WorkingSet([])
     >>> ws.add(dist,"foo")
-    >>> ws.add(dist,"bar")
     >>> ws.entries
-    ['http://example.com/something', ..., 'foo', 'bar']
+    ['foo']
 
 But even if a distribution is found under multiple path entries, it still only
 shows up once when iterating the working set:
@@ -222,14 +222,14 @@
     >>> def added(dist): print "Added", dist
     >>> ws.subscribe(added)
     Added Bar 0.9
-    >>> foo12 = Distribution(project_name="Foo", version="1.2") 
+    >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12") 
     >>> ws.add(foo12)
     Added Foo 1.2
 
 Note, however, that only the first distribution added for a given project name
 will trigger a callback, even during the initial ``subscribe()`` callback::
 
-    >>> foo14 = Distribution(project_name="Foo", version="1.4") 
+    >>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14") 
     >>> ws.add(foo14)   # no callback, because Foo 1.2 is already active
 
     >>> ws = WorkingSet([])

Index: pkg_resources.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -d -r1.73 -r1.74
--- pkg_resources.py	26 Sep 2005 00:35:35 -0000	1.73
+++ pkg_resources.py	17 Oct 2005 02:26:39 -0000	1.74
@@ -356,7 +356,7 @@
         self.entry_keys.setdefault(entry, [])
         self.entries.append(entry)
         for dist in find_distributions(entry, True):
-            self.add(dist, entry)
+            self.add(dist, entry, False)
 
 
     def __contains__(self,dist):
@@ -421,7 +421,7 @@
                     seen[key]=1
                     yield self.by_key[key]
 
-    def add(self, dist, entry=None):
+    def add(self, dist, entry=None, insert=True):
         """Add `dist` to working set, associated with `entry`
 
         If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
@@ -432,23 +432,23 @@
         doesn't already have a distribution in the set.  If it's added, any
         callbacks registered with the ``subscribe()`` method will be called.
         """
+        if insert:
+            dist.insert_on(self.entries, entry)
+
         if entry is None:
             entry = dist.location
-
-        if entry not in self.entry_keys:
-            self.entries.append(entry)
-            self.entry_keys[entry] = []
+        keys = self.entry_keys.setdefault(entry,[])
 
         if dist.key in self.by_key:
             return      # ignore hidden distros
 
         self.by_key[dist.key] = dist
-        keys = self.entry_keys[entry]
         if dist.key not in keys:
             keys.append(dist.key)
 
         self._added_new(dist)
 
+
     def resolve(self, requirements, env=None, installer=None):
         """List all distributions needed to (recursively) meet `requirements`
 
@@ -1837,12 +1837,12 @@
     def activate(self,path=None):
         """Ensure distribution is importable on `path` (default=sys.path)"""
         if path is None: path = sys.path
-        if self.location not in path:
-            path.append(self.location)
+        self.insert_on(path)
         if path is sys.path:
             fixup_namespace_packages(self.location)
             map(declare_namespace, self._get_metadata('namespace_packages.txt'))
 
+
     def egg_name(self):
         """Return what this distribution's standard .egg filename should be"""
         filename = "%s-%s-py%s" % (
@@ -1907,6 +1907,47 @@
         """Return the EntryPoint object for `group`+`name`, or ``None``"""
         return self.get_entry_map(group).get(name)
 
+    def insert_on(self, path, loc = None):
+        """Insert self.location in path before its nearest parent directory"""
+        loc = loc or self.location
+        if not loc: return
+        if path is sys.path:
+            self.check_version_conflict()
+        best, pos = 0, -1
+        for p,item in enumerate(path):
+            if loc.startswith(item) and len(item)>best and loc<>item:
+                best, pos = len(item), p
+        if pos==-1:
+            if loc not in path: path.append(loc)
+        elif loc not in path[:pos+1]:
+            while loc in path: path.remove(loc)
+            path.insert(pos,loc)
+
+
+
+    def check_version_conflict(self):
+        if self.key=='setuptools':
+            return      # ignore the inevitable setuptools self-conflicts  :(
+
+        nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
+
+        for modname in self._get_metadata('top_level.txt'):
+            if modname not in sys.modules or modname in nsp:
+                continue
+
+            fn = getattr(sys.modules[modname], '__file__', None)
+            if fn and fn.startswith(self.location):
+                continue
+
+            from warnings import warn
+            warn(
+                "Module %s was already imported from %s, but %s is being added"
+                " to sys.path" % (modname, fn, self.location)
+            )
+
+
+
+
 
 
 
@@ -2165,9 +2206,9 @@
 add_activation_listener = working_set.subscribe
 run_script = working_set.run_script
 run_main = run_script   # backward compatibility
-
 # Activate all distributions already on sys.path, and ensure that
 # all distributions added to the working set in the future (e.g. by
 # calling ``require()``) will get activated as well.
 add_activation_listener(lambda dist: dist.activate())
+working_set.entries=[]; map(working_set.add_entry,sys.path) # match order
 

Index: pkg_resources.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- pkg_resources.txt	26 Sep 2005 00:35:35 -0000	1.16
+++ pkg_resources.txt	17 Oct 2005 02:26:39 -0000	1.17
@@ -1488,6 +1488,17 @@
 Release Notes/Change History
 ----------------------------
 
+0.6a6
+ * Activated distributions are now inserted in ``sys.path`` (and the working
+   set) just before the directory that contains them, instead of at the end.
+   This allows e.g. eggs in ``site-packages`` to override unmanged modules in
+   the same location, and allows eggs found earlier on ``sys.path`` to override
+   ones found later.
+
+ * When a distribution is activated, it now checks whether any contained
+   non-namespace modules have already been imported and issues a warning if
+   a conflicting module has already been imported.
+
 0.6a4
  * Fix a bug in ``WorkingSet.resolve()`` that was introduced in 0.6a3.
 

Index: setup.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setup.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -d -r1.44 -r1.45
--- setup.py	29 Sep 2005 17:05:43 -0000	1.44
+++ setup.py	17 Oct 2005 02:26:39 -0000	1.45
@@ -37,7 +37,7 @@
     test_suite = 'setuptools.tests.test_suite',
     packages = find_packages(),
     package_data = {'setuptools': ['*.exe']},
-    py_modules = ['pkg_resources', 'easy_install'],
+    py_modules = ['pkg_resources', 'easy_install', 'site'],
 
     zip_safe = False,   # We want 'python -m easy_install' to work, for now :(
     entry_points = {



More information about the Python-checkins mailing list