Commits

Anonymous committed d24edbe

Major updates and fixes include:

* Fix for the Python 2.6.3 build_ext API change
* Support for the most recent Sourceforge download link insanity
* Support for SVN 1.6
* Stop crashing on certain types of HTTP error
* Stop re-trying URLs that already failed retrieval once
* Fixes for various dependency management problems such as looping
builds, re-downloading packages already present on sys.path (but not
in a registered "site" directory), and randomly preferring local -f
packages over local installed packages
* Prevent lots of spurious "already imported from another path" warnings
(e.g. when pkg_resources is imported late)
* Ensure C libraries (as opposed to extensions) are also built when
doing bdist_egg

Other changes:

* Misc. documentation fixes
* Improved Jython support
* Fewer warnings under Python 2.6+
* Warn when 'packages' uses paths instead of package names (because it
causes other problems, like spurious "already imported" warnings)
* Stop using /usr/bin/sw_vers on Mac OS (replaced w/'platform' module
calls)

Note: This is NOT a merge from Distribute; upon review, many of the
tracker-submitted patches used as a basis for forking were incorrect,
incomplete, introduced new bugs, or were not addressing the root
causes. (E.g., one of the changes in this patch fixes three superficially
unrelated issues in the setuptools bug tracker.) Careful review will be
required if you want to merge this work back into Distribute.

Comments (0)

Files changed (20)

 If you want to delete the currently installed version of a package (or all
 versions of a package), you should first run::
 
-    easy_install -m PackageName
+    easy_install -mxN PackageName
 
 This will ensure that Python doesn't continue to search for a package you're
 planning to remove. After you've done this, you can safely delete the .egg
 
 
 Password-Protected Sites
-------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~
 
 If a site you want to download from is password-protected using HTTP "Basic"
 authentication, you can specify your credentials in the URL, like so::
 Release Notes/Change History
 ============================
 
-0.6final
+0.6c10
+ * Support for the most recent Sourceforge download link insanity
+
+ * Stop crashing on certain types of HTTP error
+
+ * Stop re-trying URLs that already failed retrieval once
+
+ * Fixes for various dependency management problems such as looping builds,
+   re-downloading packages already present on sys.path (but not in a registered
+   "site" directory), and semi-randomly preferring local "-f" packages over
+   local installed packages
+
+0.6c9
  * Fixed ``win32.exe`` support for .pth files, so unnecessary directory nesting
    is flattened out in the resulting egg.  (There was a case-sensitivity
    problem that affected some distributions, notably ``pywin32``.)
 This file can also be run as a script to install or upgrade setuptools.
 """
 import sys
-DEFAULT_VERSION = "0.6c9"
+DEFAULT_VERSION = "0.6c10"
 DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
 
 md5_data = {
 method.
 """
 
-import sys, os, zipimport, time, re, imp, new
+import sys, os, zipimport, time, re, imp
 
 try:
     frozenset
 
 
 
+_state_vars = {}
+
+def _declare_state(vartype, **kw):
+    g = globals()
+    for name, val in kw.iteritems():
+        g[name] = val
+        _state_vars[name] = vartype
+
+def __getstate__():
+    state = {}
+    g = globals()
+    for k, v in _state_vars.iteritems():
+        state[k] = g['_sget_'+v](g[k])
+    return state
+
+def __setstate__(state):
+    g = globals()
+    for k, v in state.iteritems():
+        g['_sset_'+_state_vars[k]](k, g[k], v)
+    return state
+
+def _sget_dict(val):
+    return val.copy()
+
+def _sset_dict(key, ob, state):
+    ob.clear()
+    ob.update(state)
+
+def _sget_object(val):
+    return val.__getstate__()
+
+def _sset_object(key, ob, state):
+    ob.__setstate__(state)
+
+_sget_none = _sset_none = lambda *args: None
+
+
+
+
+
+
 def get_supported_platform():
     """Return this platform's maximum compatible version.
 
 
 def _macosx_vers(_cache=[]):
     if not _cache:
-        info = os.popen('/usr/bin/sw_vers').read().splitlines()
-        for line in info:
-            key, value = line.split(None, 1)
-            if key == 'ProductVersion:':
-                _cache.append(value.strip().split("."))
-                break
-        else:
-            raise ValueError, "What?!"
+        from platform import mac_ver
+        _cache.append(mac_ver()[0].split('.'))
     return _cache[0]
 
 def _macosx_arch(machine):
 
 
 
+
+
+
+
+
+
 def compatible_platforms(provided,required):
     """Can code for the `provided` platform run on the `required` platform?
 
     def add_entry(self, entry):
         """Add a path item to ``.entries``, finding any distributions on it
 
-        ``find_distributions(entry,False)`` is used to find distributions
+        ``find_distributions(entry, True)`` is used to find distributions
         corresponding to the path entry, and they are added.  `entry` is
         always appended to ``.entries``, even if it is already present.
         (This is because ``sys.path`` can contain the same value more than
         activated to fulfill the requirements; all relevant distributions are
         included, even if they were already activated in this working set.
         """
-
         needed = self.resolve(parse_requirements(requirements))
 
         for dist in needed:
 
         return needed
 
-
     def subscribe(self, callback):
         """Invoke `callback` for all distributions (including existing ones)"""
         if callback in self.callbacks:
         for dist in self:
             callback(dist)
 
-
     def _added_new(self, dist):
         for callback in self.callbacks:
             callback(dist)
 
-
-
-
-
-
-
-
-
+    def __getstate__(self):
+        return (
+            self.entries[:], self.entry_keys.copy(), self.by_key.copy(),
+            self.callbacks[:]
+        )
+
+    def __setstate__(self, (entries, keys, by_key, callbacks)):
+        self.entries = entries[:]
+        self.entry_keys = keys.copy()
+        self.by_key = by_key.copy()
+        self.callbacks = callbacks[:]
 
 
 class Environment(object):
 
 
 
-_distribution_finders = {}
+_declare_state('dict', _distribution_finders = {})
 
 def register_finder(importer_type, distribution_finder):
     """Register `distribution_finder` to find distributions in sys.path items
     """Yield distributions accessible on a sys.path directory"""
     path_item = _normalize_cached(path_item)
 
-    if os.path.isdir(path_item):
+    if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
         if path_item.lower().endswith('.egg'):
             # unpacked egg
             yield Distribution.from_filename(
                         break
 register_finder(ImpWrapper,find_on_path)
 
-_namespace_handlers = {}
-_namespace_packages = {}
+_declare_state('dict', _namespace_handlers = {})
+_declare_state('dict', _namespace_packages = {})
 
 def register_namespace_handler(importer_type, namespace_handler):
     """Register `namespace_handler` to declare namespace packages
         return None
     module = sys.modules.get(packageName)
     if module is None:
-        module = sys.modules[packageName] = new.module(packageName)
+        module = sys.modules[packageName] = imp.new_module(packageName)
         module.__path__ = []; _set_parent_ns(packageName)
     elif not hasattr(module,'__path__'):
         raise TypeError("Not a package:", packageName)
         if not loc:
             return
 
-        if path is sys.path:
-            self.check_version_conflict()
-
         nloc = _normalize_cached(loc)
         bdir = os.path.dirname(nloc)
-        npath= map(_normalize_cached, path)
+        npath= [(p and _normalize_cached(p) or p) for p in path]
 
         bp = None
         for p, item in enumerate(npath):
                 break
             elif item==bdir and self.precedence==EGG_DIST:
                 # if it's an .egg, give it precedence over its directory
+                if path is sys.path:
+                    self.check_version_conflict()
                 path.insert(p, loc)
                 npath.insert(p, nloc)
                 break
         else:
+            if path is sys.path:
+                self.check_version_conflict()
             path.append(loc)
             return
 
         return
 
 
-
     def check_version_conflict(self):
         if self.key=='setuptools':
             return      # ignore the inevitable setuptools self-conflicts  :(
                 continue
 
             fn = getattr(sys.modules[modname], '__file__', None)
-            if fn and normalize_path(fn).startswith(loc):
+            if fn and (normalize_path(fn).startswith(loc) or fn.startswith(loc)):
                 continue
             issue_warning(
                 "Module %s was already imported from %s, but %s is being added"
 
     def __contains__(self,item):
         if isinstance(item,Distribution):
-            if item.key <> self.key: return False
+            if item.key != self.key: return False
             if self.index: item = item.parsed_version  # only get if we need it
         elif isinstance(item,basestring):
             item = parse_version(item)
         os.open = old_open  # and then put it back
 
 
-# Set up global resource manager
+# Set up global resource manager (deliberately not state-saved)
 _manager = ResourceManager()
 def _initialize(g):
     for name in dir(_manager):
 _initialize(globals())
 
 # Prepare the master working set and make the ``require()`` API available
-working_set = WorkingSet()
+_declare_state('object', working_set = WorkingSet())
 try:
     # Does the main program list any requirements?
     from __main__ import __requires__

pkg_resources.txt

     the global ``working_set`` to reflect the change.  This method is also
     called by the ``WorkingSet()`` constructor during initialization.
 
-    This method uses ``find_distributions(entry,False)`` to find distributions
+    This method uses ``find_distributions(entry, True)`` to find distributions
     corresponding to the path entry, and then ``add()`` them.  `entry` is
     always appended to the ``entries`` attribute, even if it is already
     present, however. (This is because ``sys.path`` can contain the same value
     for obtaining an "importer" object.  It first checks for an importer for
     the path item in ``sys.path_importer_cache``, and if not found it calls
     each of the ``sys.path_hooks`` and caches the result if a good importer is
-    found.  If no importer is found, this routine returns an ``ImpWrapper``
-    instance that wraps the builtin import machinery as a PEP 302-compliant
-    "importer" object.  This ``ImpWrapper`` is *not* cached; instead a new
-    instance is returned each time.
-
-    (Note: When run under Python 2.5, this function is simply an alias for
-    ``pkgutil.get_importer()``, and instead of ``pkg_resources.ImpWrapper``
-    instances, it may return ``pkgutil.ImpImporter`` instances.)
+    found.  If no importer is found, this routine returns a wrapper object
+    that wraps the builtin import machinery as a PEP 302-compliant "importer"
+    object.  This wrapper object is *not* cached; instead a new instance is
+    returned each time.
 
 
 File/Path Utilities
 Release Notes/Change History
 ----------------------------
 
-0.6final
+0.6c10
+ * Prevent lots of spurious "already imported from another path" warnings (e.g.
+   when pkg_resources is imported late).
+
+0.6c9
  * Fix ``resource_listdir('')`` always returning an empty list for zipped eggs.
  
 0.6c7
 # If your initials aren't PJE, don't run it.  :)
 #
 
-export VERSION="0.6c9"
+export VERSION="0.6c10"
 
 python2.3 setup.py -q release source --target-version=2.3 upload && \
 python2.4 setup.py -q release binary --target-version=2.4 upload && \
 execfile(convert_path('setuptools/command/__init__.py'), d)
 
 SETUP_COMMANDS = d['__all__']
-VERSION = "0.6c9"
+VERSION = "0.6c10"
 
 from setuptools import setup, find_packages
 import sys
             "include_package_data = setuptools.dist:assert_bool",
             "dependency_links     = setuptools.dist:assert_string_list",
             "test_loader          = setuptools.dist:check_importable",
+            "packages             = setuptools.dist:check_packages",
         ],
-
         "egg_info.writers": [
             "PKG-INFO = setuptools.command.egg_info:write_pkg_info",
             "requires.txt = setuptools.command.egg_info:write_requirements",

setuptools.egg-info/entry_points.txt

 eager_resources = setuptools.dist:assert_string_list
 zip_safe = setuptools.dist:assert_bool
 test_loader = setuptools.dist:check_importable
+packages = setuptools.dist:check_packages
 tests_require = setuptools.dist:check_requirements
 
 [setuptools.installation]
   inform the user of the missing program(s).
 
 
+A Note Regarding Dependencies
+-----------------------------
+
+If the project *containing* your distutils/setuptools extension(s) depends on
+any projects other than setuptools, you *must* also declare those dependencies
+as part of your project's  ``setup_requires`` keyword, so that they will
+already be built (and at least temprorarily installed) before your extension
+project is built.
+
+So, if for example you create a project Foo that includes a new file finder
+plugin, and Foo depends on Bar, then you *must* list Bar in both the
+``install_requires`` **and** ``setup_requires`` arguments to ``setup()``.
+
+If you don't do this, then in certain edge cases you may cause setuptools to
+try to go into infinite recursion, trying to build your dependencies to resolve
+your dependencies, while still building your dependencies.  (It probably won't
+happen on your development machine, but it *will* happen in a full build
+pulling everything from revision control on a clean machine, and then you or
+your users will be scratching their heads trying to figure it out!)
+
+
 Subclassing ``Command``
 -----------------------
 
 Release Notes/Change History
 ----------------------------
 
-0.6final
+0.6c10
+ * Fix for the Python 2.6.3 build_ext API change
+
+ * Ensure C libraries (as opposed to extensions) are also built when doing
+   bdist_egg
+
+ * Support for SVN 1.6
+
+0.6c9
  * Fixed a missing files problem when using Windows source distributions on
    non-Windows platforms, due to distutils not handling manifest file line
    endings correctly.

setuptools/__init__.py

 from distutils.util import convert_path
 import os.path
 
-__version__ = '0.6c9'
+__version__ = '0.6c10'
 __all__ = [
     'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
     'find_packages'

setuptools/command/alias.py

     """Quote an argument for later parsing by shlex.split()"""
     for c in '"', "'", "\\", "#":
         if c in arg: return repr(arg)
-    if arg.split()<>[arg]:
+    if arg.split()!=[arg]:
         return repr(arg)
     return arg        
 
 
     def finalize_options(self):
         option_base.finalize_options(self)
-        if self.remove and len(self.args)<>1:
+        if self.remove and len(self.args)!=1:
             raise DistutilsOptionError(
                 "Must specify exactly one argument (the alias name) when "
                 "using --remove"

setuptools/command/bdist_egg.py

     def run(self):
         # Generate metadata first
         self.run_command("egg_info")
-
         # We run install_lib before install_data, because some data hacks
         # pull their data path from the install_lib command.
         log.info("installing library code to %s" % self.bdist_dir)
         instcmd = self.get_finalized_command('install')
         old_root = instcmd.root; instcmd.root = None
+        if self.distribution.has_c_libraries() and not self.skip_build:
+            self.run_command('build_clib')
         cmd = self.call_command('install_lib', warn_dir=0)
         instcmd.root = old_root
 
         to_compile.extend(self.make_init_files())
         if to_compile:
             cmd.byte_compile(to_compile)
-
         if self.distribution.data_files:
             self.do_install_data()
 
     for flag,fn in safety_flags.items():
         fn = os.path.join(egg_dir, fn)
         if os.path.exists(fn):
-            if safe is None or bool(safe)<>flag:
+            if safe is None or bool(safe)!=flag:
                 os.unlink(fn)
         elif safe is not None and bool(safe)==flag:
             f=open(fn,'wb'); f.write('\n'); f.close()

setuptools/command/build_ext.py

 
     def get_ext_filename(self, fullname):
         filename = _build_ext.get_ext_filename(self,fullname)
-        ext = self.ext_map[fullname]
-        if isinstance(ext,Library):
-            fn, ext = os.path.splitext(filename)
-            return self.shlib_compiler.library_filename(fn,libtype)
-        elif use_stubs and ext._links_to_dynamic:
-            d,fn = os.path.split(filename)
-            return os.path.join(d,'dl-'+fn)
-        else:
-            return filename
+        if fullname in self.ext_map:
+            ext = self.ext_map[fullname]
+            if isinstance(ext,Library):
+                fn, ext = os.path.splitext(filename)
+                return self.shlib_compiler.library_filename(fn,libtype)
+            elif use_stubs and ext._links_to_dynamic:
+                d,fn = os.path.split(filename)
+                return os.path.join(d,'dl-'+fn)
+        return filename
 
     def initialize_options(self):
         _build_ext.initialize_options(self)

setuptools/command/easy_install.py

         self.outputs = []
 
     def run(self):
-        if self.verbose<>self.distribution.verbose:
+        if self.verbose!=self.distribution.verbose:
             log.set_verbosity(self.verbose)
         try:
             for spec in self.args:
         # Is it a configured, PYTHONPATH, implicit, or explicit site dir?
         is_site_dir = instdir in self.all_site_dirs
 
-        if not is_site_dir:
+        if not is_site_dir and not self.multi_version:
             # No?  Then directly test whether it does .pth file processing
             is_site_dir = self.check_pth_processing()
         else:
 
             self.check_editable(spec)
             dist = self.package_index.fetch_distribution(
-                spec, tmpdir, self.upgrade, self.editable, not self.always_copy
+                spec, tmpdir, self.upgrade, self.editable, not self.always_copy,
+                self.local_index
             )
-
             if dist is None:
                 msg = "Could not find suitable distribution for %r" % spec
                 if self.always_copy:
             f = open(pkg_inf,'w')
             f.write('Metadata-Version: 1.0\n')
             for k,v in cfg.items('metadata'):
-                if k<>'target_version':
+                if k!='target_version':
                     f.write('%s: %s\n' % (k.replace('_','-').title(), v))
             f.close()
         script_dir = os.path.join(egg_info,'scripts')
         def pf(src,dst):
             if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
                 to_compile.append(dst)
-                to_chmod.append(dst)
             elif dst.endswith('.dll') or dst.endswith('.so'):
                 to_chmod.append(dst)
             self.unpack_progress(src,dst)
 
 
 
+
     def no_default_version_msg(self):
         return """bad install directory or PYTHONPATH
 
                 if parts[1].endswith('.egg-info'):
                     prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/'))
                     break
-            if len(parts)<>2 or not name.endswith('.pth'):
+            if len(parts)!=2 or not name.endswith('.pth'):
                 continue
             if name.endswith('-nspkg.pth'):
                 continue

setuptools/command/egg_info.py

             data = f.read()
             f.close()
 
-            if data.startswith('9') or data.startswith('8'):
+            if data.startswith('<?xml'):
+                dirurl = urlre.search(data).group(1)    # get repository URL
+                localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
+            else:
+                try: svnver = int(data.splitlines()[0])
+                except: svnver=-1
+                if data<8:
+                    log.warn("unrecognized .svn/entries format; skipping %s", base)
+                    dirs[:] = []
+                    continue
+                   
                 data = map(str.splitlines,data.split('\n\x0c\n'))
                 del data[0][0]  # get rid of the '8' or '9'
                 dirurl = data[0][3]
                 localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0])
-            elif data.startswith('<?xml'):
-                dirurl = urlre.search(data).group(1)    # get repository URL
-                localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
-            else:
-                log.warn("unrecognized .svn/entries format; skipping %s", base)
-                dirs[:] = []
-                continue
             if base==os.curdir:
                 base_url = dirurl+'/'   # save the root url
             elif not dirurl.startswith(base_url):
 
 
 
-
-
-
     def find_sources(self):
         """Generate SOURCES.txt manifest file"""
         manifest_filename = os.path.join(self.egg_info,"SOURCES.txt")

setuptools/command/sdist.py

 from distutils.command.sdist import sdist as _sdist
 from distutils.util import convert_path
 from distutils import log
+from glob import glob
 import os, re, sys, pkg_resources
 
 entities = [
 
 
 
-
 def walk_revctrl(dirname=''):
     """Find all files under revision control"""
     for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
     f = open(filename,'rU')
     data = f.read()
     f.close()
-    if data.startswith('9') or data.startswith('8'):    # subversion 1.5/1.4
+    if data.startswith('<?xml'):
+        for match in entries_pattern.finditer(data):
+            yield joinpath(dirname,unescape(match.group(1)))
+    else:
+        svnver=-1
+        try: svnver = int(data.splitlines()[0])
+        except: pass
+        if svnver<8:
+            log.warn("unrecognized .svn/entries format in %s", dirname)
+            return           
         for record in map(str.splitlines, data.split('\n\x0c\n')[1:]):
             if not record or len(record)>=6 and record[5]=="delete":
                 continue    # skip deleted
             yield joinpath(dirname, record[0])
-    elif data.startswith('<?xml'):
-        for match in entries_pattern.finditer(data):
-            yield joinpath(dirname,unescape(match.group(1)))
-    else:
-        log.warn("unrecognized .svn/entries format in %s", dirname)
-
+        
 
 finders = [
     (convert_path('CVS/Entries'),
 
 
 
-
-
-
-
 class sdist(_sdist):
     """Smart sdist that finds anything supported by revision control"""
 
             sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
             raise
 
+    # Cribbed from old distutils code, to work around new distutils code
+    # that tries to do some of the same stuff as we do, in a way that makes
+    # us loop.
+    
+    def add_defaults (self):
+        standards = [('README', 'README.txt'), self.distribution.script_name]
+
+        for fn in standards:
+            if type(fn) is tuple:
+                alts = fn
+                got_it = 0
+                for fn in alts:
+                    if os.path.exists(fn):
+                        got_it = 1
+                        self.filelist.append(fn)
+                        break
+
+                if not got_it:
+                    self.warn("standard file not found: should have one of " +
+                              string.join(alts, ', '))
+            else:
+                if os.path.exists(fn):
+                    self.filelist.append(fn)
+                else:
+                    self.warn("standard file '%s' not found" % fn)
+
+        optional = ['test/test*.py', 'setup.cfg']
+        
+        for pattern in optional:
+            files = filter(os.path.isfile, glob(pattern))
+            if files:
+                self.filelist.extend(files)
+
+        if self.distribution.has_pure_modules():
+            build_py = self.get_finalized_command('build_py')
+            self.filelist.extend(build_py.get_source_files())
+
+        if self.distribution.has_ext_modules():
+            build_ext = self.get_finalized_command('build_ext')
+            self.filelist.extend(build_ext.get_source_files())
+
+        if self.distribution.has_c_libraries():
+            build_clib = self.get_finalized_command('build_clib')
+            self.filelist.extend(build_clib.get_source_files())
+
+        if self.distribution.has_scripts():
+            build_scripts = self.get_finalized_command('build_scripts')
+            self.filelist.extend(build_scripts.get_source_files())
+
+
     def check_readme(self):
         alts = ("README", "README.txt")
         for f in alts:
 
 
 
-
-
-
-
-
-
-
-
-
 #

setuptools/depends.py

     def version_ok(self,version):
         """Is 'version' sufficiently up-to-date?"""
         return self.attribute is None or self.format is None or \
-            str(version)<>"unknown" and version >= self.requested_version
+            str(version)!="unknown" and version >= self.requested_version
 
 
     def get_version(self, paths=None, default="unknown"):

setuptools/dist.py

 from distutils.errors import DistutilsOptionError, DistutilsPlatformError
 from distutils.errors import DistutilsSetupError
 import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd
-import os, distutils.log
+import os, distutils.log, re
 
 def _get_unpatched(cls):
     """Protect against re-patching the distutils if reloaded
             parent = '.'.join(nsp.split('.')[:-1])
             if parent not in value:
                 distutils.log.warn(
-                    "%r is declared as a package namespace, but %r is not:"
-                    " please correct this in setup.py", nsp, parent
+                    "WARNING: %r is declared as a package namespace, but %r"
+                    " is not: please correct this in setup.py", nsp, parent
                 )
 
 def check_extras(dist, attr, value):
         "wildcard patterns"
     )
 
+def check_packages(dist, attr, value):
+    for pkgname in value:
+        if not re.match(r'\w+(\.\w+)*', pkgname):
+            distutils.log.warn(
+                "WARNING: %r not a valid package name; please use only"
+                ".-separated package names in setup.py", pkgname
+            )
+            
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 class Distribution(_Distribution):
     """Distribution with support for features, tests, and package data
 
         if self.packages:
             self.packages = [
                 p for p in self.packages
-                    if p<>package and not p.startswith(pfx)
+                    if p!=package and not p.startswith(pfx)
             ]
 
         if self.py_modules:
             self.py_modules = [
                 p for p in self.py_modules
-                    if p<>package and not p.startswith(pfx)
+                    if p!=package and not p.startswith(pfx)
             ]
 
         if self.ext_modules:
             self.ext_modules = [
                 p for p in self.ext_modules
-                    if p.name<>package and not p.name.startswith(pfx)
+                    if p.name!=package and not p.name.startswith(pfx)
             ]
 
 

setuptools/package_index.py

 """PyPI and direct package downloading"""
 import sys, os.path, re, urlparse, urllib2, shutil, random, socket, cStringIO
+import httplib
 from pkg_resources import *
 from distutils import log
 from distutils.errors import DistutilsError
 except ImportError:
     from md5 import md5
 from fnmatch import translate
-
 EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
 HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
 # this is here to fix emacs' cruddy broken syntax highlighting
 def egg_info_for_url(url):
     scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
     base = urllib2.unquote(path.split('/')[-1])
+    if server=='sourceforge.net' and base=='download':    # XXX Yuck
+        base = urllib2.unquote(path.split('/')[-2])
     if '#' in base: base, fragment = base.split('#',1)
     return base,fragment
 
     if basename.endswith('.egg') and '-' in basename:
         # only one, unambiguous interpretation
         return [Distribution.from_location(location, basename, metadata)]
-
     if basename.endswith('.exe'):
         win_base, py_ver = parse_bdist_wininst(basename)
         if win_base is not None:
             return interpret_distro_name(
                 location, win_base, metadata, py_ver, BINARY_DIST, "win32"
             )
-
     # Try source distro extensions (.zip, .tgz, etc.)
     #
     for ext in EXTENSIONS:
             return
 
         self.info("Reading %s", url)
+        self.fetched_urls[url] = True   # prevent multiple fetch attempts
         f = self.open_url(url, "Download error: %s -- Some packages may not be found!")
         if f is None: return
-        self.fetched_urls[url] = self.fetched_urls[f.url] = True
-
+        self.fetched_urls[f.url] = True
         if 'html' not in f.headers.get('content-type', '').lower():
             f.close()   # not html, we can't process it
             return
     def check_md5(self, cs, info, filename, tfp):
         if re.match('md5=[0-9a-f]{32}$', info):
             self.debug("Validating md5 checksum for %s", filename)
-            if cs.hexdigest()<>info[4:]:
+            if cs.hexdigest()!=info[4:]:
                 tfp.close()
                 os.unlink(filename)
                 raise DistutilsError(
 
 
     def fetch_distribution(self,
-        requirement, tmpdir, force_scan=False, source=False, develop_ok=False
+        requirement, tmpdir, force_scan=False, source=False, develop_ok=False,
+        local_index=None, 
     ):
         """Obtain a distribution suitable for fulfilling `requirement`
 
         set, development and system eggs (i.e., those using the ``.egg-info``
         format) will be ignored.
         """
-
         # process a Requirement
         self.info("Searching for %s", requirement)
         skipped = {}
+        dist = None
 
-        def find(req):
+        def find(env, req):
             # Find a matching distribution; may be called more than once
 
-            for dist in self[req.key]:
+            for dist in env[req.key]:
 
                 if dist.precedence==DEVELOP_DIST and not develop_ok:
                     if dist not in skipped:
                     continue
 
                 if dist in req and (dist.precedence<=SOURCE_DIST or not source):
-                    self.info("Best match: %s", dist)
-                    return dist.clone(
-                        location=self.download(dist.location, tmpdir)
-                    )
+                    return dist
+
+
 
         if force_scan:
             self.prescan()
             self.find_packages(requirement)
+            dist = find(self, requirement)
+            
+        if local_index is not None:
+            dist = dist or find(local_index, requirement)
 
-        dist = find(requirement)
         if dist is None and self.to_scan is not None:
             self.prescan()
-            dist = find(requirement)
+            dist = find(self, requirement)
 
         if dist is None and not force_scan:
             self.find_packages(requirement)
-            dist = find(requirement)
+            dist = find(self, requirement)
 
         if dist is None:
             self.warn(
                 (source and "a source distribution of " or ""),
                 requirement,
             )
-        return dist
+        self.info("Best match: %s", dist)
+        return dist.clone(location=self.download(dist.location, tmpdir))
+
 
     def fetch(self, requirement, tmpdir, force_scan=False, source=False):
         """Obtain a file suitable for fulfilling `requirement`
 
 
 
-
-
-
-
-
     def gen_setup(self, filename, fragment, tmpdir):
         match = EGG_FRAGMENT.match(fragment)
         dists = match and [d for d in
 
 
     def open_url(self, url, warning=None):
-        if url.startswith('file:'):
-            return local_open(url)
+        if url.startswith('file:'): return local_open(url)
         try:
             return open_with_auth(url)
         except urllib2.HTTPError, v:
             return v
         except urllib2.URLError, v:
-            if warning: self.warn(warning, v.reason)
-            else:
-                raise DistutilsError("Download error for %s: %s"
-                                     % (url, v.reason))
+            reason = v.reason
+        except httplib.HTTPException, v: 
+            reason = "%s: %s" % (v.__doc__ or v.__class__.__name__, v)
+        if warning:
+            self.warn(warning, reason)
+        else:
+            raise DistutilsError("Download error for %s: %s" % (url, reason))
 
     def _download_url(self, scheme, url, tmpdir):
         # Determine download filename
             self.url_ok(url, True)   # raises error if not allowed
             return self._attempt_download(url, filename)
 
-
-
     def scan_url(self, url):
         self.process_url(url, True)
 

setuptools/sandbox.py

-import os, sys, __builtin__, tempfile, operator
+import os, sys, __builtin__, tempfile, operator, pkg_resources
 _os = sys.modules[os.name]
 _open = open
+_file = file
+
 from distutils.errors import DistutilsError
+from pkg_resources import working_set
+
 __all__ = [
     "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
 ]
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 def run_setup(setup_script, args):
     """Run a distutils setup script, sandboxed in its directory"""
-
     old_dir = os.getcwd()
     save_argv = sys.argv[:]
     save_path = sys.path[:]
     temp_dir = os.path.join(setup_dir,'temp')
     if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
     save_tmp = tempfile.tempdir
-
+    save_modules = sys.modules.copy()
+    pr_state = pkg_resources.__getstate__()
     try:
-        tempfile.tempdir = temp_dir
-        os.chdir(setup_dir)
+        tempfile.tempdir = temp_dir; os.chdir(setup_dir)
         try:
             sys.argv[:] = [setup_script]+list(args)
             sys.path.insert(0, setup_dir)
+            # reset to include setup dir, w/clean callback list
+            working_set.__init__()  
+            working_set.callbacks.append(lambda dist:dist.activate())
             DirectorySandbox(setup_dir).run(
                 lambda: execfile(
                     "setup.py",
                 raise
             # Normal exit, just return
     finally:
+        pkg_resources.__setstate__(pr_state)
+        sys.modules.update(save_modules)
+        for key in list(sys.modules):
+            if key not in save_modules: del sys.modules[key]
         os.chdir(old_dir)
         sys.path[:] = save_path
         sys.argv[:] = save_argv
         tempfile.tempdir = save_tmp
 
+
+
 class AbstractSandbox:
     """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
 
         """Run 'func' under os sandboxing"""
         try:
             self._copy(self)
-            __builtin__.open = __builtin__.file = self._open
+            __builtin__.file = self._file
+            __builtin__.open = self._open
             self._active = True
             return func()
         finally:
             self._active = False
-            __builtin__.open = __builtin__.file = _open
+            __builtin__.open = _file
+            __builtin__.file = _open
             self._copy(_os)
 
-
     def _mk_dual_path_wrapper(name):
         original = getattr(_os,name)
         def wrap(self,src,dst,*args,**kw):
             return original(src,dst,*args,**kw)
         return wrap
 
-
     for name in ["rename", "link", "symlink"]:
         if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
 
             return original(path,*args,**kw)
         return wrap
 
-    _open = _mk_single_path_wrapper('file', _open)
+    _open = _mk_single_path_wrapper('open', _open)
+    _file = _mk_single_path_wrapper('file', _file)
     for name in [
         "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
         "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
     ]:
         if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
 
-
     def _mk_single_with_return(name):
         original = getattr(_os,name)
         def wrap(self,path,*args,**kw):
             self._violation(operation, src, dst, *args, **kw)
         return (src,dst)
 
+    def _file(self, path, mode='r', *args, **kw):
+        if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
+            self._violation("file", path, mode, *args, **kw)
+        return _file(path,mode,*args,**kw)
+
     def open(self, file, flags, mode=0777):
         """Called for low-level os.open()"""
         if flags & WRITE_FLAGS and not self._ok(file):
             self._violation("os.open", file, flags, mode)
         return _os.open(file,flags,mode)
 
-
 WRITE_FLAGS = reduce(
-    operator.or_,
-    [getattr(_os, a, 0) for a in
+    operator.or_, [getattr(_os, a, 0) for a in
         "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
 )
 
-
-
-
 class SandboxViolation(DistutilsError):
     """A setup script attempted to modify the filesystem outside the sandbox"""
 
 [setuptools]
 status = 'release candidate'
 major = 0
-build = 9
+build = 10
 minor = 6