diff options
-rwxr-xr-x | EasyInstall.txt | 18 | ||||
-rwxr-xr-x | ez_setup.py | 2 | ||||
-rw-r--r-- | pkg_resources.py | 109 | ||||
-rwxr-xr-x | pkg_resources.txt | 20 | ||||
-rwxr-xr-x | release.sh | 2 | ||||
-rwxr-xr-x | setup.py | 4 | ||||
-rwxr-xr-x | setuptools.egg-info/entry_points.txt | 1 | ||||
-rwxr-xr-x | setuptools.txt | 31 | ||||
-rw-r--r-- | setuptools/__init__.py | 2 | ||||
-rwxr-xr-x | setuptools/command/alias.py | 4 | ||||
-rw-r--r-- | setuptools/command/bdist_egg.py | 6 | ||||
-rw-r--r-- | setuptools/command/build_ext.py | 18 | ||||
-rwxr-xr-x | setuptools/command/easy_install.py | 14 | ||||
-rwxr-xr-x | setuptools/command/egg_info.py | 22 | ||||
-rwxr-xr-x | setuptools/command/sdist.py | 83 | ||||
-rw-r--r-- | setuptools/depends.py | 2 | ||||
-rw-r--r-- | setuptools/dist.py | 53 | ||||
-rwxr-xr-x | setuptools/package_index.py | 62 | ||||
-rwxr-xr-x | setuptools/sandbox.py | 75 | ||||
-rwxr-xr-x | version.dat | 2 |
20 files changed, 368 insertions, 162 deletions
diff --git a/EasyInstall.txt b/EasyInstall.txt index b0341e88..20e2053a 100755 --- a/EasyInstall.txt +++ b/EasyInstall.txt @@ -256,7 +256,7 @@ or directory (found in the installation directory). 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 @@ -427,7 +427,7 @@ below, and also the section on the `Package Index "API"`_. 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:: @@ -1217,7 +1217,19 @@ displayed MD5 info (broken onto two lines for readability):: 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``.) diff --git a/ez_setup.py b/ez_setup.py index d24e845e..f4de7e4b 100755 --- a/ez_setup.py +++ b/ez_setup.py @@ -14,7 +14,7 @@ the appropriate options to ``use_setuptools()``. 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 = { diff --git a/pkg_resources.py b/pkg_resources.py index 9edb6c0b..baff4225 100644 --- a/pkg_resources.py +++ b/pkg_resources.py @@ -13,7 +13,7 @@ The package resource API is designed to work with normal filesystem packages, method. """ -import sys, os, zipimport, time, re, imp, new +import sys, os, zipimport, time, re, imp try: frozenset @@ -39,6 +39,47 @@ from os import open as os_open +_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. @@ -164,14 +205,8 @@ def get_provider(moduleOrReq): 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): @@ -203,6 +238,12 @@ get_platform = get_build_platform # XXX backward compat + + + + + + def compatible_platforms(provided,required): """Can code for the `provided` platform run on the `required` platform? @@ -387,7 +428,7 @@ class WorkingSet(object): 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 @@ -622,7 +663,6 @@ class WorkingSet(object): 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: @@ -630,7 +670,6 @@ class WorkingSet(object): return needed - def subscribe(self, callback): """Invoke `callback` for all distributions (including existing ones)""" if callback in self.callbacks: @@ -639,19 +678,21 @@ class WorkingSet(object): 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): @@ -1597,7 +1638,7 @@ else: -_distribution_finders = {} +_declare_state('dict', _distribution_finders = {}) def register_finder(importer_type, distribution_finder): """Register `distribution_finder` to find distributions in sys.path items @@ -1646,7 +1687,7 @@ def find_on_path(importer, path_item, only=False): """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( @@ -1679,8 +1720,8 @@ def find_on_path(importer, path_item, only=False): 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 @@ -1709,7 +1750,7 @@ def _handle_ns(packageName, path_item): 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) @@ -2220,12 +2261,9 @@ class Distribution(object): 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): @@ -2233,10 +2271,14 @@ class Distribution(object): 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 @@ -2253,7 +2295,6 @@ class Distribution(object): return - def check_version_conflict(self): if self.key=='setuptools': return # ignore the inevitable setuptools self-conflicts :( @@ -2267,7 +2308,7 @@ class Distribution(object): 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" @@ -2444,7 +2485,7 @@ class Requirement: 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) @@ -2541,7 +2582,7 @@ def _mkstemp(*args,**kw): 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): @@ -2550,7 +2591,7 @@ def _initialize(g): _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__ diff --git a/pkg_resources.txt b/pkg_resources.txt index 7d8afd37..03793b62 100755 --- a/pkg_resources.txt +++ b/pkg_resources.txt @@ -269,7 +269,7 @@ instance: 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 @@ -1661,14 +1661,10 @@ PEP 302 Utilities 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 @@ -1692,7 +1688,11 @@ 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 @@ -7,7 +7,7 @@ # 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 && \ @@ -7,7 +7,7 @@ d = {} 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 @@ -53,8 +53,8 @@ setup( "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", diff --git a/setuptools.egg-info/entry_points.txt b/setuptools.egg-info/entry_points.txt index f7367e0d..10f1da6c 100755 --- a/setuptools.egg-info/entry_points.txt +++ b/setuptools.egg-info/entry_points.txt @@ -49,6 +49,7 @@ test_suite = setuptools.dist:check_test_suite 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] diff --git a/setuptools.txt b/setuptools.txt index d1b25302..93940528 100755 --- a/setuptools.txt +++ b/setuptools.txt @@ -2569,6 +2569,27 @@ A few important points for writing revision control file finders: 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`` ----------------------- @@ -2611,7 +2632,15 @@ XXX 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. diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 56cbf767..a9f6544d 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -7,7 +7,7 @@ from distutils.core import Command as _Command from distutils.util import convert_path import os.path -__version__ = '0.6c9' +__version__ = '0.6c10' __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', 'find_packages' diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index f5368b29..40c00b55 100755 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -9,7 +9,7 @@ def shquote(arg): """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 @@ -33,7 +33,7 @@ class alias(option_base): 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" diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 9e852a3f..7e5a3799 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -165,12 +165,13 @@ class bdist_egg(Command): 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 @@ -190,7 +191,6 @@ class bdist_egg(Command): to_compile.extend(self.make_init_files()) if to_compile: cmd.byte_compile(to_compile) - if self.distribution.data_files: self.do_install_data() @@ -398,7 +398,7 @@ def write_safety_flag(egg_dir, safe): 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() diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index c0aaa8e8..f6f3355d 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -82,15 +82,15 @@ class build_ext(_build_ext): 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) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index f06b6ddd..a5a23ad4 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -204,7 +204,7 @@ class easy_install(Command): 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: @@ -252,7 +252,7 @@ class easy_install(Command): # 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: @@ -430,9 +430,9 @@ Please make the appropriate changes for your system and try again. 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: @@ -722,7 +722,7 @@ Please make the appropriate changes for your system and try again. 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') @@ -988,7 +988,6 @@ See the setuptools documentation for the "develop" command for more info. 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) @@ -1023,6 +1022,7 @@ See the setuptools documentation for the "develop" command for more info. + def no_default_version_msg(self): return """bad install directory or PYTHONPATH @@ -1286,7 +1286,7 @@ def get_exe_prefixes(exe_filename): 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 diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 9741e26a..5a8b2db8 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -217,18 +217,21 @@ class egg_info(Command): 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): @@ -241,9 +244,6 @@ class egg_info(Command): - - - def find_sources(self): """Generate SOURCES.txt manifest file""" manifest_filename = os.path.join(self.egg_info,"SOURCES.txt") diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 5bd5ebd4..d5121de5 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -1,6 +1,7 @@ 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 = [ @@ -38,7 +39,6 @@ def joinpath(prefix,suffix): - def walk_revctrl(dirname=''): """Find all files under revision control""" for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): @@ -86,17 +86,21 @@ def entries_finder(dirname, filename): 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'), @@ -117,10 +121,6 @@ finders = [ - - - - class sdist(_sdist): """Smart sdist that finds anything supported by revision control""" @@ -162,6 +162,56 @@ class sdist(_sdist): 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: @@ -193,13 +243,4 @@ class sdist(_sdist): - - - - - - - - - # diff --git a/setuptools/depends.py b/setuptools/depends.py index 4b7b3437..5fdf2d7e 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -36,7 +36,7 @@ class Require: 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"): diff --git a/setuptools/dist.py b/setuptools/dist.py index 30ff35e3..c1218ef2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,7 +8,7 @@ from setuptools.command.install_lib import install_lib 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 @@ -61,8 +61,8 @@ def check_nsp(dist, attr, value): 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): @@ -121,6 +121,47 @@ def check_package_data(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 @@ -415,19 +456,19 @@ class Distribution(_Distribution): 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) ] diff --git a/setuptools/package_index.py b/setuptools/package_index.py index da3fed12..70b75a6f 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,5 +1,6 @@ """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 @@ -8,7 +9,6 @@ try: 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 @@ -42,6 +42,8 @@ def parse_bdist_wininst(name): 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 @@ -64,14 +66,12 @@ def distros_for_location(location, basename, metadata=None): 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: @@ -186,10 +186,10 @@ class PackageIndex(Environment): 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 @@ -329,7 +329,7 @@ class PackageIndex(Environment): 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( @@ -409,7 +409,8 @@ class PackageIndex(Environment): 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` @@ -427,15 +428,15 @@ class PackageIndex(Environment): 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: @@ -444,23 +445,25 @@ class PackageIndex(Environment): 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( @@ -468,7 +471,9 @@ class PackageIndex(Environment): (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` @@ -485,11 +490,6 @@ class PackageIndex(Environment): - - - - - def gen_setup(self, filename, fragment, tmpdir): match = EGG_FRAGMENT.match(fragment) dists = match and [d for d in @@ -573,17 +573,19 @@ class PackageIndex(Environment): 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 @@ -611,8 +613,6 @@ class PackageIndex(Environment): 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) diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 4db0dbdb..00eb0124 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -1,14 +1,46 @@ -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[:] @@ -16,13 +48,16 @@ def run_setup(setup_script, args): 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", @@ -34,11 +69,17 @@ def run_setup(setup_script, args): 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""" @@ -58,15 +99,16 @@ class AbstractSandbox: """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): @@ -75,7 +117,6 @@ class AbstractSandbox: 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) @@ -88,7 +129,8 @@ class AbstractSandbox: 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", @@ -96,7 +138,6 @@ class AbstractSandbox: ]: 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): @@ -187,22 +228,22 @@ class DirectorySandbox(AbstractSandbox): 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""" diff --git a/version.dat b/version.dat index 32144c8e..16ecd2aa 100755 --- a/version.dat +++ b/version.dat @@ -1,6 +1,6 @@ [setuptools]
status = 'release candidate'
major = 0
-build = 9
+build = 10
minor = 6
|